packages feed

hevm 0.57.0 → 0.58.0

raw patch · 41 files changed

+11157/−7460 lines, 41 filesdep +cryptondep +randomdep −cryptonitedep ~QuickCheckdep ~timedep ~vectorPVP ok

version bump matches the API change (PVP)

Dependencies added: crypton, random

Dependencies removed: cryptonite

Dependency ranges changed: QuickCheck, time, vector

API changes (from Hackage documentation)

- EVM.Dapp: unitTestMethods :: Text -> SolcContract -> [Sig]
- EVM.Dapp: unitTestMethodsFiltered :: Text -> (Text -> Bool) -> SolcContract -> [Sig]
- EVM.Expr: Array :: StorageType
- EVM.Expr: ConstState :: Map (Expr 'EWord) W256 -> Map (Expr 'EWord) W256 -> Map (Expr 'EWord) W256 -> Bool -> ConstState
- EVM.Expr: Map :: StorageType
- EVM.Expr: Mixed :: StorageType
- EVM.Expr: SmallSlot :: StorageType
- EVM.Expr: UNK :: StorageType
- EVM.Expr: [canBeSat] :: ConstState -> Bool
- EVM.Expr: [lowerBounds] :: ConstState -> Map (Expr 'EWord) W256
- EVM.Expr: [upperBounds] :: ConstState -> Map (Expr 'EWord) W256
- EVM.Expr: [values] :: ConstState -> Map (Expr 'EWord) W256
- EVM.Expr: allLit :: [Expr 'Byte] -> Bool
- EVM.Expr: bytesToW256 :: [Word8] -> W256
- EVM.Expr: data ConstState
- EVM.Expr: data StorageType
- EVM.Expr: flattenProps :: [Prop] -> [Prop]
- EVM.Expr: isLitByte :: Expr 'Byte -> Bool
- EVM.Expr: lhsConstHelper :: forall (a :: EType). Expr a -> Maybe ()
- EVM.Expr: maxWord256 :: W256
- EVM.Expr: padBytesLeft :: Int -> [Expr 'Byte] -> [Expr 'Byte]
- EVM.Expr: preImageLookupMap :: Map W256 Word8
- EVM.Expr: readWordFromBytes :: Expr 'EWord -> Expr 'Buf -> Expr 'EWord
- EVM.Expr: remRedundantProps :: [Prop] -> [Prop]
- EVM.Expr: simplifyNoLitToKeccak :: forall (a :: EType). Expr a -> Expr a
- EVM.Expr: slice :: Expr 'EWord -> Expr 'EWord -> Expr 'Buf -> Expr 'Buf
- EVM.Expr: word256At :: Expr 'EWord -> Lens (Expr 'Buf) (Expr 'Buf) (Expr 'EWord) (Expr 'EWord)
- EVM.Solidity: WarningData :: SolcContract -> SourceCache -> VM t -> WarningData (t :: VMType)
- EVM.Solidity: [solcContr] :: WarningData (t :: VMType) -> SolcContract
- EVM.Solidity: [sourceCache] :: WarningData (t :: VMType) -> SourceCache
- EVM.Solidity: [vm] :: WarningData (t :: VMType) -> VM t
- EVM.Solidity: data WarningData (t :: VMType)
- EVM.Solvers: CacheEntry :: [Prop] -> CacheEntry
- EVM.Solvers: MultiData :: SMT2 -> MultiSol -> Chan (Maybe [W256]) -> MultiData
- EVM.Solvers: SingleData :: SMT2 -> Maybe [Prop] -> Chan SMTResult -> SingleData
- EVM.Solvers: SolverInstance :: Solver -> Handle -> Handle -> ProcessHandle -> SolverInstance
- EVM.Solvers: TaskMulti :: MultiData -> Task
- EVM.Solvers: TaskSingle :: SingleData -> Task
- EVM.Solvers: [multiSol] :: MultiData -> MultiSol
- EVM.Solvers: [process] :: SolverInstance -> ProcessHandle
- EVM.Solvers: [props] :: SingleData -> Maybe [Prop]
- EVM.Solvers: [resultChan] :: SingleData -> Chan SMTResult
- EVM.Solvers: [smt2] :: SingleData -> SMT2
- EVM.Solvers: [solvertype] :: SolverInstance -> Solver
- EVM.Solvers: [stdin] :: SolverInstance -> Handle
- EVM.Solvers: [stdout] :: SolverInstance -> Handle
- EVM.Solvers: checkCommand :: SolverInstance -> SMTEntry -> IO ()
- EVM.Solvers: data MultiData
- EVM.Solvers: data SingleData
- EVM.Solvers: data SolverInstance
- EVM.Solvers: dumpUnsolved :: SMT2 -> Int -> Maybe FilePath -> IO ()
- EVM.Solvers: getModel :: SolverInstance -> CexVars -> IO SMTCex
- EVM.Solvers: getMultiSol :: (MonadIO m, ReadConfig m) => SMT2 -> MultiSol -> Chan (Maybe [W256]) -> SolverInstance -> Chan SolverInstance -> Int -> m ()
- EVM.Solvers: getOneSol :: (MonadIO m, ReadConfig m) => SMT2 -> Maybe [Prop] -> Chan SMTResult -> TChan CacheEntry -> SolverInstance -> Chan SolverInstance -> Int -> m ()
- EVM.Solvers: getValue :: SolverInstance -> Text -> IO Text
- EVM.Solvers: mkTimeout :: Maybe Natural -> Text
- EVM.Solvers: newtype CacheEntry
- EVM.Solvers: readSExpr :: Handle -> IO [Text]
- EVM.Solvers: sendCommand :: SolverInstance -> SMTEntry -> IO Text
- EVM.Solvers: sendScript :: SolverInstance -> SMTScript -> IO (Either Text ())
- EVM.Solvers: solverArgs :: Solver -> Natural -> Maybe Natural -> [Text]
- EVM.Solvers: spawnSolver :: Solver -> Natural -> Maybe Natural -> IO SolverInstance
- EVM.Solvers: stopSolver :: SolverInstance -> IO ()
- EVM.Solvers: stripCarriageReturn :: Text -> Text
- EVM.Solvers: supersetAny :: Set Prop -> [Set Prop] -> Bool
- EVM.Solvers: writeSMT2File :: SMT2 -> FilePath -> String -> IO ()
- EVM.Types: [SHA256] :: Expr 'Buf -> Expr 'EWord
- EVM.UnitTest: [match] :: UnitTestOptions -> Text
- EVM.UnitTest: [prefix] :: UnitTestOptions -> Text
+ EVM: Indeterminate :: String -> RevertMatch
+ EVM: Match :: RevertMatch
+ EVM: Mismatch :: RevertMatch
+ EVM: assertionFailedBuf :: ByteString -> Expr 'Buf
+ EVM: consoleAddr :: Expr 'EAddr
+ EVM: data RevertMatch
+ EVM: defaultVMOpts :: forall (t :: VMType). VMOps t => VMOpts t
+ EVM: dummyCreateAddress :: Expr 'EAddr
+ EVM: dummySuccessReturn :: Expr 'Buf
+ EVM: errorMsg :: ByteString -> Expr 'Buf
+ EVM: finishFrameNoExpectation :: forall (t :: VMType). VMOps t => FrameResult -> EVM t ()
+ EVM: instance GHC.Classes.Eq EVM.RevertMatch
+ EVM: instance GHC.Show.Show EVM.RevertMatch
+ EVM: isPrecompileAddr' :: Addr -> Bool
+ EVM: matchExpectedRevert :: ExpectedRevert -> Expr 'Buf -> Expr 'EAddr -> RevertMatch
+ EVM: matchFullReason :: Expr 'Buf -> Expr 'Buf -> RevertMatch
+ EVM: matchPartialPrefix :: Expr 'Buf -> Expr 'Buf -> RevertMatch
+ EVM: replaceCodeEtch :: forall (t :: VMType). Expr 'EAddr -> ContractCode -> EVM t ()
+ EVM: setEIP2935Storage :: forall (t :: VMType). VMOpts t -> VM t -> VM t
+ EVM: setExpectedRevert :: forall (t :: VMType). VMOps t => Maybe (Expr 'Buf) -> Bool -> Maybe (Expr 'EAddr) -> EVM t ()
+ EVM: stripErrorPrefix :: ByteString -> Maybe ByteString
+ EVM: terminateVMWithError :: forall (t :: VMType). VMOps t => EvmError -> EVM t ()
+ EVM.ConsoleLog: formatConsoleLog :: Expr 'Buf -> Text
+ EVM.Dapp: TestMethodInfo :: SolcContract -> Sig -> TestMethodInfo
+ EVM.Dapp: [contract] :: TestMethodInfo -> SolcContract
+ EVM.Dapp: [methodSignature] :: TestMethodInfo -> Sig
+ EVM.Dapp: contractMethods :: SolcContract -> [Sig]
+ EVM.Dapp: contractMethodsFiltered :: TestMethodFilter -> SolcContract -> [Sig]
+ EVM.Dapp: data TestMethodInfo
+ EVM.Dapp: fallbackSrcPos :: DappInfo -> Trace -> Maybe (FilePath, Int)
+ EVM.Dapp: type TestMethodFilter = TestMethodInfo -> Bool
+ EVM.Effects: [earlyAbort] :: Config -> Bool
+ EVM.Effects: [maxDynSize] :: Config -> Int
+ EVM.Effects: [mergeMaxBudget] :: Config -> Int
+ EVM.Expr: clz :: Expr 'EWord -> Expr 'EWord
+ EVM.FeeSchedule: [g_txdatafloor] :: FeeSchedule n -> n
+ EVM.Fetch: [rpcThrottle] :: Session -> IORef (Maybe UTCTime)
+ EVM.Format: formatCaveat :: Caveat -> Text
+ EVM.Merge: tryMergeForwardJump :: Config -> EVM 'Symbolic () -> Int -> Int -> Expr 'EWord -> [Expr 'EWord] -> EVM 'Symbolic Bool
+ EVM.Op: OpClz :: GenericOp a
+ EVM.SMT: type MaybeIO = MaybeT IO
+ EVM.Solvers: defMemLimit :: Natural
+ EVM.SymExec: [shouldAbort] :: InterpTask (m :: Type -> Type) a -> TVar Bool
+ EVM.SymExec: isDy :: CalldataFragment -> Bool
+ EVM.SymExec: noopPathHandler :: Applicative m => PathHandler m (Expr 'End)
+ EVM.SymExec: type PathHandler (m :: k -> Type) (a :: k) = Expr 'End -> TVar Bool -> m a
+ EVM.Transaction: EIP4844Transaction :: TxType
+ EVM.Transaction: EIP7702Transaction :: TxType
+ EVM.Transaction: txdataFloorGas :: FeeSchedule Word64 -> Transaction -> Word64
+ EVM.Types: AssumeCheatFailed :: EvmError
+ EVM.Types: ConsoleLog :: Expr 'Buf -> TraceData
+ EVM.Types: DynArgBounded :: Int -> Caveat
+ EVM.Types: ExpectedRevert :: Maybe (Expr 'Buf) -> Int -> Bool -> Maybe (Expr 'EAddr) -> Maybe (Expr 'EAddr) -> ExpectedRevert
+ EVM.Types: MergeState :: Bool -> Int -> MergeState
+ EVM.Types: OpClz :: GenericOp a
+ EVM.Types: SrcLookup :: (Map (Expr 'EAddr) Contract -> Expr 'EAddr -> Int -> String) -> SrcLookup
+ EVM.Types: [CLZ] :: Expr 'EWord -> Expr 'EWord
+ EVM.Types: [ITE] :: Expr 'EWord -> Expr 'EWord -> Expr 'EWord -> Expr 'EWord
+ EVM.Types: [actualReverter] :: ExpectedRevert -> Maybe (Expr 'EAddr)
+ EVM.Types: [depth] :: ExpectedRevert -> Int
+ EVM.Types: [expectedRevert] :: VM (t :: VMType) -> Maybe ExpectedRevert
+ EVM.Types: [maxSize] :: Caveat -> Int
+ EVM.Types: [mergeState] :: VM (t :: VMType) -> MergeState
+ EVM.Types: [msActive] :: MergeState -> Bool
+ EVM.Types: [msRemainingBudget] :: MergeState -> Int
+ EVM.Types: [parentHash] :: VMOpts (t :: VMType) -> W256
+ EVM.Types: [partialMatch] :: ExpectedRevert -> Bool
+ EVM.Types: [reason] :: ExpectedRevert -> Maybe (Expr 'Buf)
+ EVM.Types: [reverter] :: ExpectedRevert -> Maybe (Expr 'EAddr)
+ EVM.Types: [srcLookup] :: VM (t :: VMType) -> Maybe SrcLookup
+ EVM.Types: [txdataFloorGas] :: VMOpts (t :: VMType) -> Word64
+ EVM.Types: data Caveat
+ EVM.Types: data ExpectedRevert
+ EVM.Types: data MergeState
+ EVM.Types: defaultMergeState :: MergeState
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.MergeState, b GHC.Types.~ EVM.Types.MergeState) => Optics.Label.LabelOptic "mergeState" k (EVM.Types.VM t) (EVM.Types.VM t) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.W256, b GHC.Types.~ EVM.Types.W256) => Optics.Label.LabelOptic "parentHash" k (EVM.Types.VMOpts t) (EVM.Types.VMOpts t) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Maybe.Maybe EVM.Types.ExpectedRevert, b GHC.Types.~ GHC.Maybe.Maybe EVM.Types.ExpectedRevert) => Optics.Label.LabelOptic "expectedRevert" k (EVM.Types.VM t) (EVM.Types.VM t) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Maybe.Maybe EVM.Types.SrcLookup, b GHC.Types.~ GHC.Maybe.Maybe EVM.Types.SrcLookup) => Optics.Label.LabelOptic "srcLookup" k (EVM.Types.VM t) (EVM.Types.VM t) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Word.Word64, b GHC.Types.~ GHC.Word.Word64) => Optics.Label.LabelOptic "txdataFloorGas" k (EVM.Types.VMOpts t) (EVM.Types.VMOpts t) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Word.Word64, b GHC.Types.~ GHC.Word.Word64) => Optics.Label.LabelOptic "txdataFloorGas" k EVM.Types.TxState EVM.Types.TxState a b
+ EVM.Types: instance GHC.Classes.Eq EVM.Types.Caveat
+ EVM.Types: instance GHC.Classes.Eq EVM.Types.MergeState
+ EVM.Types: instance GHC.Classes.Ord EVM.Types.Caveat
+ EVM.Types: instance GHC.Generics.Generic EVM.Types.ExpectedRevert
+ EVM.Types: instance GHC.Generics.Generic EVM.Types.MergeState
+ EVM.Types: instance GHC.Read.Read EVM.Types.FunctionSelector
+ EVM.Types: instance GHC.Show.Show EVM.Types.Caveat
+ EVM.Types: instance GHC.Show.Show EVM.Types.ExpectedRevert
+ EVM.Types: instance GHC.Show.Show EVM.Types.MergeState
+ EVM.Types: instance GHC.Show.Show EVM.Types.SrcLookup
+ EVM.Types: newtype SrcLookup
+ EVM.Types: runSrcLookup :: Maybe SrcLookup -> Map (Expr 'EAddr) Contract -> Expr 'EAddr -> Int -> String
+ EVM.UnitTest: [methodFilter] :: UnitTestOptions -> TestMethodFilter
+ EVM.UnitTest: makeSrcLookup :: DappInfo -> SourceCache -> SrcLookup
+ EVM.UnitTest: printCaveats :: [Caveat] -> IO ()
- EVM.ABI: AbiFunction :: ByteString -> AbiValue
+ EVM.ABI: AbiFunction :: Addr -> FunctionSelector -> AbiValue
- EVM.Dapp: findUnitTests :: Text -> Text -> [SolcContract] -> [(Text, [Sig])]
+ EVM.Dapp: findUnitTests :: TestMethodFilter -> SolcContract -> [Sig]
- EVM.Dapp: mkSig :: Text -> Method -> Maybe Sig
+ EVM.Dapp: mkSig :: Method -> Sig
- EVM.Effects: Config :: Bool -> Bool -> Bool -> Maybe FilePath -> Bool -> Bool -> Bool -> Bool -> Int -> Int -> Maybe Int -> Int -> Bool -> Bool -> Config
+ EVM.Effects: Config :: Bool -> Bool -> Bool -> Maybe FilePath -> Bool -> Bool -> Bool -> Bool -> Int -> Int -> Maybe Int -> Int -> Bool -> Bool -> Bool -> Int -> Int -> Config
- EVM.FeeSchedule: FeeSchedule :: n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> FeeSchedule n
+ EVM.FeeSchedule: FeeSchedule :: n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> FeeSchedule n
- EVM.Fetch: Session :: Session -> MVar (Maybe W256) -> MVar FetchCache -> Maybe FilePath -> MVar (Set Addr) -> MVar (Set (Addr, W256)) -> Session
+ EVM.Fetch: Session :: Session -> MVar (Maybe W256) -> MVar FetchCache -> Maybe FilePath -> MVar (Set Addr) -> MVar (Set (Addr, W256)) -> IORef (Maybe UTCTime) -> Session
- EVM.Fetch: zero :: forall (t :: VMType) m. Natural -> Maybe Natural -> Fetcher t m
+ EVM.Fetch: zero :: forall (t :: VMType) m. Natural -> Maybe Natural -> Natural -> Fetcher t m
- EVM.Format: formatPartialDetailed :: forall (t :: VMType). Maybe (WarningData t) -> PartialExec -> Text
+ EVM.Format: formatPartialDetailed :: Maybe SrcLookup -> Map (Expr 'EAddr) Contract -> PartialExec -> Text
- EVM.SMT: getAddrs :: (Text -> Expr 'EAddr) -> (Text -> IO Text) -> [Text] -> IO (Map (Expr 'EAddr) Addr)
+ EVM.SMT: getAddrs :: (Text -> Expr 'EAddr) -> ValGetter -> [Text] -> MaybeIO (Map (Expr 'EAddr) Addr)
- EVM.SMT: getBufs :: (Text -> IO Text) -> [Text] -> IO (Map (Expr 'Buf) BufModel)
+ EVM.SMT: getBufs :: ValGetter -> [Text] -> MaybeIO (Map (Expr 'Buf) BufModel)
- EVM.SMT: getStore :: (Text -> IO Text) -> StorageReads -> IO (Map (Expr 'EAddr) (Map W256 W256))
+ EVM.SMT: getStore :: ValGetter -> StorageReads -> MaybeIO (Map (Expr 'EAddr) (Map W256 W256))
- EVM.SMT: getVars :: (Text -> Expr 'EWord) -> (Text -> IO Text) -> [Text] -> IO (Map (Expr 'EWord) W256)
+ EVM.SMT: getVars :: (Text -> Expr 'EWord) -> ValGetter -> [Text] -> MaybeIO (Map (Expr 'EWord) W256)
- EVM.SMT: queryMaxReads :: (Text -> IO Text) -> Map Text (Expr 'EWord) -> IO (Map Text W256)
+ EVM.SMT: queryMaxReads :: ValGetter -> Map Text (Expr 'EWord) -> MaybeIO (Map Text W256)
- EVM.Solidity: solcRuntime :: App m => Text -> Text -> m (Maybe ByteString)
+ EVM.Solidity: solcRuntime :: MonadIO m => ContractName -> SourceCode -> m (Maybe ByteString)
- EVM.Solidity: solcRuntime' :: App m => Text -> Text -> Bool -> m (Maybe ByteString)
+ EVM.Solidity: solcRuntime' :: MonadIO m => ContractName -> SourceCode -> Bool -> m (Maybe ByteString)
- EVM.Solidity: solidity :: MonadUnliftIO m => Text -> Text -> m (Maybe ByteString)
+ EVM.Solidity: solidity :: MonadIO m => ContractName -> SourceCode -> m (Maybe ByteString)
- EVM.Solvers: withSolvers :: App m => Solver -> Natural -> Natural -> Maybe Natural -> (SolverGroup -> m a) -> m a
+ EVM.Solvers: withSolvers :: App m => Solver -> Natural -> Maybe Natural -> Natural -> (SolverGroup -> m a) -> m a
- EVM.SymExec: Dy :: [Prop] -> Expr 'EWord -> Expr 'Buf -> CalldataFragment
+ EVM.SymExec: Dy :: [Prop] -> Expr 'EWord -> Int -> Expr 'Buf -> CalldataFragment
- EVM.SymExec: InterpTask :: Fetcher 'Symbolic m -> IterConfig -> VM 'Symbolic -> Chan (InterpTask m a) -> TVar Natural -> Stepper 'Symbolic (Expr 'End) -> (Expr 'End -> m a) -> InterpTask (m :: Type -> Type) a
+ EVM.SymExec: InterpTask :: Fetcher 'Symbolic m -> IterConfig -> VM 'Symbolic -> Chan (InterpTask m a) -> TVar Natural -> Stepper 'Symbolic (Expr 'End) -> PathHandler m a -> TVar Bool -> InterpTask (m :: Type -> Type) a
- EVM.SymExec: Process :: Expr 'End -> (Expr 'End -> m a) -> Process (m :: k -> Type) (a :: k)
+ EVM.SymExec: Process :: Expr 'End -> PathHandler m a -> Process (m :: k -> Type) (a :: k)
- EVM.SymExec: [handler] :: Process (m :: k -> Type) (a :: k) -> Expr 'End -> m a
+ EVM.SymExec: [handler] :: Process (m :: k -> Type) (a :: k) -> PathHandler m a
- EVM.SymExec: executeVM :: App m => Fetcher 'Symbolic m -> IterConfig -> VM 'Symbolic -> (Expr 'End -> m a) -> m [a]
+ EVM.SymExec: executeVM :: App m => Fetcher 'Symbolic m -> IterConfig -> VM 'Symbolic -> (Expr 'End -> TVar Bool -> m a) -> m [a]
- EVM.SymExec: groupPartials :: forall (t :: VMType). Maybe (WarningData t) -> [Expr 'End] -> [(Integer, String)]
+ EVM.SymExec: groupPartials :: Maybe SrcLookup -> Map (Expr 'EAddr) Contract -> [Expr 'End] -> [(Integer, String)]
- EVM.SymExec: interpret :: App m => Fetcher 'Symbolic m -> IterConfig -> VM 'Symbolic -> Stepper 'Symbolic (Expr 'End) -> (Expr 'End -> m a) -> m [a]
+ EVM.SymExec: interpret :: App m => Fetcher 'Symbolic m -> IterConfig -> VM 'Symbolic -> Stepper 'Symbolic (Expr 'End) -> PathHandler m a -> m [a]
- EVM.SymExec: mkCalldata :: App m => Maybe Sig -> [String] -> m (Expr 'Buf, [Prop])
+ EVM.SymExec: mkCalldata :: App m => Maybe Sig -> [String] -> m ((Expr 'Buf, [Prop]), [Caveat])
- EVM.SymExec: symAbiArg :: Text -> AbiType -> CalldataFragment
+ EVM.SymExec: symAbiArg :: Int -> Text -> AbiType -> CalldataFragment
- EVM.SymExec: symCalldata :: App m => Text -> [AbiType] -> [String] -> Expr 'Buf -> m (Expr 'Buf, [Prop])
+ EVM.SymExec: symCalldata :: App m => Text -> [AbiType] -> [String] -> Expr 'Buf -> m (Expr 'Buf, [Prop], [Caveat])
- EVM.Types: TxState :: W256 -> Word64 -> W256 -> Expr 'EAddr -> Expr 'EAddr -> Expr 'EWord -> SubState -> Bool -> Map (Expr 'EAddr) Contract -> TxState
+ EVM.Types: TxState :: W256 -> Word64 -> W256 -> Expr 'EAddr -> Expr 'EAddr -> Expr 'EWord -> SubState -> Bool -> Map (Expr 'EAddr) Contract -> Word64 -> TxState
- EVM.Types: VM :: Maybe (VMResult t) -> FrameState t -> [Frame t] -> Env -> Block -> TxState -> [Expr 'Log] -> TreePos Empty Trace -> PathsVisited -> !Gas t -> Map CodeLocation (Int, [Expr 'EWord]) -> [Prop] -> RuntimeConfig -> Seq ForkState -> Int -> Map Addr Text -> Map String String -> Int -> Int -> Set (ByteString, W256) -> VM (t :: VMType)
+ EVM.Types: VM :: Maybe (VMResult t) -> FrameState t -> [Frame t] -> Env -> Block -> TxState -> [Expr 'Log] -> TreePos Empty Trace -> PathsVisited -> !Gas t -> Map CodeLocation (Int, [Expr 'EWord]) -> [Prop] -> RuntimeConfig -> Seq ForkState -> Int -> Maybe SrcLookup -> Map Addr Text -> Map String String -> Int -> Int -> Set (ByteString, W256) -> MergeState -> Maybe ExpectedRevert -> VM (t :: VMType)
- EVM.Types: VMOpts :: Contract -> [(Expr 'EAddr, Contract)] -> (Expr 'Buf, [Prop]) -> BaseState -> Expr 'EWord -> W256 -> Expr 'EAddr -> Expr 'EAddr -> Expr 'EAddr -> Gas t -> Word64 -> Expr 'EWord -> Expr 'EWord -> Expr 'EAddr -> W256 -> W256 -> Word64 -> W256 -> W256 -> FeeSchedule Word64 -> W256 -> Bool -> Map (Expr 'EAddr) [W256] -> Bool -> Int -> W256 -> VMOpts (t :: VMType)
+ EVM.Types: VMOpts :: Contract -> [(Expr 'EAddr, Contract)] -> (Expr 'Buf, [Prop]) -> BaseState -> Expr 'EWord -> W256 -> Expr 'EAddr -> Expr 'EAddr -> Expr 'EAddr -> Gas t -> Word64 -> Expr 'EWord -> Expr 'EWord -> Expr 'EAddr -> W256 -> W256 -> Word64 -> W256 -> W256 -> FeeSchedule Word64 -> W256 -> Bool -> Map (Expr 'EAddr) [W256] -> Bool -> Int -> W256 -> W256 -> Word64 -> VMOpts (t :: VMType)
- EVM.UnitTest: UnitTestOptions :: RpcInfo -> SolverGroup -> Session -> Maybe Integer -> Integer -> Maybe Natural -> Text -> Text -> DappInfo -> TestVMParams -> Bool -> Bool -> LoopHeuristic -> UnitTestOptions
+ EVM.UnitTest: UnitTestOptions :: RpcInfo -> SolverGroup -> Session -> Maybe Integer -> Integer -> Maybe Natural -> TestMethodFilter -> DappInfo -> TestVMParams -> Bool -> Bool -> LoopHeuristic -> UnitTestOptions
- EVM.UnitTest: printWarnings :: forall (t :: VMType) b a. Maybe (WarningData t) -> GetUnknownStr b => [Expr 'End] -> [ProofResult a b] -> String -> IO ()
+ EVM.UnitTest: printWarnings :: Maybe SrcLookup -> Map (Expr 'EAddr) Contract -> GetUnknownStr b => [Expr 'End] -> [ProofResult a b] -> String -> IO ()
- EVM.UnitTest: runUnitTestContract :: App m => UnitTestOptions -> BuildOutput -> (Text, [Sig]) -> m [(Bool, Bool)]
+ EVM.UnitTest: runUnitTestContract :: App m => UnitTestOptions -> BuildOutput -> (SolcContract, [Sig]) -> m [(Bool, Bool)]
- EVM.UnitTest: symRun :: App m => UnitTestOptions -> VM 'Concrete -> Sig -> SolcContract -> SourceCache -> m (Bool, Bool)
+ EVM.UnitTest: symRun :: App m => UnitTestOptions -> VM 'Concrete -> Sig -> SourceCache -> m (Bool, Bool)

Files

CHANGELOG.md view
@@ -5,6 +5,53 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased]++## [0.58.0] - 2026-06-26++## Added+- RPC retry with exponential backoff and a shared cooldown across workers, so transient+  network errors and rate limits (e.g. HTTP 429) no longer abort symbolic execution+- Support for a subset of the [`expectRevert`](https://www.getfoundry.sh/reference/cheatcodes/expect-revert#expectrevert) family of foundry cheatcodes:+  - `expectRevert()`+  - `expectRevert(bytes)`+  - `expectRevert(bytes4)`+  - `expectRevert(address)`+  - `expectRevert(bytes4,address)`+  - `expectRevert(bytes,address)`+  - `expectPartialRevert(bytes4)`+  - `expectPartialRevert(bytes4,address)`+- Support for Foundry/Hardhat `console.log` — calls to the console address are intercepted and decoded in traces+- Source location info (file, line, code snippet) is now shown in state merge debug messages+- New opcode: CLZ+- New POr/PAnd/PImpl/Or/And simplification rules+- Support for `--early-abort` flag that aborts symbolic execution as soon as a counterexample is found+- Support for `vm.etch(address, bytecode)` cheatcode to set the code of a contract at a given address+- State merging via speculative execution of both branches of a JUMPI, joining the+  resulting states whenever possible. Amount of speculative execution is+  controlled via `merge-max-budget`+- Missing simplifications for Eq, Mod, SMod, XOR, SHL, SHR, and Or+- A few more simplification rules around Eq, SHL/SHR, Sub+Add combos, and Xor+- Symbolic execution now supports dynamic `bytes`/`string` calldata arguments,+  with their length bounded by `--max-dyn-size` (default 64). When such a bound+  is applied the result carries a program-wide `[CAVEAT]` noting that+  counterexamples requiring longer inputs may be missed (rather than a per-path+  `Partial`)+- `readWord` disjointness rule for `WriteWord (Add (Lit c) X) …` with bounded `X`.++## Changed+- Simplifier now rewrites `Mul(-1, x)` and `~x + 1` to `Sub(0, x)`+- `AbiFunction` and `AbiBytes` parser is now more strict+- We now check length of AbiArrayDynamic and don't crash in case it's too large+- SMT queries now run in separate processes, which allows us to better manage+  timeouts and memory usage by the SMT solver.+- We now allow limiting the SMT solver's memory usage via `--smt-memory` (in MB).+- The option `--smttimeout` is now called `--smt-timeout` for consistency with other+  options.+- We now abort VM run on a failed `assume`.+- Removed SHA256 from the Expr since it was not being used+- When doing CopySlice of exactly one concrete byte, we now correctly simplify+ ## [0.57.0] - 2026-01-08  ## Added
bench/bench-perf.hs view
@@ -20,7 +20,6 @@ import EVM.UnitTest import Test.Tasty.Bench import Control.Monad.ST-import EVM.FeeSchedule (feeSchedule)  -- benchmark hevm using tasty-bench @@ -38,35 +37,10 @@ vm0 c = makeVm $ vm0Opts c  vm0Opts :: Contract -> VMOpts Concrete-vm0Opts c =-  VMOpts-    { contract = c,-      calldata = (ConcreteBuf "", []),-      value = Lit 0xfffffffffffff, -- balance-      baseState = EmptyBase,-      address = LitAddr 0xacab,-      caller = LitAddr 0,-      origin = LitAddr 0,-      gas = 0xffffffffffffffff,-      baseFee = 0,-      priorityFee = 0,-      gaslimit = 0xffffffffffffffff,-      coinbase = LitAddr 0,-      number = Lit 0,-      timestamp = Lit 0,-      blockGaslimit = 0xffffffffffffffff,-      gasprice = 0,-      maxCodeSize = 0xffffffff,-      prevRandao = 0,-      schedule = feeSchedule,-      chainId = 1,-      create = False,-      txAccessList = mempty, -- TODO: support me soon-      allowFFI = False,-      otherContracts = [],-      freshAddresses = 0,-      beaconRoot = 0-    }+vm0Opts c = defaultVMOpts+  { contract = c,+    value = Lit 0xfffffffffffff -- balance+  }  vmOptsToTestVMParams :: VMOpts Concrete -> TestVMParams vmOptsToTestVMParams v =@@ -98,7 +72,7 @@ callMainForBytecode :: App m => ByteString -> m (Either EvmError (Expr 'Buf)) callMainForBytecode bs = do   vm <- vmFromRawByteString bs-  Stepper.interpret (Fetch.zero 0 Nothing) vm (Stepper.evm (abiCall (vmOptsToTestVMParams (vm0Opts (initialContract (RuntimeCode (ConcreteRuntimeCode bs))))) (Left ("main()", emptyAbi))) >> Stepper.execFully)+  Stepper.interpret (Fetch.zero 0 Nothing 1024) vm (Stepper.evm (abiCall (vmOptsToTestVMParams (vm0Opts (initialContract (RuntimeCode (ConcreteRuntimeCode bs))))) (Left ("main()", emptyAbi))) >> Stepper.execFully)  benchMain :: (String, ByteString) -> Benchmark benchMain (name, bs) = bench name $ nfIO $ runApp $ (\x -> if isRight x then () else internalError "failed") <$> callMainForBytecode bs
bench/bench.hs view
@@ -43,9 +43,8 @@   tests <- ts   putStrLn "    executing blockchain tests"   let cases = concat . Map.elems . (fmap Map.toList) $ tests-      ignored = Map.keys BCTests.problematicTests   foldM (\acc (n, c) ->-      if n `elem` ignored+      if isJust (BCTests.findIgnoreReason n)       then pure True       else do         res <- runApp $ runBCTest c@@ -57,7 +56,7 @@ runBCTest x =  do   vm0 <- liftIO $ BCTests.vmForCase x-  result <- Stepper.interpret (Fetch.zero 0 Nothing) vm0 Stepper.runFully+  result <- Stepper.interpret (Fetch.zero 0 Nothing 1024) vm0 Stepper.runFully   writeTrace vm0   pure $ isNothing $ BCTests.checkExpectation x result @@ -67,7 +66,7 @@  findPanics :: App m => Solver -> Natural -> Integer -> ByteString -> m () findPanics solver count iters c = do-  _ <- withSolvers solver count 1 Nothing $ \s -> do+  _ <- withSolvers solver count Nothing 1024 $ \s -> do     let opts = (defaultVeriOpts :: VeriOpts) { iterConf = defaultIterConf {maxIter = Just iters, askSmtIters = iters + 1 }}     checkAssert s allPanicCodes c Nothing [] opts   liftIO $ putStrLn "done"
cli/cli.hs view
@@ -39,12 +39,11 @@ import Text.Read (readMaybe) import JSONL (jsonlBuilder, jsonLine) -import EVM (initialContract, abstractContract, makeVm)+import EVM (initialContract, abstractContract, makeVm, defaultVMOpts) import EVM.ABI (Sig(..))-import EVM.Dapp (dappInfo, DappInfo, emptyDapp)+import EVM.Dapp (dappInfo, DappInfo, emptyDapp, TestMethodInfo(..)) import EVM.Expr qualified as Expr import EVM.Concrete qualified as Concrete-import EVM.FeeSchedule (feeSchedule) import EVM.Fetch qualified as Fetch import EVM.Format (hexByteString, strip0x, formatExpr, indent) import EVM.Solidity@@ -88,9 +87,9 @@   , verb          ::Int   , root          ::Maybe String   , assertionType ::AssertionType-  , solverThreads ::Natural-  , smttimeout    ::Natural-  , smtdebug      ::Bool+  , smtTimeout    ::Natural+  , smtMemory     ::Natural+  , smtDebug      ::Bool   , dumpUnsolved  ::Maybe String   , numSolvers    ::Maybe Natural   , maxIterations ::Integer@@ -101,6 +100,9 @@   , noSimplify    ::Bool   , onlyDeployed  ::Bool   , cacheDir      ::Maybe String+  , earlyAbort    ::Bool+  , mergeMaxBudget :: Int+  , maxDynSize    ::Int   }  commonOptions :: Parser CommonOptions@@ -117,9 +119,9 @@   <*> (option auto $ long "verb"            <> showDefault <> value 1 <> help "Append call trace: {1} failures {2} all")   <*> (optional $ strOption $ long "root"   <> help "Path to  project root directory")   <*> (option auto $ long "assertion-type"  <> showDefault <> value Forge <> help "Assertions as per Forge or DSTest")-  <*> (option auto $ long "solver-threads"  <> showDefault <> value 1 <> help "Number of threads for each solver instance. Only respected for Z3")-  <*> (option auto $ long "smttimeout"      <> value 300 <> help "Timeout given to SMT solver in seconds")-  <*> (switch $ long "smtdebug"             <> help "Print smt queries sent to the solver")+  <*> (option auto $ long "smt-timeout"     <> value 300 <> help "Timeout given to SMT solver in seconds. Not available on Windows")+  <*> (option auto $ long "smt-memory"      <> value defMemLimit <> help "Maximum memory limit for SMT solver in MB. Only available on Linux")+  <*> (switch $ long "smt-debug"            <> help "Print smt queries sent to the solver")   <*> (optional $ strOption $ long "dump-unsolved" <> help "Dump unsolved SMT queries to this (relative) path")   <*> (optional $ option auto $ long "num-solvers" <> help "Number of solver instances to use (default: number of cpu cores)")   <*> (option auto $ long "max-iterations"  <> showDefault <> value 5 <> help "Number of times we may revisit a particular branching point. For no bound, set -1")@@ -130,6 +132,9 @@   <*> (switch $ long "no-simplify" <> help "Don't perform simplification of expressions")   <*> (switch $ long "only-deployed" <> help "When trying to resolve unknown addresses, only use addresses of deployed contracts")   <*> (optional $ strOption $ long "cache-dir" <> help "Directory to save and load RPC cache")+  <*> (switch $  long "early-abort" <> help "Stop exploration immediately upon finding the first counterexample")+  <*> (option auto $ long "merge-max-budget" <> showDefault <> value 100 <> help "Max instructions for speculative merge exploration during path merging")+  <*> (option auto $ long "max-dyn-size" <> showDefault <> value 64 <> help "Max byte length for concretized dynamic types (bytes, string) in symbolic arguments")  data CommonExecOptions = CommonExecOptions   { address       ::Maybe Addr@@ -188,8 +193,8 @@   , rpc           ::Maybe URL   , number        ::Maybe W256   , coverage      ::Bool-  , match         ::Maybe String-  , prefix        ::String+  , match         ::Maybe Text+  , prefix        ::Text   , ffi           ::Bool   } @@ -330,7 +335,7 @@       solver <- getSolver cOpts.solver       cores <- liftIO $ unsafeInto <$> getNumProcessors       let solverCount = fromMaybe cores cOpts.numSolvers-      runEnv env $ withSolvers solver solverCount cOpts.solverThreads (Just cOpts.smttimeout) $ \solvers -> do+      runEnv env $ withSolvers solver solverCount (Just cOpts.smtTimeout) cOpts.smtMemory $ \solvers -> do         buildOut <- readBuildOutput root testOpts.projectType         case buildOut of           Left e -> liftIO $ do@@ -358,7 +363,7 @@         putStrLn "Error: maxBufSize must be at least 0. Negative values do not make sense. A value of zero means at most 1 byte long buffers"         exitFailure       pure Env { config = defaultConfig-        { dumpQueries = cOpts.smtdebug+        { dumpQueries = cOpts.smtDebug         , dumpUnsolved = cOpts.dumpUnsolved         , debug = cOpts.debug         , dumpEndStates = cOpts.debug@@ -372,6 +377,9 @@         , verb = cOpts.verb         , simp = Prelude.not cOpts.noSimplify         , onlyDeployed = cOpts.onlyDeployed+        , earlyAbort = cOpts.earlyAbort+        , mergeMaxBudget = cOpts.mergeMaxBudget+        , maxDynSize = cOpts.maxDynSize         } }  @@ -411,11 +419,11 @@                             , loopHeuristic = cOpts.loopDetectionHeuristic                             }                           }-  calldata <- buildCalldata cOpts eqOpts.sig eqOpts.arg+  (calldata, _caveats) <- buildCalldata cOpts eqOpts.sig eqOpts.arg   solver <- liftIO $ getSolver cOpts.solver   cores <- liftIO $ unsafeInto <$> getNumProcessors   let solverCount = fromMaybe cores cOpts.numSolvers-  withSolvers solver solverCount cOpts.solverThreads (Just cOpts.smttimeout) $ \s -> do+  withSolvers solver solverCount (Just cOpts.smtTimeout) cOpts.smtMemory $ \s -> do     sess <- Fetch.mkSession cOpts.cacheDir Nothing     eq <- equivalenceCheck s (Just sess) (fromJust bytecodeA) (fromJust bytecodeB) veriOpts calldata eqOpts.create     let anyIssues =  not (null eq.partials) || any (isUnknown . fst) eq.res  || any (isError . fst) eq.res@@ -423,7 +431,7 @@       (False, False) -> putStrLn "   \x1b[32m[PASS]\x1b[0m Contracts behave equivalently"       (True, _)      -> putStrLn "   \x1b[31m[FAIL]\x1b[0m Contracts do not behave equivalently"       (_, True)      -> putStrLn "   \x1b[31m[FAIL]\x1b[0m Contracts may not behave equivalently"-    liftIO $ printWarnings Nothing eq.partials (map fst eq.res) "the contracts under test"+    liftIO $ printWarnings Nothing mempty eq.partials (map fst eq.res) "the contracts under test"     case any (isCex . fst) eq.res of       False -> liftIO $ do         when anyIssues exitFailure@@ -472,8 +480,9 @@ parseMaxIters :: Integer -> Maybe Integer parseMaxIters num = if num < 0 then Nothing else Just num --- | Builds a buffer representing calldata based on the given cli arguments-buildCalldata :: App m => CommonOptions -> Maybe Text -> [String] -> m (Expr Buf, [Prop])+-- | Builds a buffer representing calldata based on the given cli arguments,+-- together with any program-wide soundness caveats incurred (see 'Caveat').+buildCalldata :: App m => CommonOptions -> Maybe Text -> [String] -> m ((Expr Buf, [Prop]), [Caveat]) buildCalldata cOpts sig arg = case (cOpts.calldata, sig) of   -- fully abstract calldata   (Nothing, Nothing) -> mkCalldata Nothing []@@ -483,7 +492,7 @@     if (isNothing val) then liftIO $ do       putStrLn $ "Error, invalid calldata: " <>  show c       exitFailure-    else pure (ConcreteBuf (fromJust val), [])+    else pure ((ConcreteBuf (fromJust val), []), [])   -- calldata according to given abi with possible specializations from the `arg` list   (Nothing, Just sig') -> do     method' <- liftIO $ functionAbi sig'@@ -500,7 +509,7 @@   let block' = maybe Fetch.Latest Fetch.BlockNumber cExecOpts.block       blockUrlInfo = (,) block' <$> cExecOpts.rpc   sess <- Fetch.mkSession cOpts.cacheDir cExecOpts.block-  calldata <- buildCalldata cOpts sOpts.sig sOpts.arg+  (calldata, caveats) <- buildCalldata cOpts sOpts.sig sOpts.arg   preState <- symvmFromCommand cExecOpts sOpts cFileOpts sess calldata   errCodes <- case sOpts.assertions of     Nothing -> pure defaultPanicCodes@@ -513,7 +522,7 @@   cores <- liftIO $ unsafeInto <$> getNumProcessors   let solverCount = fromMaybe cores cOpts.numSolvers   solver <- liftIO $ getSolver cOpts.solver-  withSolvers solver solverCount cOpts.solverThreads (Just cOpts.smttimeout) $ \solvers -> do+  withSolvers solver solverCount (Just cOpts.smtTimeout) cOpts.smtMemory $ \solvers -> do     let veriOpts = VeriOpts { iterConf = IterConfig {                               maxIter = parseMaxIters cOpts.maxIterations                               , askSmtIters = cOpts.askSmtIterations@@ -529,6 +538,7 @@     case res of       [] -> do         liftIO $ putStrLn "\nQED: No reachable property violations discovered\n"+        liftIO $ printCaveats caveats         showExtras solvers sOpts calldata expr       _ -> do         let cexs = snd <$> mapMaybe getCex res@@ -540,7 +550,8 @@                  , ""                  ] <> fmap (formatCex (fst calldata) Nothing) cexs         liftIO $ T.putStrLn $ T.unlines counterexamples-        liftIO $ printWarnings Nothing expr res "symbolically"+        liftIO $ printWarnings Nothing mempty expr res "symbolically"+        liftIO $ printCaveats caveats         showExtras solvers sOpts calldata expr         liftIO exitFailure @@ -581,7 +592,7 @@     rpcDat :: Fetch.RpcInfo = Fetch.RpcInfo blockUrlInfo    -- TODO: we shouldn't need solvers to execute this code-  withSolvers Z3 0 1 Nothing $ \solvers -> do+  withSolvers Z3 0 Nothing cOpts.smtMemory $ \solvers -> do     let fetcher = Fetch.oracle solvers (Just sess) rpcDat     vm' <- if execOpts.jsonTrace            then do@@ -705,9 +716,8 @@                   then addr (.address) (Concrete.createAddress (fromJust $ maybeLitAddrSimp origin) (W64 $ word64 (.nonce) 0))                   else addr (.address) (LitAddr 0xacab) -        vm0 baseFee miner ts blockNum prevRan c = makeVm $ VMOpts+        vm0 baseFee miner ts blockNum prevRan c = makeVm $ (defaultVMOpts @Concrete)           { contract       = c-          , otherContracts = []           , calldata       = (calldata, [])           , value          = Lit val           , address        = address@@ -724,14 +734,8 @@           , gasprice       = word (.gasprice) 0           , maxCodeSize    = word (.maxcodesize) 0xffffffff           , prevRandao     = word (.prevRandao) prevRan-          , schedule       = feeSchedule           , chainId        = word (.chainid) 1           , create         = (.create) execOpts-          , baseState      = EmptyBase-          , txAccessList   = mempty -- TODO: support me soon-          , allowFFI       = False-          , freshAddresses = 0-          , beaconRoot     = 0           }         word f def = fromMaybe def (f cExecOpts)         word64 f def = fromMaybe def (f cExecOpts)@@ -811,7 +815,7 @@     address = eaddr (.address) (SymAddr "entrypoint")     originAddr = eaddr (.origin) (SymAddr "origin")     originContr = abstractContract (RuntimeCode (SymbolicRuntimeCode mempty)) originAddr-    vm0 baseFee miner ts blockNum prevRan cd callvalue caller c baseState = makeVm $ VMOpts+    vm0 baseFee miner ts blockNum prevRan cd callvalue caller c baseState = makeVm $ defaultVMOpts       { contract       = c       , otherContracts = [(originAddr, originContr)]       , calldata       = cd@@ -819,7 +823,6 @@       , address        = address       , caller         = caller       , origin         = origin-      , gas            = ()       , gaslimit       = word64 (.gaslimit) 0xffffffffffffffff       , baseFee        = baseFee       , priorityFee    = word (.priorityFee) 0@@ -830,14 +833,9 @@       , gasprice       = word (.gasprice) 0       , maxCodeSize    = word (.maxcodesize) 0xffffffff       , prevRandao     = word (.prevRandao) prevRan-      , schedule       = feeSchedule       , chainId        = word (.chainid) 1       , create         = (.create) sOpts       , baseState      = baseState-      , txAccessList   = mempty-      , allowFFI       = False-      , freshAddresses = 0-      , beaconRoot     = 0       }     word f def = fromMaybe def (f cExecOpts)     word64 f def = fromMaybe def (f cExecOpts)@@ -868,9 +866,8 @@                 in rpcDat { Fetch.blockNumURL = i }     , maxIter = parseMaxIters cOpts.maxIterations     , askSmtIters = cOpts.askSmtIterations-    , smtTimeout = Just cOpts.smttimeout-    , match = T.pack $ fromMaybe ".*" testOpts.match-    , prefix = T.pack testOpts.prefix+    , smtTimeout = Just cOpts.smtTimeout+    , methodFilter = \(TestMethodInfo _ (Sig name _)) -> testOpts.prefix `T.isPrefixOf` name && maybe True (\match -> regexMatches match name) testOpts.match     , testParams = params     , dapp = srcInfo     , ffiAllowed = testOpts.ffi
hevm.cabal view
@@ -2,7 +2,7 @@ name:   hevm version:-  0.57.0+  0.58.0 synopsis:   Symbolic EVM Evaluator description:@@ -101,6 +101,7 @@     EVM.Solvers,     EVM.Exec,     EVM.Format,+    EVM.ConsoleLog,     EVM.Fetch,     EVM.FeeSchedule,     EVM.Op,@@ -113,6 +114,7 @@     EVM.Traversals,     EVM.CSE,     EVM.Keccak,+    EVM.Merge,     EVM.Transaction,     EVM.Types,     EVM.UnitTest,@@ -161,7 +163,7 @@     mtl                               >= 2.2 && < 2.4,     filepath                          >= 1.4.2 && < 1.6,     cereal                            >= 0.5.8 && < 0.6,-    cryptonite                        >= 0.30 && < 0.31,+    crypton                           >= 0.32 && < 1.1,     memory                            >= 0.16.0 && < 0.20,     data-dword                        >= 0.3.1 && < 0.4,     process                           >= 1.6.5 && < 1.7,@@ -175,8 +177,10 @@     pretty-hex                        >= 1.1 && < 1.2,     rosezipper                        >= 0.2 && < 0.3,     wreq                              >= 0.5.3 && < 0.6,+    random                            >= 1.2 && < 1.3,     regex-tdfa                        >= 1.2.3 && < 1.4,     base                              >= 4.9 && < 5,+    time                              >= 1.9 && < 1.15,     smt2-parser                       >= 0.1.0 && < 0.2,     spool                             >= 0.1 && < 0.2,     stm                               >= 2.5.0 && < 2.6.0,@@ -199,7 +203,7 @@     cli   main-is:     cli.hs-  ghc-options: -threaded -with-rtsopts=-N+  ghc-options: -threaded "-with-rtsopts=-N -A32m"   other-modules:     Paths_hevm   autogen-modules:@@ -234,8 +238,6 @@   autogen-modules:     Paths_hevm   build-depends:-    QuickCheck,-    quickcheck-instances,     aeson,     base,     containers,@@ -248,11 +250,9 @@     process,     tasty,     tasty-hunit,-    tasty-quickcheck,     tasty-expected-failure,     temporary,     text,-    vector,     witherable,     optics-core,     witch,@@ -264,19 +264,18 @@   default-language: GHC2021   exposed-modules:     EVM.Test.Utils-    EVM.Test.FuzzSymExec     EVM.Test.BlockchainTests  common test-common   import: test-base   default-language: GHC2021   if flag(devel)-    ghc-options: -threaded -with-rtsopts=-N+    ghc-options: -threaded "-with-rtsopts=-N -A32m"   build-depends:-    test-utils+    test-utils,+    vector,   other-modules:     EVM.Test.Utils-    EVM.Test.FuzzSymExec     EVM.Test.BlockchainTests  --- Test Suites ---@@ -288,6 +287,14 @@     exitcode-stdio-1.0   main-is:     test.hs+  other-modules:+    EVM.Equivalence.EquivalenceTests+    EVM.Expr.ExprTests+    EVM.Expr.Generator+    EVM.ConcreteExecution.ConcreteExecutionTests+    EVM.SymExec.SymExecTests+    EVM.Test.FuzzSymExec+    EVM.Test.FoundryTests   build-depends:     base16,     binary,@@ -296,7 +303,10 @@     here,     time,     optics-extra,-    regex+    QuickCheck,+    quickcheck-instances,+    regex,+    tasty-quickcheck,  -- these tests require network access so we split them into a separate test -- suite to make it easy to skip them when running nix-build@@ -374,7 +384,7 @@   hs-source-dirs:     bench   ghc-options:-    -O2 -threaded -with-rtsopts=-N -fproc-alignment=64+    -O2 -threaded -rtsopts "-with-rtsopts=-N1 -A32m" -fproc-alignment=64   other-modules:     Paths_hevm   autogen-modules:
src/EVM.hs view
@@ -15,6 +15,8 @@   writeByte, bufLength, indexWord, readBytes, copySlice, wordToAddr, maybeLitByteSimp, maybeLitWordSimp, maybeLitAddrSimp) import EVM.Expr qualified as Expr import EVM.FeeSchedule (FeeSchedule (..))+import EVM.FeeSchedule qualified as Fees (feeSchedule)+import EVM.Merge qualified as Merge import EVM.Op import EVM.Precompiled qualified import EVM.Solidity@@ -58,7 +60,7 @@ import Data.Vector.Storable qualified as VS import Data.Vector.Storable.Mutable qualified as VS.Mutable import Data.Vector.Storable.ByteString (vectorToByteString, byteStringToVector)-import Data.Word (Word8, Word32, Word64)+import Data.Word (Word8, Word64) import Text.Read (readMaybe) import Witch (into, tryFrom, unsafeInto, tryInto) @@ -66,6 +68,38 @@ import Crypto.Hash qualified as Crypto import Crypto.Number.ModArithmetic (expFast) +defaultVMOpts :: VMOps t => VMOpts t+defaultVMOpts = VMOpts+  { contract       = emptyContract+  , otherContracts = []+  , calldata       = mempty+  , value          = Lit 0+  , address        = LitAddr 0xacab+  , caller         = LitAddr 0+  , origin         = LitAddr 0+  , gas            = toGas maxBound+  , gaslimit       = maxBound+  , baseFee        = 0+  , priorityFee    = 0+  , coinbase       = LitAddr 0+  , number         = Lit 0+  , timestamp      = Lit 0+  , blockGaslimit  = maxBound+  , gasprice       = 0+  , maxCodeSize    = 0xffffffff+  , prevRandao     = 0+  , schedule       = Fees.feeSchedule+  , chainId        = 1+  , create         = False+  , baseState      = EmptyBase+  , txAccessList   = mempty+  , allowFFI       = False+  , freshAddresses = 0+  , beaconRoot     = 0+  , parentHash     = 0+  , txdataFloorGas = Fees.feeSchedule.g_transaction+  }+ blankState :: VMOps t => ST RealWorld (FrameState t) blankState = do   memory <- ConcreteMemory <$> VS.Mutable.new 0@@ -116,7 +150,7 @@       initialAccessedStorageKeys = fromList $ foldMap (uncurry (map . (,))) (Map.toList txaccessList)       touched = if o.create then [txorigin] else [txorigin, txtoAddr]   memory <- ConcreteMemory <$> VS.Mutable.new 0-  pure $ setEIP4788Storage o $ VM+  pure $ setEIP2935Storage o $ setEIP4788Storage o $ VM     { result = Nothing     , frames = mempty     , tx = TxState@@ -129,6 +163,7 @@       , subState = SubState mempty touched initialAccessedAddrs initialAccessedStorageKeys mempty mempty       , isCreate = o.create       , txReversion = Map.fromList ((o.address,o.contract):o.otherContracts)+      , txdataFloorGas = o.txdataFloorGas       }     , logs = []     , traces = Zipper.fromForest []@@ -160,12 +195,15 @@       }     , forks = Seq.singleton (ForkState env block mempty "")     , currentFork = 0+    , srcLookup = Nothing     , labels = mempty     , osEnv = mempty     , freshVar = 0     , exploreDepth = 0     , keccakPreImgs = fromList []     , pathsVisited = mempty+    , mergeState = defaultMergeState+    , expectedRevert = Nothing     }     where     env = Env@@ -214,6 +252,25 @@       }     Nothing -> vm +-- https://eips.ethereum.org/EIPS/eip-2935+setEIP2935Storage :: VMOpts t -> VM t -> VM t+setEIP2935Storage o vm = do+  let historyStorageAddress = LitAddr 0x0000F90827F1C53a10cb7A02335B175320002935+  case Map.lookup historyStorageAddress vm.env.contracts of+    Just historyContract -> do+      let+        historyBufferLength = 8191+        slotIdx = Expr.mod (Expr.sub o.number (Lit 1)) (Lit historyBufferLength)+        storage = Expr.writeStorage slotIdx (Lit o.parentHash) historyContract.storage+      vm {+        env = vm.env {+          contracts = Map.insert historyStorageAddress+                                 (historyContract { storage } :: Contract)+                                 vm.env.contracts+        }+      }+    Nothing -> vm+ -- | Initialize an abstract contract with unknown code unknownContract :: Expr EAddr -> Contract unknownContract addr = Contract@@ -301,7 +358,7 @@     doStop = finishFrame (FrameReturned mempty)     litSelf = maybeLitAddrSimp self -  if isJust litSelf && (fromJust litSelf) > 0x0 && (fromJust litSelf) <= 0xa then do+  if isJust litSelf && isPrecompileAddr' (fromJust litSelf) then do     -- call to precompile     let ?op = 0x00 -- dummy value     let calldatasize = bufLength vm.state.calldata@@ -361,16 +418,13 @@                   pushSym y          OpSwap i -> {-# SCC "OpSwap" #-}-          case (stk ^? ix_i, stk ^? ix_0) of-            (Just ei, Just e0) ->-              burn g_verylow $ do-                next-                zoom (#state % #stack) $ do-                  ix_i .= e0-                  ix_0 .= ei-            _ -> underrun-          where-            (ix_i, ix_0) = (ix (into i), ix 0)+          let idx = into i in+          case splitAt idx stk of+          (e0:middle, ei:after) ->+            burn g_verylow $ do+              next+              assign' (#state % #stack) $ ei : middle ++ (e0 : after)+          _ -> underrun          OpLog n -> {-# SCC "OpLog" #-}           notStatic $@@ -425,6 +479,7 @@         OpShl -> {-# SCC "OpShl" #-} stackOp2 g_verylow Expr.shl         OpShr -> {-# SCC "OpShr" #-} stackOp2 g_verylow Expr.shr         OpSar -> {-# SCC "OpSar" #-} stackOp2 g_verylow Expr.sar+        OpClz -> {-# SCC "OpClz" #-} stackOp1 g_low Expr.clz          -- more accurately referred to as KECCAK         OpSha3 -> {-# SCC "OpSha3" #-}@@ -473,7 +528,11 @@             next >> pushSym vm.state.callvalue          OpCalldataload -> {-# SCC "OpCalldataload" #-} stackOp1 g_verylow $-          \ind -> Expr.readWord ind vm.state.calldata+        -- Reading past call data length should always return 0. We force this in the concrete case.+        -- However, symbolic mode relies on `Expr.readWord` which has wrap-around semantics+          \ind -> case (ind, vm.state.calldata) of+            (Lit i, ConcreteBuf bs) | i > (fromIntegral $ BS.length bs) -> Lit 0+            _ -> Expr.readWord ind vm.state.calldata          OpCalldatasize -> {-# SCC "OpCalldatasize" #-}           limitStack 1 . burn g_base $@@ -520,7 +579,7 @@                                Just b -> pushSym (bufLength b)                                Nothing -> pushSym $ CodeSize x               case x of-                a@(LitAddr _) -> if a == cheatCode+                a@(LitAddr _) -> if a == cheatCode || a == consoleAddr                   then do                     next                     assign' (#state % #stack) xs@@ -651,14 +710,14 @@                 accessMemoryWord x $ do                   next                   buf <- readMemory x (Lit 32)-                  let w = Expr.readWordFromBytes (Lit 0) buf+                  let w = Expr.readWord (Lit 0) buf                   assign' (#state % #stack) (w : xs)             _ -> underrun           OpMcopy -> {-# SCC "OpMcopy" #-}           case stk of-            dstOff:srcOff:sz:xs ->  do+            dstOff:srcOff:sz:xs ->               case sz of                 Lit sz' -> do                   let words_copied = (sz' + 31) `div` 32@@ -666,14 +725,16 @@                   burn (g_verylow + (unsafeInto g_mcopy)) $                     accessMemoryRange srcOff sz $ accessMemoryRange dstOff sz $ do                       next+                      assign' (#state % #stack) xs                       mcopy sz srcOff dstOff                 _ -> do                   -- symbolic, ignore gas                   next+                  assign' (#state % #stack) xs                   mcopy sz srcOff dstOff-              assign' (#state % #stack) xs             _ -> underrun             where+            mcopy (Lit 0) _ _ = pure ()             mcopy sz srcOff dstOff = do                   m <- gets (.state.memory)                   case m of@@ -813,14 +874,39 @@          OpJumpi -> {-# SCC "OpJumpi" #-}           case stk of-            x:y:xs -> forceConcreteLimitSz x 2 "JUMPI: symbolic jumpdest" $ \x' ->+            x:cond:xs -> forceConcreteLimitSz x 2 "JUMPI: symbolic jumpdest" $ \maybeTarget ->               burn g_high $                 let jump :: Bool -> EVM t ()                     jump False = assign' (#state % #stack) xs >> next-                    jump _    = case tryInto x' of+                    jump _    = case tryInto maybeTarget of                       Left _ -> vmError BadJumpDestination-                      Right i -> checkJump i xs-                in branch conf.maxDepth y jump+                      Right jumpTarget -> checkJump jumpTarget xs+                    -- For Symbolic execution, try forward-jump merge first+                    symbolicMerge :: EVM Symbolic ()+                    symbolicMerge = case tryInto maybeTarget of+                      Left _ ->+                        -- Invalid destination - but we only error if we try to jump+                        -- Use branch to decide based on condition+                        branch conf.maxDepth cond $ \case+                          False -> assign' (#state % #stack) xs >> next+                          True -> vmError BadJumpDestination+                      Right jumpTarget -> do+                        -- Check if we're already in merge mode (speculative execution)+                        ms <- use #mergeState+                        if ms.msActive then+                          -- Inside speculation: nested branch, bail out+                          -- This will cause speculateLoop to return Nothing+                          assign #result $ Just $ VMFailure BadJumpDestination+                        else do+                          merged <- Merge.tryMergeForwardJump conf (exec1 conf) vm.state.pc jumpTarget cond xs+                          unless merged $ do+                            let jumpSym :: Bool -> EVM Symbolic ()+                                jumpSym False = assign' (#state % #stack) xs >> next+                                jumpSym _    = checkJump jumpTarget xs+                            branch conf.maxDepth cond jumpSym+                in case eqT @t @Symbolic of+                     Just Refl -> symbolicMerge+                     Nothing -> branch conf.maxDepth cond jump             _ -> underrun          OpPc -> {-# SCC "OpPc" #-}@@ -984,7 +1070,7 @@                       touchAddress from'                        let (cost, gas') = costOfCreate fees availableGas xSize True-                      newAddr <- create2Address self xSalt initCode+                      newAddr <- create2Address from' xSalt initCode                       _ <- accessAccountForGas newAddr                       burn' cost $                         create from' this xSize gas' xValue xs newAddr (ConcreteBuf initCode)@@ -1046,6 +1132,10 @@             xOffset:xSize:_ ->               accessMemoryRange xOffset xSize $ do                 output <- readMemory xOffset xSize+                case vm.expectedRevert of+                  Just e | isNothing e.actualReverter ->+                    assign #expectedRevert (Just e { actualReverter = Just self })+                  _ -> pure ()                 finishFrame (FrameReverted output)             _ -> underrun @@ -1252,21 +1342,26 @@         forceConcreteBuf input "MODEXP" $ \input' -> do           let             (lenb, lene, lenm) = parseModexpLength input'--            output = ConcreteBuf $-              if isZero (96 + lenb + lene) lenm input'-              then truncpadlit (unsafeInto lenm) (asBE (0 :: Int))-              else-                let-                  b = asInteger $ lazySlice 96 lenb input'-                  e = asInteger $ lazySlice (96 + lenb) lene input'-                  m = asInteger $ lazySlice (96 + lenb + lene) lenm input'-                in-                  padLeft (unsafeInto lenm) (asBE (expFast b e m))-          assign' (#state % #stack) (Lit 1 : xs)-          assign (#state % #returndata) output-          copyBytesToMemory output outSize (Lit 0) outOffset-          next+            -- EIP-7823: ModExp upper bounds - each input limited to 1024 bytes (8192 bits)+            modexpLimit = 1024 :: W256+          if lenb > modexpLimit || lene > modexpLimit || lenm > modexpLimit+          then precompileFail  -- EIP-7823: fail and consume all gas if limits exceeded+          else do+            let+              output = ConcreteBuf $+                if isZero (96 + lenb + lene) lenm input'+                then truncpadlit (unsafeInto lenm) (asBE (0 :: Int))+                else+                  let+                    b = asInteger $ lazySlice 96 lenb input'+                    e = asInteger $ lazySlice (96 + lenb) lene input'+                    m = asInteger $ lazySlice (96 + lenb + lene) lenm input'+                  in+                    padLeft (unsafeInto lenm) (asBE (expFast b e m))+            assign' (#state % #stack) (Lit 1 : xs)+            assign (#state % #returndata) output+            copyBytesToMemory output outSize (Lit 0) outOffset+            next        -- ECADD       0x6 ->@@ -1337,8 +1432,8 @@ parseModexpLength :: ByteString -> (W256, W256, W256) parseModexpLength input =   let lenb = word $ LS.toStrict $ lazySlice  0 32 input-      lene = word $ LS.toStrict $ lazySlice 32 64 input-      lenm = word $ LS.toStrict $ lazySlice 64 96 input+      lene = word $ LS.toStrict $ lazySlice 32 32 input+      lenm = word $ LS.toStrict $ lazySlice 64 32 input   in (lenb, lene, lenm)  --- checks if a range of ByteString bs starting at offset and length size is all zeros.@@ -1523,8 +1618,20 @@       -- deposit the code from a creation tx       creation <- use (#tx % #isCreate)       createe  <- use (#state % #contract)-      createeExists <- (Map.member createe) <$> use (#env % #contracts)-      when (creation && createeExists) $+      createeContract <- preuse (#env % #contracts % ix createe)+      -- Check if this is a collision case (target has RuntimeCode instead of InitCode)+      let isCollision = creation && case createeContract of+            Just c -> case c.code of+              InitCode _ _ -> False+              RuntimeCode _ -> True   -- collision: existing contract has RuntimeCode+              UnknownCode _ -> internalError "cannot determine collision with unknown code"+            Nothing -> internalError "create transaction but no code found"+      -- For collision: burn all gas (failed CREATE consumes all gas)+      when isCollision $ assign (#state % #gas) initialGas+      -- Only replace code if this is a creation tx, the contract exists,+      -- and it has InitCode (not RuntimeCode from a collision)+      let shouldReplaceCode = creation && not isCollision+      when shouldReplaceCode $         case output of           ConcreteBuf bs -> replaceCode createe (RuntimeCode (ConcreteRuntimeCode bs))           _ ->@@ -1756,6 +1863,11 @@ cheatCode :: Expr EAddr cheatCode = LitAddr $ unsafeInto (keccak' "hevm cheat code") +-- | The address used by Foundry/Hardhat console.log+-- 0x000000000000000000636F6e736F6c652e6c6f67 (ASCII "console.log")+consoleAddr :: Expr EAddr+consoleAddr = LitAddr 0x000000000000000000636F6e736F6c652e6c6f67+ cheat   :: forall t . (?conf :: Config, ?op :: Word8, VMOps t, Typeable t)   => Gas t -> (Expr EWord, Expr EWord) -> (Expr EWord, Expr EWord) -> [Expr EWord]@@ -1833,8 +1945,10 @@   , action "assume(bool)" $       \sig input -> case decodeStaticArgs 0 1 input of         [c] -> do-          modifying #constraints ((:) (PEq (Lit 1) c))-          doStop+          whenSymbolicElse (modifying #constraints ((:) (PEq (Lit 1) c)) >> doStop) $ do+            case c of+              Lit v -> if (v == 0) then (terminateVMWithError AssumeCheatFailed) else doStop+              _ -> internalError "Symbolic value encountered in concrete mode"         _ -> vmError (BadCheatCode "assume(bool) parameter decoding failed." sig)    , action "roll(uint256)" $@@ -1977,6 +2091,58 @@           _ -> vmError (BadCheatCode "setEnv(string,string) address decoding failed" sig)         _ -> vmError (BadCheatCode "setEnv(string,string) parameter decoding failed" sig) +  , action "etch(address,bytes)" $+      \sig input ->  case decodeBuf [AbiAddressType, AbiBytesDynamicType] input of+        (CAbi [AbiAddress addr, AbiBytesDynamic bytes],"") -> fetchAccount (LitAddr addr) $ \_ -> do+            replaceCodeEtch (LitAddr addr) (RuntimeCode (ConcreteRuntimeCode bytes))+            doStop+        _ -> vmError (BadCheatCode "etch(address,bytes) address decoding failed" sig)++  , action "expectRevert()" $+      \_ _ -> setExpectedRevert Nothing False Nothing++  , action "expectRevert(bytes)" $+      \sig input -> case decodeBuf [AbiBytesDynamicType] input of+        (CAbi [AbiBytesDynamic bs],"") ->+          setExpectedRevert (Just (ConcreteBuf bs)) False Nothing+        _ -> vmError (BadCheatCode "expectRevert(bytes) parameter decoding failed" sig)++  , action "expectRevert(bytes4)" $+      \sig input -> case decodeBuf [AbiBytesType 4] input of+        (CAbi [AbiBytes 4 bs],"") ->+          setExpectedRevert (Just (ConcreteBuf bs)) False Nothing+        _ -> vmError (BadCheatCode "expectRevert(bytes4) parameter decoding failed" sig)++  , action "expectRevert(address)" $+      \sig input -> case decodeBuf [AbiAddressType] input of+        (CAbi [AbiAddress addr],"") ->+          setExpectedRevert Nothing False (Just (LitAddr addr))+        _ -> vmError (BadCheatCode "expectRevert(address) parameter decoding failed" sig)++  , action "expectRevert(bytes4,address)" $+      \sig input -> case decodeBuf [AbiBytesType 4, AbiAddressType] input of+        (CAbi [AbiBytes 4 bs, AbiAddress addr],"") ->+          setExpectedRevert (Just (ConcreteBuf bs)) False (Just (LitAddr addr))+        _ -> vmError (BadCheatCode "expectRevert(bytes4,address) parameter decoding failed" sig)++  , action "expectRevert(bytes,address)" $+      \sig input -> case decodeBuf [AbiBytesDynamicType, AbiAddressType] input of+        (CAbi [AbiBytesDynamic bs, AbiAddress addr],"") ->+          setExpectedRevert (Just (ConcreteBuf bs)) False (Just (LitAddr addr))+        _ -> vmError (BadCheatCode "expectRevert(bytes,address) parameter decoding failed" sig)++  , action "expectPartialRevert(bytes4)" $+      \sig input -> case decodeBuf [AbiBytesType 4] input of+        (CAbi [AbiBytes 4 bs],"") ->+          setExpectedRevert (Just (ConcreteBuf bs)) True Nothing+        _ -> vmError (BadCheatCode "expectPartialRevert(bytes4) parameter decoding failed" sig)++  , action "expectPartialRevert(bytes4,address)" $+      \sig input -> case decodeBuf [AbiBytesType 4, AbiAddressType] input of+        (CAbi [AbiBytes 4 bs, AbiAddress addr],"") ->+          setExpectedRevert (Just (ConcreteBuf bs)) True (Just (LitAddr addr))+        _ -> vmError (BadCheatCode "expectPartialRevert(bytes4,address) parameter decoding failed" sig)+   -- Single-value environment read cheat actions   , $(envReadSingleCheat "envBool(string)") AbiBool stringToBool   , $(envReadSingleCheat "envUint(string)") (AbiUInt 256) stringToWord256@@ -2022,6 +2188,7 @@   , action "assertEq(address,address)" $ assertEq AbiAddressType   , action "assertEq(bytes32,bytes32)" $ assertEq (AbiBytesType 32)   , action "assertEq(string,string)"   $ assertEq (AbiStringType)+  , action "assertEq(bytes,bytes)"     $ assertEq AbiBytesDynamicType   --   , action "assertNotEq(bool,bool)"       $ assertNotEq AbiBoolType   , action "assertNotEq(uint256,uint256)" $ assertNotEq (AbiUIntType 256)@@ -2029,6 +2196,7 @@   , action "assertNotEq(address,address)" $ assertNotEq AbiAddressType   , action "assertNotEq(bytes32,bytes32)" $ assertNotEq (AbiBytesType 32)   , action "assertNotEq(string,string)"   $ assertNotEq (AbiStringType)+  , action "assertNotEq(bytes,bytes)"      $ assertNotEq AbiBytesDynamicType   --   , action "assertLt(uint256,uint256)" $ assertLt (AbiUIntType 256)   , action "assertLt(int256,int256)"   $ assertSLt (AbiIntType 256)@@ -2039,6 +2207,45 @@   , action "assertGe(uint256,uint256)" $ assertGe (AbiUIntType 256)   , action "assertGe(int256,int256)"   $ assertSGe (AbiIntType 256)   --+  , action "assertApproxEqAbs(uint256,uint256,uint256)" $ assertApproxEqAbsUint+  , action "assertApproxEqAbs(int256,int256,uint256)"   $ assertApproxEqAbsInt+  --+  , action "assertApproxEqRel(uint256,uint256,uint256)" $ assertApproxEqRelUint+  , action "assertApproxEqRel(int256,int256,uint256)"   $ assertApproxEqRelInt+  --+  -- Variants with a trailing `err` string. The string is decoded away and never+  -- used; each behaves exactly like its non-message counterpart.+  , action "assertEq(bool,bool,string)"       $ withMsg 2 (assertEq AbiBoolType)+  , action "assertEq(uint256,uint256,string)" $ withMsg 2 (assertEq (AbiUIntType 256))+  , action "assertEq(int256,int256,string)"   $ withMsg 2 (assertEq (AbiIntType 256))+  , action "assertEq(address,address,string)" $ withMsg 2 (assertEq AbiAddressType)+  , action "assertEq(bytes32,bytes32,string)" $ withMsg 2 (assertEq (AbiBytesType 32))+  , action "assertEq(string,string,string)"   $ assertEqMsgDyn AbiStringType+  , action "assertEq(bytes,bytes,string)"      $ assertEqMsgDyn AbiBytesDynamicType+  --+  , action "assertNotEq(bool,bool,string)"       $ withMsg 2 (assertNotEq AbiBoolType)+  , action "assertNotEq(uint256,uint256,string)" $ withMsg 2 (assertNotEq (AbiUIntType 256))+  , action "assertNotEq(int256,int256,string)"   $ withMsg 2 (assertNotEq (AbiIntType 256))+  , action "assertNotEq(address,address,string)" $ withMsg 2 (assertNotEq AbiAddressType)+  , action "assertNotEq(bytes32,bytes32,string)" $ withMsg 2 (assertNotEq (AbiBytesType 32))+  , action "assertNotEq(string,string,string)"   $ assertNotEqMsgDyn AbiStringType+  , action "assertNotEq(bytes,bytes,string)"      $ assertNotEqMsgDyn AbiBytesDynamicType+  --+  , action "assertLt(uint256,uint256,string)" $ withMsg 2 (assertLt (AbiUIntType 256))+  , action "assertLt(int256,int256,string)"   $ withMsg 2 (assertSLt (AbiIntType 256))+  , action "assertLe(uint256,uint256,string)" $ withMsg 2 (assertLe (AbiUIntType 256))+  , action "assertLe(int256,int256,string)"   $ withMsg 2 (assertSLe (AbiIntType 256))+  , action "assertGt(uint256,uint256,string)" $ withMsg 2 (assertGt (AbiUIntType 256))+  , action "assertGt(int256,int256,string)"   $ withMsg 2 (assertSGt (AbiIntType 256))+  , action "assertGe(uint256,uint256,string)" $ withMsg 2 (assertGe (AbiUIntType 256))+  , action "assertGe(int256,int256,string)"   $ withMsg 2 (assertSGe (AbiIntType 256))+  --+  , action "assertApproxEqAbs(uint256,uint256,uint256,string)" $ withMsg 3 assertApproxEqAbsUint+  , action "assertApproxEqAbs(int256,int256,uint256,string)"   $ withMsg 3 assertApproxEqAbsInt+  --+  , action "assertApproxEqRel(uint256,uint256,uint256,string)" $ withMsg 3 assertApproxEqRelUint+  , action "assertApproxEqRel(int256,int256,uint256,string)"   $ withMsg 3 assertApproxEqRelInt+  --   , action "toString(address)" $ toStringCheat AbiAddressType   , action "toString(bool)"    $ toStringCheat AbiBoolType   , action "toString(uint256)" $ toStringCheat (AbiUIntType 256)@@ -2057,8 +2264,6 @@     frameReturnExpr e = finishFrame (FrameReturned e)     frameRevert :: VMOps t => ByteString -> EVM t ()     frameRevert err = finishFrame (FrameReverted $ errorMsg err)-    errorMsg :: ByteString -> Expr Buf-    errorMsg err = ConcreteBuf $ selector "Error(string)" <> encodeAbiValue (AbiTuple $ V.fromList [AbiString err])     continueOnce cont = do       assign #result Nothing       cont@@ -2088,17 +2293,47 @@       ")  parameter decoding failed. Error: " <> show abivals     revertErr a b comp = frameRevert $ "assertion failed: " <>       BS8.pack (show a) <> " " <> comp <> " " <> BS8.pack (show b)-    genAssert comp exprComp invComp name abitype sig input = do-      case decodeBuf [abitype, abitype] input of-        (CAbi [a, b],"") | a `comp` b -> doStop-        (CAbi [a, b],"")-> revertErr a b invComp-        (SAbi [ew1, ew2],"") -> case (Expr.simplify (Expr.iszero $ exprComp ew1 ew2)) of+    -- 1e18 (100% in foundry's 18-decimal fixed point) as Integer and W256+    scale1e18 :: Integer+    scale1e18 = 10^(18::Int)+    scale1e18W :: W256+    scale1e18W = 10^(18::Int)+    -- largest absDelta with absDelta*1e18 < 2^256 (symbolic overflow guard)+    maxSafeRelDelta :: W256+    maxSafeRelDelta = (maxBound :: W256) `Prelude.div` scale1e18W+    -- shared rel-assertion failure message; realDelta is pre-rendered+    relFailMsg :: Show a => a -> a -> Word256 -> String -> ByteString+    relFailMsg a b maxPercentDelta realDelta = "assertion failed: " <>+      BS8.pack (show a) <> " !~= " <> BS8.pack (show b) <>+      " (max delta: " <> BS8.pack (show maxPercentDelta) <>+      ", real delta: " <> BS8.pack realDelta <> ")"+    genAssert = genAssertN []+    -- like genAssert but ignores `extra` trailing ABI args (the err-string+    -- overloads). Only used directly for string operands; static operands strip+    -- the err arg upstream via withMsg to keep the symbolic path.+    genAssertN extra comp exprComp invComp name abitype sig input = do+      case decodeBuf ([abitype, abitype] ++ extra) input of+        (CAbi (a:b:_),"") | a `comp` b -> doStop+        (CAbi (a:b:_),"")-> revertErr a b invComp+        (SAbi (ew1:ew2:_),"") -> case (Expr.simplify (Expr.iszero $ exprComp ew1 ew2)) of           Lit 0 -> doStop           Lit _ -> revertErr ew1 ew2 invComp           ew -> branch (?conf).maxDepth ew $ \case             False -> doStop             True -> revertErr ew1 ew2 invComp         abivals -> vmError (BadCheatCode (paramDecodeErr abitype name abivals) sig)+    -- keep the first n 32-byte head words, dropping the trailing err string so+    -- operand-only handlers decode normally (and keep SAbi for symbolic operands)+    keepHeadWords n input =+      foldr (\i acc -> let off = Lit (fromIntegral (i * 32 :: Int))+                       in writeWord off (readWord off input) acc)+            mempty [0 .. n - 1]+    -- Run a no-message handler after stripping the trailing `err` string.+    withMsg n handler sig input = handler sig (keepHeadWords n input)+    -- dynamic operands (string/bytes) can't be sliced: decode [op,op,string] and+    -- compare the first two concretely (no symbolic path), ignoring the err string+    assertEqMsgDyn    op = genAssertN [AbiStringType] (==) Expr.eq "!=" "assertEq" op+    assertNotEqMsgDyn op = genAssertN [AbiStringType] (/=) (\a b -> Expr.iszero $ Expr.eq a b) "==" "assertNotEq" op     assertEq =    genAssert (==) Expr.eq "!=" "assertEq"     assertNotEq = genAssert (/=) (\a b -> Expr.iszero $ Expr.eq a b) "==" "assertNotEq"     assertLt =    genAssert (<)  Expr.lt ">=" "assertLt"@@ -2109,11 +2344,250 @@     assertSLe =   genAssert (<=) (\a b -> Expr.iszero $ Expr.sgt a b) ">" "assertLe"     assertGe =    genAssert (>=) Expr.geq "<" "assertGe"     assertSGe =   genAssert (>=) (\a b -> Expr.iszero $ Expr.slt a b) "<" "assertGe"+    -- | assertApproxEqAbs for uint256: passes when |left - right| <= maxDelta+    assertApproxEqAbsUint sig input = do+      case decodeBuf [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256] input of+        (CAbi [AbiUInt _ a, AbiUInt _ b, AbiUInt _ maxDelta],"") ->+          let delta = if a > b then a - b else b - a+          in if delta <= maxDelta then doStop+             else frameRevert $ "assertion failed: " <>+               BS8.pack (show a) <> " !~= " <> BS8.pack (show b) <>+               " (max delta: " <> BS8.pack (show maxDelta) <>+               ", real delta: " <> BS8.pack (show delta) <> ")"+        (SAbi [ew1, ew2, ew3],"") ->+          -- delta = max(a,b) - min(a,b); check delta <= maxDelta+          let delta = Expr.sub (Expr.max ew1 ew2) (Expr.min ew1 ew2)+          in case Expr.simplify (Expr.iszero $ Expr.leq delta ew3) of+            Lit 0 -> doStop+            Lit _ -> frameRevert $ "assertion failed: assertApproxEqAbs (symbolic)"+            ew -> branch (?conf).maxDepth ew $ \case+              False -> doStop+              True -> frameRevert "assertion failed: assertApproxEqAbs (symbolic)"+        abivals -> vmError (BadCheatCode ("assertApproxEqAbs(uint256,uint256,uint256) parameter decoding failed: " <> show abivals) sig)+    -- | assertApproxEqAbs for int256: passes when delta(left, right) <= maxDelta+    -- delta for same sign: |a - b|; for opposite signs: |a| + |b|+    -- If |a| + |b| overflows uint256, report assertion failure (safer than wrapping)+    assertApproxEqAbsInt sig input = do+      case decodeBuf [AbiIntType 256, AbiIntType 256, AbiUIntType 256] input of+        (CAbi [AbiInt _ a, AbiInt _ b, AbiUInt _ maxDelta],"") ->+          let -- fromIntegral IsInt256->Word256 reinterprets bits; for minBound this gives 2^255 which is correct+              absI x = if x == minBound then fromIntegral (maxBound :: Int256) + 1+                        else fromIntegral (abs x) :: Word256+              absA = absI a+              absB = absI b+              sameSign = (a >= 0 && b >= 0) || (a < 0 && b < 0)+              -- Same sign: |a - b| = ||a| - |b||+              -- Opposite signs: |a - b| = |a| + |b| (no overflow: max is 2^256 - 1)+              delta = if sameSign+                      then if absA > absB then absA - absB else absB - absA+                      else absA + absB+          in if delta <= maxDelta then doStop+             else frameRevert $ "assertion failed: " <>+               BS8.pack (show a) <> " !~= " <> BS8.pack (show b) <>+               " (max delta: " <> BS8.pack (show maxDelta) <>+               ", real delta: " <> BS8.pack (show delta) <> ")"+        (SAbi [ew1, ew2, ew3],"") ->+          -- For symbolic int256, compute unsigned delta via:+          -- if sameSign(a,b) then |a-b| else |a|+|b|+          -- We approximate with unsigned sub of max/min which works for same-sign+          -- but for full correctness with symbolic int256, we use the uint comparison+          let delta = Expr.sub (Expr.max ew1 ew2) (Expr.min ew1 ew2)+          in case Expr.simplify (Expr.iszero $ Expr.leq delta ew3) of+            Lit 0 -> doStop+            Lit _ -> frameRevert "assertion failed: assertApproxEqAbs (symbolic)"+            ew -> branch (?conf).maxDepth ew $ \case+              False -> doStop+              True -> frameRevert "assertion failed: assertApproxEqAbs (symbolic)"+        abivals -> vmError (BadCheatCode ("assertApproxEqAbs(int256,int256,uint256) parameter decoding failed: " <> show abivals) sig)+    -- | assertApproxEqRel(uint256): pass iff |a-b|*1e18/b <= maxPercentDelta+    -- (1e18 == 100%, denominator is the reference b), per forge-std. Special cases:+    -- b==0&&a==0 -> pass; b==0&&a/=0 -> "undefined"; result > uint256 -> overflow.+    assertApproxEqRelUint sig input = do+      case decodeBuf [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256] input of+        (CAbi [AbiUInt _ a, AbiUInt _ b, AbiUInt _ maxPercentDelta],"")+          | b == 0 ->+            if a == 0 then doStop+            else frameRevert $ relFailMsg a b maxPercentDelta "undefined"+          | otherwise ->+            let absDelta = if a > b then a - b else b - a+                -- in Integer to avoid the intermediate overflow (foundry uses U512)+                percentDelta = (toInteger absDelta * scale1e18) `Prelude.div` toInteger b+            in if percentDelta > toInteger (maxBound :: Word256)+               then frameRevert "assertion failed: overflow in delta calculation"+               else if percentDelta <= toInteger maxPercentDelta then doStop+               else frameRevert $ relFailMsg a b maxPercentDelta (show percentDelta)+        (SAbi [ew1, ew2, ew3],"") ->+          -- Sound encoding: Expr.mul truncates mod 2^256, so it's only trustworthy+          -- when absDelta*1e18 can't overflow (absDelta <= maxSafeRelDelta). Pass+          -- only when provably within bound; overflow-possible or b==0&&a/=0 fail+          -- (b==0&&a==0 passes). May give false positives, never an unsound pass.+          let absDelta     = Expr.sub (Expr.max ew1 ew2) (Expr.min ew1 ew2)+              percentDelta = Expr.div (Expr.mul absDelta (Lit scale1e18W)) ew2+              bZero        = Expr.iszero ew2+              aNotZero     = Expr.iszero (Expr.iszero ew1)+              overflow     = Expr.gt absDelta (Lit maxSafeRelDelta)+              pdExceeds    = Expr.gt percentDelta ew3+              -- fail iff (b == 0 && a /= 0) || (b /= 0 && (overflow || pd > max))+              failCond     = Expr.or (Expr.and bZero aNotZero)+                                     (Expr.and (Expr.iszero bZero) (Expr.or overflow pdExceeds))+          in case Expr.simplify failCond of+            Lit 0 -> doStop+            Lit _ -> frameRevert "assertion failed: assertApproxEqRel (symbolic)"+            ew -> branch (?conf).maxDepth ew $ \case+              False -> doStop+              True -> frameRevert "assertion failed: assertApproxEqRel (symbolic)"+        abivals -> vmError (BadCheatCode ("assertApproxEqRel(uint256,uint256,uint256) parameter decoding failed: " <> show abivals) sig)+    -- | assertApproxEqRel(int256): as uint, but signed absDelta (same sign+    -- ||a|-|b||, opposite |a|+|b|) over denominator |b|.+    assertApproxEqRelInt sig input = do+      case decodeBuf [AbiIntType 256, AbiIntType 256, AbiUIntType 256] input of+        (CAbi [AbiInt _ a, AbiInt _ b, AbiUInt _ maxPercentDelta],"") ->+          let -- fromIntegral Int256->Word256 reinterprets bits; for minBound this gives 2^255 which is correct+              absI x = if x == minBound then fromIntegral (maxBound :: Int256) + 1+                        else fromIntegral (abs x) :: Word256+              absA = absI a+              absB = absI b+              sameSign = (a >= 0 && b >= 0) || (a < 0 && b < 0)+              absDelta = if sameSign+                         then if absA > absB then absA - absB else absB - absA+                         else absA + absB+          in if b == 0+             then if a == 0 then doStop+                  else frameRevert $ relFailMsg a b maxPercentDelta "undefined"+             else+               let percentDelta = (toInteger absDelta * scale1e18) `Prelude.div` toInteger absB+               in if percentDelta > toInteger (maxBound :: Word256)+                  then frameRevert "assertion failed: overflow in delta calculation"+                  else if percentDelta <= toInteger maxPercentDelta then doStop+                  else frameRevert $ relFailMsg a b maxPercentDelta (show percentDelta)+        (SAbi [_, _, _],"") ->+          -- A sound signed encoding isn't expressible with hevm's Expr (would need+          -- full-precision signed abs + |b| division), so we conservatively fail+          -- rather than risk an unsound pass. May be a false positive.+          frameRevert "assertion failed: assertApproxEqRel (symbolic int256 operands not supported)"+        abivals -> vmError (BadCheatCode ("assertApproxEqRel(int256,int256,uint256) parameter decoding failed: " <> show abivals) sig)     toStringCheat abitype sig input = do       case decodeBuf [abitype] input of         (CAbi [val],"") -> frameReturn $ AbiTuple $ V.fromList [AbiString $ Char8.pack $ show val]         _ -> vmError (BadCheatCode ("toString parameter decoding failed for " <> show abitype) sig) +-- * expectRevert support+--+-- The cheat captures the depth of the call stack so that when the immediately+-- following sub-call returns/reverts at the same depth, finishFrame consumes+-- the expectation and rewrites the result.++setExpectedRevert+  :: VMOps t => Maybe (Expr Buf) -> Bool -> Maybe (Expr EAddr) -> EVM t ()+setExpectedRevert reasonExp isPartial reverterExp = do+  vm <- get+  case vm.expectedRevert of+    Just _ ->+      vmError (BadCheatCode "expectRevert: previous expectation not yet consumed" 0)+    Nothing -> do+      assign #expectedRevert $ Just ExpectedRevert+        { reason         = reasonExp+        , partialMatch   = isPartial+        , reverter       = reverterExp+        , depth          = length vm.frames+        , actualReverter = Nothing+        }+      finishFrame (FrameReturned mempty)++-- | ABI-encode an Error(string) revert payload.+errorMsg :: ByteString -> Expr Buf+errorMsg err = ConcreteBuf $ selector "Error(string)" <> encodeAbiValue (AbiTuple $ V.fromList [AbiString err])++assertionFailedBuf :: ByteString -> Expr Buf+assertionFailedBuf msg = errorMsg ("assertion failed: " <> msg)++-- | If a concrete buffer is an Error(string)-encoded revert, return the inner+-- string bytes. Otherwise Nothing.+stripErrorPrefix :: ByteString -> Maybe ByteString+stripErrorPrefix bs+  | BS.length bs < 4 = Nothing+  | BS.take 4 bs /= selector "Error(string)" = Nothing+  | otherwise = case decodeBuf [AbiStringType] (ConcreteBuf (BS.drop 4 bs)) of+      (CAbi [AbiString inner], _) -> Just inner+      _ -> Nothing++-- | Outcome of matching an expectation against an actual revert.+-- 'Indeterminate' carries the reason a concrete decision could not be made;+-- the caller should bail with an unexpected-symbolic error. A definite+-- 'Mismatch' takes precedence over an 'Indeterminate' on a different axis, so+-- an indeterminate reason check cannot mask a known reverter mismatch (and+-- vice versa).+data RevertMatch+  = Match+  | Mismatch+  | Indeterminate String+  deriving (Eq, Show)++matchExpectedRevert+  :: ExpectedRevert -> Expr Buf -> Expr EAddr -> RevertMatch+matchExpectedRevert e actual reverterAddr = combine reasonMatches reverterMatches+  where+    combine Mismatch     _            = Mismatch+    combine _            Mismatch     = Mismatch+    combine (Indeterminate s) _            = Indeterminate s+    combine _            (Indeterminate s) = Indeterminate s+    combine Match        Match        = Match++    reasonMatches = case e.reason of+      Nothing -> Match+      Just expected+        | e.partialMatch -> matchPartialPrefix expected actual+        | otherwise      -> matchFullReason   expected actual++    reverterMatches = case e.reverter of+      Nothing -> Match+      Just want -> case (want, reverterAddr) of+        (LitAddr a, LitAddr b) -> if a == b then Match else Mismatch+        (SymAddr a, SymAddr b) -> if a == b then Match else Mismatch+        (SymAddr a, LitAddr b) -> Indeterminate $ "cannot compare concrete and symbolic addresses: " ++ (unpack a) ++ " and " ++ (show b)+        (LitAddr a, SymAddr b) -> Indeterminate $ "cannot compare concrete and symbolic addresses: " ++ (show a) ++ " and " ++ (unpack b)+        _ -> internalError "unexpected GVar"++-- | Compare only the first four bytes (selector). Succeeds when both sides have+-- a concrete 4-byte prefix even if the rest of the buffer is symbolic.+matchPartialPrefix :: Expr Buf -> Expr Buf -> RevertMatch+matchPartialPrefix expected actual = case (prefix4 (Expr.simplify expected), prefix4 (Expr.simplify actual)) of+  (Right e, Right a)   -> if e == a then Match else Mismatch+  (Left s, _)          -> Indeterminate s+  (_, Left s)          -> Indeterminate s+  where+    prefix4 :: Expr Buf -> Either String ByteString+    prefix4 buf = BS.pack <$> traverse (toLitByte . Expr.simplify . flip Expr.readByte buf . Lit) [0..3]+    toLitByte :: Expr Byte -> Either String Word8+    toLitByte (LitByte w) = Right w+    toLitByte _           = Left "first 4 bytes of revert data are symbolic"++-- | Full-buffer reason match. Both sides are normalized by stripping an+-- Error(string) wrapper if present, so a wrapped expected matches a raw actual+-- inner string and vice versa.+matchFullReason :: Expr Buf -> Expr Buf -> RevertMatch+matchFullReason expected actual = case (Expr.simplify expected, Expr.simplify actual) of+  (ConcreteBuf eBs, ConcreteBuf aBs) ->+    let normalize bs = fromMaybe bs (stripErrorPrefix bs)+    in if normalize eBs == normalize aBs then Match else Mismatch+  _ -> Indeterminate "revert data is symbolic"++-- | Dummy success returndata used when swallowing a matched revert at a CALL+-- boundary. 8192 zero bytes — large enough to satisfy Solidity's return-data+-- decoders for any reasonable return type. Matches foundry's+-- @DUMMY_CALL_OUTPUT@ in @crates/cheatcodes/src/test/revert_handlers.rs@:+-- https://github.com/foundry-rs/foundry/blob/master/crates/cheatcodes/src/test/revert_handlers.rs+dummySuccessReturn :: Expr Buf+dummySuccessReturn = ConcreteBuf (BS.replicate 8192 0)++-- | Sentinel address pushed on the stack when swallowing a matched CREATE+-- revert. Matches foundry's @DUMMY_CREATE_ADDRESS@ (= 0x01) in+-- @crates/cheatcodes/src/test/revert_handlers.rs@ so tests that inspect the+-- LHS of @new C()@ after expectRevert behave identically across both tools:+-- https://github.com/foundry-rs/foundry/blob/master/crates/cheatcodes/src/test/revert_handlers.rs+dummyCreateAddress :: Expr EAddr+dummyCreateAddress = LitAddr 1+ -- * General call implementation ("delegateCall") -- note that the continuation is ignored in the precompile case delegateCall@@ -2138,6 +2612,12 @@             precompiledContract this gasGiven xTo' xContext' xValue xInOffset xInSize xOutOffset xOutSize xs   | xTo == cheatCode = do       cheat gasGiven (xInOffset, xInSize) (xOutOffset, xOutSize) xs+  | xTo == consoleAddr = do+      calldata <- readMemory xInOffset xInSize+      pushTrace $ ConsoleLog calldata+      assign' (#state % #stack) (Lit 1 : xs)+      assign (#state % #returndata) mempty+      next   | otherwise =       callChecks this gasGiven xContext xTo xValue xInOffset xInSize xOutOffset xOutSize xs $         \xGas -> do@@ -2196,14 +2676,21 @@  -- -- * Contract creation --- EIP 684+-- EIP-684 and EIP-7610: collision if nonce != 0, code is non-empty, or storage is non-empty collision :: Maybe Contract -> Bool collision c' = case c' of-  Just c -> c.nonce /= Just 0 || case c.code of+  Just c -> c.nonce /= Just 0 || not (isStorageEmpty c.storage) || case c.code of     RuntimeCode (ConcreteRuntimeCode "") -> False     RuntimeCode (SymbolicRuntimeCode b) -> not $ null b     _ -> True   Nothing -> False+  where+    isStorageEmpty :: Expr Storage -> Bool+    isStorageEmpty = \case+      ConcreteStore m -> Map.null m+      AbstractStore _ _ -> True -- empty symbolic store+      SStore _ _ _ -> False     -- has writes, so non-empty+      GVar _ -> internalError "unexpected global variable"  create :: forall t. (?op :: Word8, ?conf::Config, VMOps t)   => Expr EAddr -> Contract@@ -2338,6 +2825,7 @@               { balance = now.balance               , nonce = now.nonce               , storage = now.storage+              , tStorage = now.tStorage               }         RuntimeCode _ ->           internalError $ "Can't replace code of deployed contract " <> show target@@ -2346,6 +2834,23 @@       Nothing ->         internalError "Can't replace code of nonexistent contract" +replaceCodeEtch :: Expr EAddr -> ContractCode -> EVM t ()+replaceCodeEtch target newCode =+  zoom (#env % #contracts % at target) $+    get >>= \case+      Just now -> case now.code of+        InitCode _ _ -> internalError "Can't etch code of contract in init code phase"+        RuntimeCode _ -> do+          put . Just $+            ((now :: Contract)+              { code = newCode+              , codehash = hashcode newCode+              , opIxMap = mkOpIxMap newCode+              , codeOps = mkCodeOps newCode+              })+        UnknownCode _ -> internalError "Can't etch unknown code"+      Nothing -> put . Just $ initialContract newCode+ replaceCodeOfSelf :: ContractCode -> EVM t () replaceCodeOfSelf newCode = do   vm <- get@@ -2374,6 +2879,15 @@   | FrameErrored EvmError -- ^ Any other error   deriving Show +terminateVMWithError :: VMOps t => EvmError -> EVM t ()+terminateVMWithError err = do+  vm <- get+  case vm.frames of+    [] -> finishFrame (FrameErrored err)+    _ -> do+      finishFrame (FrameErrored err)+      terminateVMWithError err+ finishAllFramesAndStop :: VMOps t => EVM t () finishAllFramesAndStop = do   vm <- get@@ -2391,7 +2905,73 @@ finishFrame :: VMOps t => FrameResult -> EVM t () finishFrame how = do   oldVm <- get+  case oldVm.expectedRevert of+    Just e+      -- we are back at the start depth (ignoring calls to the cheat address)+      | length oldVm.frames == e.depth+      , not (poppingCheatFrame oldVm.frames)+      -> do+          assign #expectedRevert Nothing+          let isCreate = case oldVm.frames of+                Frame { context = CreationContext {} } : _ -> True+                _ -> False+          case how of+            FrameReverted actual ->+              let reverterAddr =+                    fromMaybe+                      (internalError "expectRevert: missing actualReverter for FrameReverted")+                      e.actualReverter+              in case matchExpectedRevert e actual reverterAddr of+                Match+                  -- if the expected revert occured, then in order to match+                  -- foundry, we need to add some sentinel values to the stack+                  -- and pretend the actual call/create succeeded.+                  | isCreate -> do+                      finishFrameNoExpectation (FrameReverted actual)+                      -- drop the prev return value+                      modifying (#state % #stack) (drop 1)+                      -- add our sentinel+                      pushAddr dummyCreateAddress+                      -- empty returndata+                      assign (#state % #returndata) mempty+                  | otherwise -> do+                      -- process the revert with our dummy returndata+                      finishFrameNoExpectation (FrameReverted dummySuccessReturn)+                      -- drop the prev return value+                      modifying (#state % #stack) (drop 1)+                      -- pretend the call succeeded+                      push 1+                Mismatch ->+                  finishFrameNoExpectation+                    (FrameReverted (assertionFailedBuf "expected revert did not match"))+                Indeterminate reason -> do+                  unexpectedSymArg ("expectRevert: " <> reason) [actual]+                  finishFrameNoExpectation how+            FrameReturned _ ->+              finishFrameNoExpectation+                (FrameReverted (assertionFailedBuf "expected call did not revert"))+            FrameErrored _ -> finishFrameNoExpectation how +      -- we have popped out past the start depth of the expected revert (i.e. returned without a revert occuring)+      | length oldVm.frames < e.depth+      , not (poppingCheatFrame oldVm.frames)+      -> do+          assign #expectedRevert Nothing+          finishFrameNoExpectation $+            FrameReverted (assertionFailedBuf "expected revert never occurred")+    _ -> finishFrameNoExpectation how+  where+    -- | True when the frame being popped is a cheat-call frame. Such frames+    -- must be ignored by the expectRevert interception, as cheatcode calls are+    -- not checked by `expectRevert`+    poppingCheatFrame (Frame { context = CallContext { target } } : _) = target == cheatCode+    poppingCheatFrame _ = False++-- | The non-interception body of finishFrame+finishFrameNoExpectation :: VMOps t => FrameResult -> EVM t ()+finishFrameNoExpectation how = do+  oldVm <- get+   case oldVm.frames of     -- Is the current frame the only one?     [] -> do@@ -2867,27 +3447,45 @@  concreteModexpGasFee :: ByteString -> Word64 concreteModexpGasFee input =-  if lenb < into (maxBound :: Word32) &&-     (lene < into (maxBound :: Word32) || (lenb == 0 && lenm == 0)) &&-     lenm < into (maxBound :: Word64)-  then-    max 200 ((multiplicationComplexity * iterCount) `div` 3)-  else-    maxBound -- TODO: this is not 100% correct, return Nothing on overflow-  where-    (lenb, lene, lenm) = parseModexpLength input-    ez = isZero (96 + lenb) lene input-    e' = word $ LS.toStrict $-      lazySlice (96 + lenb) (min 32 lene) input-    nwords :: Word64-    nwords = ceilDiv (unsafeInto $ max lenb lenm) 8-    multiplicationComplexity = nwords * nwords-    iterCount' :: Word64-    iterCount' | lene <= 32 && ez = 0-               | lene <= 32 = unsafeInto (log2 e')-               | e' == 0 = 8 * (unsafeInto lene - 32)-               | otherwise = unsafeInto (log2 e') + 8 * (unsafeInto lene - 32)-    iterCount = max iterCount' 1+  let (lenb, lene, lenm) = parseModexpLength input+      -- EIP-7823: Cap input lengths at 1024 bytes (8192 bits)+      -- If any length exceeds this, the precompile will fail+      -- and consume all provided gas+      eip7823Limit :: W256+      eip7823Limit = 1024+  in if lenb > eip7823Limit || lene > eip7823Limit || lenm > eip7823Limit+     then 0  -- EIP-7823: limits exceeded, gas cost is 0 (precompileFail will consume all gas)+     else+       let ez = isZero (96 + lenb) lene input+           e' = word $ LS.toStrict $ lazySlice (96 + lenb) (min 32 lene) input+           maxLength :: Word64+           maxLength = unsafeInto $ max lenb lenm+           nwords :: Word64+           nwords = ceilDiv maxLength 8+           -- Check for overflow in nwords * nwords calculation+           multiplicationComplexity :: Word64+           multiplicationComplexity+             | maxLength <= 32 = 16+             | nwords > 0xFFFFFFFF = maxBound  -- nwords^2 would overflow+             | otherwise = let nw2 = nwords * nwords+                           in if nw2 > maxBound `div` 2+                              then maxBound+                              else 2 * nw2+           lene64 :: Word64+           lene64 = unsafeInto lene+           iterCount' :: Word64+           iterCount' | lene <= 32 && ez = 0+                      | lene <= 32 = unsafeInto (log2 e')+                      | e' == 0 = 16 * (lene64 - 32)+                      | otherwise = unsafeInto (log2 e') + 16 * (lene64 - 32)+           iterCount = max iterCount' 1+           -- Check for overflow in final multiplication+           result = if multiplicationComplexity == maxBound+                    then maxBound+                    else if iterCount > maxBound `div` multiplicationComplexity+                         then maxBound+                         else multiplicationComplexity * iterCount+       in max 500 result  -- Gas cost of precompiles costOfPrecompile :: FeeSchedule Word64 -> Addr -> Expr Buf -> Word64@@ -2986,9 +3584,13 @@   n <- use (#env % #freshAddresses)   pure $ SymAddr ("freshSymAddr" <> (pack $ show n)) +isPrecompileAddr' :: Addr -> Bool+isPrecompileAddr' 0x100 = True+isPrecompileAddr' x = 0x0 < x && x <= 0x11+ isPrecompileAddr :: Expr EAddr -> Bool isPrecompileAddr = \case-  LitAddr a -> 0x0 < a && a <= 0x09+  LitAddr a -> isPrecompileAddr' a   SymAddr _ -> False   GVar _ -> internalError "Unexpected GVar" @@ -3074,7 +3676,7 @@         assign (#pathsVisited % at (loc, iteration)) (Just v)         assign (#iterations % at loc) (Just (iteration + 1, stack))         continue v-      -- Both paths are possible; we ask for more input+      -- Both paths are possible       runBothPaths loc exploreDepth UnknownBranch =         (fork depthLimit exploreDepth ) . PleaseRunBoth $ (runBothPaths loc exploreDepth) . Case @@ -3169,9 +3771,12 @@     else continue    gasTryFrom (forceLit -> w256) =-    case tryFrom w256 of-      Left _ -> Left ()-      Right a -> Right a+    -- EVM spec: gas values larger than Word64 max should be treated as "max gas"+    -- The 63/64 rule will cap it appropriately later+    -- Note: We can't use tryFrom here because TryFrom W256 Word64 always succeeds (truncates)+    if w256 > into (maxBound :: Word64)+    then Right maxBound  -- Cap at Word64 max for overflow+    else Right (unsafeInto w256)    -- Gas cost of create, including hash cost if needed   costOfCreate (FeeSchedule {..}) availableGas size hashNeeded = (createCost, initGas)@@ -3213,10 +3818,17 @@     gasRemaining <- use (#state % #gas)      let-      sumRefunds   = sum (snd <$> tx.subState.refunds)-      gasUsed      = tx.gaslimit - gasRemaining-      cappedRefund = min (quot gasUsed 5) sumRefunds-      originPay    = (into $ gasRemaining + cappedRefund) * tx.gasprice+      gasUsedBeforeRefund = tx.gaslimit - gasRemaining+      sumRefunds          = sum (snd <$> tx.subState.refunds)+      cappedRefund        = min (quot gasUsedBeforeRefund 5) sumRefunds+      gasUsedAfterRefund  = gasUsedBeforeRefund - cappedRefund+      -- EIP-7623: Apply floor cost based on calldata tokens+      gasUsed             = max gasUsedAfterRefund tx.txdataFloorGas+      -- Adjust refund if floor kicks in+      actualRefund        = if gasUsed > gasUsedAfterRefund+                            then cappedRefund - (gasUsed - gasUsedAfterRefund)+                            else cappedRefund+      originPay    = (into $ gasRemaining + actualRefund) * tx.gasprice       minerPay     = tx.priorityFee * (into gasUsed)      modifying (#env % #contracts)
src/EVM/ABI.hs view
@@ -61,9 +61,9 @@  import Prelude hiding (Foldable(..)) -import EVM.Expr (readWord, isLitWord)+import EVM.Expr (readWord, readByte, isLitWord, simplify) import EVM.Types-import EVM.Expr (maybeLitWordSimp)+import EVM.Expr (maybeLitWordSimp, maybeLitByteSimp)  import Control.Applicative ((<|>)) import Control.Monad (replicateM, replicateM_, forM_, void)@@ -72,6 +72,8 @@ import Data.Bits (shiftL, shiftR, (.&.), testBit, bit, complement, (.|.)) import Data.ByteString (ByteString) import Data.ByteString qualified as BS+import Data.DoubleWord (Word160(..), Word128(..))+import Data.Serialize qualified as Cereal import Data.ByteString.Base16 qualified as BS16 import Data.ByteString.Char8 qualified as Char8 import Data.ByteString.Lazy qualified as BSLazy@@ -112,7 +114,7 @@   | AbiArrayDynamic AbiType (Vector AbiValue)   | AbiArray        Int AbiType (Vector AbiValue)   | AbiTuple        (Vector AbiValue)-  | AbiFunction     BS.ByteString+  | AbiFunction     Addr FunctionSelector   deriving (Read, Eq, Ord, Generic)  class ShowAlter a where@@ -133,7 +135,7 @@     "[" ++ intercalate ", " (show <$> Vector.toList v) ++ "]"   show (AbiTuple v) =     "(" ++ intercalate ", " (show <$> Vector.toList v) ++ ")"-  show (AbiFunction b)       = show (ByteStringS b)+  show (AbiFunction addr sel) = show (ByteStringS (word160Bytes addr <> word32Bytes sel))  -- | Pretty-print some 'AbiValue'. instance ShowAlter AbiValue where@@ -151,7 +153,7 @@     "[" ++ intercalate ", " (showAlter <$> Vector.toList v) ++ "]"   showAlter (AbiTuple v) = "AbiTuple" <>     "(" ++ intercalate ", " (show <$> Vector.toList v) ++ ")"-  showAlter (AbiFunction b)       = "AbiFunction " <> show (ByteStringS b)+  showAlter (AbiFunction addr sel) = "AbiFunction " <> show (ByteStringS (word160Bytes addr <> word32Bytes sel))  data AbiType   = AbiUIntType         Int@@ -217,7 +219,7 @@   AbiArrayDynamic t _ -> AbiArrayDynamicType t   AbiArray n t _      -> AbiArrayType n t   AbiTuple v          -> AbiTupleType (abiValueType <$> v)-  AbiFunction _       -> AbiFunctionType+  AbiFunction _ _     -> AbiFunctionType  getAbi :: AbiType -> Get AbiValue getAbi t = label (Text.unpack (abiTypeSolidity t)) $@@ -255,14 +257,20 @@      AbiArrayDynamicType t' -> do       AbiUInt _ n <- label "array length" (getAbi (AbiUIntType 256))-      AbiArrayDynamic t' <$>+      let maxLen = fromIntegral (maxBound :: Int) :: Word256+      if n > maxLen then fail "array length exceeds maximum Int"+      else AbiArrayDynamic t' <$>         label "array body" (getAbiSeq (unsafeInto n) (repeat t'))      AbiTupleType ts ->       AbiTuple <$> getAbiSeq (Vector.length ts) (Vector.toList ts) -    AbiFunctionType ->-      AbiFunction <$> getBytesWith256BitPadding (24 :: Int)+    AbiFunctionType -> do+      addrBytes <- BS.pack <$> replicateM 20 getWord8+      sel <- FunctionSelector <$> getWord32be+      skip 8  -- padding to 32 bytes+      let addr = bytesToAddr addrBytes+      pure (AbiFunction addr sel)  putAbi :: AbiValue -> Put putAbi = \case@@ -296,8 +304,10 @@   AbiTuple v ->     putAbiSeq v -  AbiFunction b -> do-    putAbi (AbiBytes 24 b)+  AbiFunction addr (FunctionSelector sel) -> do+    forM_ (BS.unpack (word160Bytes addr)) putWord8+    putWord32be sel+    replicateM_ 8 (putWord8 0)  -- padding to 32 bytes  -- | Decode a sequence type (e.g. tuple / array). Will fail for non sequence types getAbiSeq :: Int -> [AbiType] -> Get (Vector AbiValue)@@ -351,7 +361,7 @@         AbiBool _    -> 32         AbiTuple v   -> sum (abiHeadSize <$> v)         AbiArray _ _ xs -> sum (abiHeadSize <$> xs)-        AbiFunction _ -> 32+        AbiFunction _ _ -> 32         _ -> internalError "impossible"  putAbiSeq :: Vector AbiValue -> Put@@ -409,20 +419,26 @@     , P.string "bool"    $> AbiBoolType     , P.string "string"  $> AbiStringType -    , sizedType "uint" AbiUIntType-    , sizedType "int"  AbiIntType-    , sizedType "bytes" AbiBytesType+    , sizedType "uint" AbiUIntType (const True)+    , sizedType "int"  AbiIntType (const True)+    , sizedType "bytes" AbiBytesType (\n -> n >= 1 && n <= 32)      , P.string "bytes" $> AbiBytesDynamicType     , P.string "tuple" $> AbiTupleType v     , P.string "function" $> AbiFunctionType++    -- Fallback for user-defined types (e.g. enums like "Order.OrderType").+    -- Solidity libraries emit qualified enum names in their ABI instead of+    -- the underlying uint8. Treat any unrecognized identifier as uint8.+    , P.try (P.some (P.alphaNumChar P.<|> P.char '.') $> AbiUIntType 8)     ]    where-    sizedType :: Text -> (Int -> AbiType) -> P.Parsec () Text AbiType-    sizedType s f = P.try $ do+    sizedType :: Text -> (Int -> AbiType) -> (Int -> Bool) -> P.Parsec () Text AbiType+    sizedType s f valid = P.try $ do       void (P.string s)-      fmap (f . read) (P.some P.digitChar)+      n <- read <$> P.some P.digitChar+      if valid n then pure (f n) else P.empty  pack32 :: Int -> [Word32] -> Word256 pack32 n xs =@@ -440,6 +456,19 @@ roundTo32Bytes :: Integral a => a -> a roundTo32Bytes n = 32 * div (n + 31) 32 +word32Bytes :: FunctionSelector -> ByteString+word32Bytes (FunctionSelector w) = BSLazy.toStrict $ runPut $ putWord32be w++bytesToAddr :: ByteString -> Addr+bytesToAddr bs = case Cereal.runGet m bs of+  Right addr -> addr+  Left _ -> internalError "bytesToAddr: failed to parse 20 bytes"+  where+    m = do a <- Cereal.getWord32be+           b <- Cereal.getWord64be+           c <- Cereal.getWord64be+           pure $ Addr (Word160 a (Word128 b c))+ emptyAbi :: AbiValue emptyAbi = AbiTuple mempty @@ -481,7 +510,9 @@                             <|> (do Boolz b <- readS_to_P reads                                     pure $ AbiBool b) parseAbiValue (AbiBytesType n) = AbiBytes n <$> do ByteStringS bytes <- bytesP-                                                   pure bytes+                                                   if BS.length bytes == n+                                                     then pure bytes+                                                     else pfail parseAbiValue AbiBytesDynamicType = AbiBytesDynamic <$> do ByteStringS bytes <- bytesP                                                            pure bytes parseAbiValue AbiStringType = AbiString <$> do Char8.pack <$> readS_to_P reads@@ -492,8 +523,13 @@   AbiArray n typ <$> do a <- listP (parseAbiValue typ)                         pure $ Vector.fromList a parseAbiValue (AbiTupleType _) = internalError "tuple types not supported"-parseAbiValue AbiFunctionType = AbiFunction <$> do ByteStringS bytes <- bytesP-                                                   pure bytes+parseAbiValue AbiFunctionType = do+  ByteStringS bytes <- bytesP+  if BS.length bytes == 24+    then let addr = bytesToAddr (BS.take 20 bytes)+             sel = FunctionSelector (word32 (BS.unpack (BS.drop 20 bytes)))+         in pure (AbiFunction addr sel)+    else pfail  listP :: ReadP a -> ReadP [a] listP parser = between (char '[') (char ']') ((do skipSpaces@@ -518,18 +554,88 @@     Right ("", _, args) -> (CAbi (toList args), "")     Right (str, _, args) -> (CAbi (toList args), "with trailing bytes: " ++ show (BSLazy.unpack str))     Left (_, _, err) -> (NoVals, "error decoding abi: " ++ err)-decodeBuf tps buf =-  if any isDynamic tps then (NoVals, "dynamic types not supported in symbolic decoding")-  else-    let-      vs = decodeStaticArgs 0 (length tps) buf-      asBS = mconcat $ fmap word256Bytes (mapMaybe maybeLitWordSimp vs)-    in if not (all isLitWord vs)-       then (SAbi vs, "")-       else decodeBuf tps (ConcreteBuf asBS)-  where-    isDynamic t = abiKind t == Dynamic+decodeBuf tps b = case simplify b of+  ConcreteBuf buf -> decodeBuf tps (ConcreteBuf buf)+  buf -> if any isDynamic tps then tryDecodeDynamic tps buf+    else let+        vs = decodeStaticArgs 0 (length tps) buf+        asBS = mconcat $ fmap word256Bytes (mapMaybe maybeLitWordSimp vs)+      in if not (all isLitWord vs)+         then (SAbi vs, "")+         else decodeBuf tps (ConcreteBuf asBS)+    where+      isDynamic t = abiKind t == Dynamic +-- | Try to decode arguments including dynamic types like bytes/string+-- For dynamic types, reads offset from head, then length and data from that offset+tryDecodeDynamic :: [AbiType] -> Expr Buf -> (AbiVals, String)+tryDecodeDynamic tps buf =+  let headVals = decodeStaticArgs 0 (length tps) buf+      headSimp = map simplify headVals+  in case decodeWithExprs 0 tps headSimp buf of+    Left err -> (NoVals, err)+    Right abiVals -> (CAbi abiVals, "")++-- | Decode types using head expressions+-- For static types, the expression is the value+-- For dynamic types, the expression must be a concrete offset+decodeWithExprs :: Int -> [AbiType] -> [Expr EWord] -> Expr Buf -> Err [AbiValue]+decodeWithExprs _ [] [] _ = Right []+decodeWithExprs _ [] _ _ = Left "mismatched types and values"+decodeWithExprs _ _ [] _ = Left "mismatched types and values"+decodeWithExprs headPos (t:ts) (v:vs) buf = do+  val <- decodeOneArgExpr t v buf+  rest <- decodeWithExprs (headPos + 32) ts vs buf+  pure (val : rest)++-- | Decode a single argument from its head expression+-- For dynamic types, the expression must be a concrete offset+decodeOneArgExpr :: AbiType -> Expr EWord -> Expr Buf -> Err AbiValue+decodeOneArgExpr t expr buf = case abiKind t of+  Static -> maybe (Left $ "static type requires concrete value, got: " ++ show expr)+    (decodeStaticArgConcrete t) simpExpr+  Dynamic -> maybe (Left $ "dynamic type requires concrete offset, got: " ++ show expr)+    (\o -> decodeDynamicArg t (unsafeInto o) buf) simpExpr+  where simpExpr = maybeLitWordSimp expr++-- | Decode a static type from a concrete 32-byte word value+decodeStaticArgConcrete :: AbiType -> W256 -> Err AbiValue+decodeStaticArgConcrete t v = case t of+  AbiUIntType n -> Right $ AbiUInt n (fromIntegral v)+  AbiIntType n -> Right $ AbiInt n (fromIntegral v)+  AbiAddressType -> Right $ AbiAddress $ fromIntegral v+  AbiBoolType -> Right $ AbiBool (v > 0)+  AbiBytesType n ->+    let bs = BS.take n (word256Bytes v)+    in Right $ AbiBytes n bs+  AbiFunctionType ->+    let bs = word256Bytes v+        addr = bytesToAddr (BS.take 20 bs)+        sel = FunctionSelector (word32 (BS.unpack (BS.drop 20 (BS.take 24 bs))))+    in Right $ AbiFunction addr sel+  _ -> Left $ "unexpected static type: " ++ show t++-- | Decode a dynamic type from buffer at given offset+decodeDynamicArg :: AbiType -> Int -> Expr Buf -> Err AbiValue+decodeDynamicArg t offset buf = case t of+  AbiBytesDynamicType -> do+    -- Read length at offset+    let lenExpr = simplify $ readWord (Lit $ unsafeInto offset) buf+    case maybeLitWordSimp lenExpr of+      Nothing -> Left "could not get concrete length for bytes"+      Just len ->+        let dataOffset = offset + 32+            byteCount = unsafeInto len :: Int+            bytes = [simplify $ readByte (Lit $ unsafeInto (dataOffset + i)) buf | i <- [0 .. byteCount - 1]]+            concreteBytes = mapMaybe maybeLitByteSimp bytes+        in if length concreteBytes /= byteCount+           then Left $ "could not get all concrete bytes (got " ++ show (length concreteBytes) ++ " of " ++ show byteCount ++ ")"+           else Right $ AbiBytesDynamic (BS.pack concreteBytes)++  -- String is encoded the same as bytes+  AbiStringType -> decodeDynamicArg AbiBytesDynamicType offset buf+  _ -> Left $ "dynamic type not yet supported: " ++ show t+ decodeStaticArgs :: Int -> Int -> Expr Buf -> [Expr EWord] decodeStaticArgs offset numArgs b =   [readWord (Lit . unsafeInto $ i) b | i <- [offset,(offset+32) .. (offset + (numArgs-1)*32)]]@@ -542,8 +648,7 @@    AbiIntType n -> do      x <- genUInt n      pure $ AbiInt n (signedWord (x - 2^(n-1)))-   AbiAddressType ->-     AbiAddress . fromIntegral <$> genUInt 20+   AbiAddressType -> AbiAddress . fromIntegral <$> genUInt 20    AbiBoolType ->      elements [AbiBool False, AbiBool True]    AbiBytesType n ->@@ -561,9 +666,12 @@        replicateM n (scale (`div` 3) (genAbiValue t))    AbiTupleType ts ->      AbiTuple <$> mapM genAbiValue ts-   AbiFunctionType ->-     do xs <- replicateM 24 arbitrary-        pure (AbiFunction (BS.pack xs))+   AbiFunctionType -> do+     addrBytes <- BS.pack <$> replicateM 20 arbitrary+     selBytes <- replicateM 4 arbitrary+     let addr = bytesToAddr addrBytes+         sel = FunctionSelector (word32 selBytes)+     pure (AbiFunction addr sel)   where     genUInt :: Int -> Gen Word256     genUInt n = arbitraryIntegralWithMax (2^n-1) :: Gen Word256@@ -603,9 +711,10 @@     AbiUInt n a -> AbiUInt n <$> (shrinkIntegral a)     AbiInt n a -> AbiInt n <$> (shrinkIntegral a)     AbiBool b -> AbiBool <$> shrink b-    AbiAddress a -> [AbiAddress 0xacab, AbiAddress 0xdeadbeef, AbiAddress 0xbabeface]-      <> (AbiAddress <$> shrinkIntegral a)-    AbiFunction b -> shrink $ AbiBytes 24 b+    AbiAddress a -> [AbiAddress 0xacab, AbiAddress a, AbiAddress 0xbabeface, AbiAddress 0xdeadbeef]+    AbiFunction addr (FunctionSelector sel) ->+      [AbiFunction a (FunctionSelector s) | a <- shrinkIntegral addr, s <- [sel]]+      ++ [AbiFunction a (FunctionSelector s) | a <- [addr], s <- shrinkIntegral sel]  -- A modification of 'arbitrarySizedBoundedIntegral' quickcheck library -- which takes the maxbound explicitly rather than relying on a Bounded instance.
src/EVM/Assembler.hs view
@@ -42,6 +42,7 @@       OpShl -> [LitByte 0x1B]       OpShr -> [LitByte 0x1C]       OpSar -> [LitByte 0x1D]+      OpClz -> [LitByte 0x1E]       OpSha3 -> [LitByte 0x20]       OpAddress -> [LitByte 0x30]       OpBalance -> [LitByte 0x31]
+ src/EVM/ConsoleLog.hs view
@@ -0,0 +1,189 @@+module EVM.ConsoleLog+  ( formatConsoleLog+  ) where++import Data.ByteString (ByteString)+import Data.ByteString qualified as BS+import Data.ByteString.Char8 qualified as Char8+import Data.ByteString.Builder (byteStringHex, toLazyByteString)+import Data.ByteString.Lazy (toStrict)+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Text (Text, pack, intercalate)+import Data.Text qualified as T+import Data.Text.Encoding qualified as T++import EVM.ABI (AbiType(..), AbiValue(..), decodeBuf, AbiVals(..))+import EVM.Types (Expr(..), EType(..), FunctionSelector(..), abiKeccak, word32)++-- | Try to decode and format a console.log calldata buffer.+-- Returns a human-readable representation of the log message.+formatConsoleLog :: Expr Buf -> Text+formatConsoleLog (ConcreteBuf bs) = formatConsoleLogBS bs+formatConsoleLog _ = "<symbolic console.log>"++formatConsoleLogBS :: ByteString -> Text+formatConsoleLogBS bs+  | BS.length bs < 4 = "console::log()"+  | otherwise =+      let selector = FunctionSelector (word32 (BS.unpack (BS.take 4 bs)))+          payload = BS.drop 4 bs+      in case Map.lookup selector consoleLogSigs of+        Just (_, types) ->+          case decodeBuf types (ConcreteBuf payload) of+            (CAbi vals, _) -> "console::log(" <> intercalate ", " (map showAbiVal vals) <> ")"+            _ -> "console::log(" <> hexBS bs <> ")"+        Nothing -> "console::log(" <> hexBS bs <> ")"+  where+    hexBS b = "0x" <> T.decodeUtf8 (toStrict (toLazyByteString (byteStringHex b)))++-- | Format an ABI value for console output+showAbiVal :: AbiValue -> Text+showAbiVal (AbiString s) = T.pack (show (Char8.unpack s))+showAbiVal (AbiAddress addr) = pack (show addr)+showAbiVal (AbiBool b) = if b then "true" else "false"+showAbiVal v = pack (show v)++-- | Map from 4-byte selector to (display name, parameter types) for all+-- console.log overloads.+consoleLogSigs :: Map FunctionSelector (Text, [AbiType])+consoleLogSigs = Map.fromList+  [ sig "log()"                  []+  -- single param+  , sig "log(bool)"              [AbiBoolType]+  , sig "log(address)"           [AbiAddressType]+  , sig "log(uint256)"           [AbiUIntType 256]+  , sig "log(int256)"            [AbiIntType 256]+  , sig "log(string)"            [AbiStringType]+  , sig "log(bytes)"             [AbiBytesDynamicType]+  -- two params+  , sig "log(bool,bool)"                [AbiBoolType, AbiBoolType]+  , sig "log(bool,address)"             [AbiBoolType, AbiAddressType]+  , sig "log(bool,uint256)"             [AbiBoolType, AbiUIntType 256]+  , sig "log(bool,string)"              [AbiBoolType, AbiStringType]+  , sig "log(address,bool)"             [AbiAddressType, AbiBoolType]+  , sig "log(address,address)"          [AbiAddressType, AbiAddressType]+  , sig "log(address,uint256)"          [AbiAddressType, AbiUIntType 256]+  , sig "log(address,string)"           [AbiAddressType, AbiStringType]+  , sig "log(uint256,bool)"             [AbiUIntType 256, AbiBoolType]+  , sig "log(uint256,address)"          [AbiUIntType 256, AbiAddressType]+  , sig "log(uint256,uint256)"          [AbiUIntType 256, AbiUIntType 256]+  , sig "log(uint256,string)"           [AbiUIntType 256, AbiStringType]+  , sig "log(string,bool)"              [AbiStringType, AbiBoolType]+  , sig "log(string,address)"           [AbiStringType, AbiAddressType]+  , sig "log(string,uint256)"           [AbiStringType, AbiUIntType 256]+  , sig "log(string,string)"            [AbiStringType, AbiStringType]+  , sig "log(string,int256)"            [AbiStringType, AbiIntType 256]+  -- three params+  , sig "log(bool,bool,bool)"                   [AbiBoolType, AbiBoolType, AbiBoolType]+  , sig "log(bool,bool,address)"                [AbiBoolType, AbiBoolType, AbiAddressType]+  , sig "log(bool,bool,uint256)"                [AbiBoolType, AbiBoolType, AbiUIntType 256]+  , sig "log(bool,bool,string)"                 [AbiBoolType, AbiBoolType, AbiStringType]+  , sig "log(bool,address,bool)"                [AbiBoolType, AbiAddressType, AbiBoolType]+  , sig "log(bool,address,address)"             [AbiBoolType, AbiAddressType, AbiAddressType]+  , sig "log(bool,address,uint256)"             [AbiBoolType, AbiAddressType, AbiUIntType 256]+  , sig "log(bool,address,string)"              [AbiBoolType, AbiAddressType, AbiStringType]+  , sig "log(bool,uint256,bool)"                [AbiBoolType, AbiUIntType 256, AbiBoolType]+  , sig "log(bool,uint256,address)"             [AbiBoolType, AbiUIntType 256, AbiAddressType]+  , sig "log(bool,uint256,uint256)"             [AbiBoolType, AbiUIntType 256, AbiUIntType 256]+  , sig "log(bool,uint256,string)"              [AbiBoolType, AbiUIntType 256, AbiStringType]+  , sig "log(bool,string,bool)"                 [AbiBoolType, AbiStringType, AbiBoolType]+  , sig "log(bool,string,address)"              [AbiBoolType, AbiStringType, AbiAddressType]+  , sig "log(bool,string,uint256)"              [AbiBoolType, AbiStringType, AbiUIntType 256]+  , sig "log(bool,string,string)"               [AbiBoolType, AbiStringType, AbiStringType]+  , sig "log(address,bool,bool)"                [AbiAddressType, AbiBoolType, AbiBoolType]+  , sig "log(address,bool,address)"             [AbiAddressType, AbiBoolType, AbiAddressType]+  , sig "log(address,bool,uint256)"             [AbiAddressType, AbiBoolType, AbiUIntType 256]+  , sig "log(address,bool,string)"              [AbiAddressType, AbiBoolType, AbiStringType]+  , sig "log(address,address,bool)"             [AbiAddressType, AbiAddressType, AbiBoolType]+  , sig "log(address,address,address)"          [AbiAddressType, AbiAddressType, AbiAddressType]+  , sig "log(address,address,uint256)"          [AbiAddressType, AbiAddressType, AbiUIntType 256]+  , sig "log(address,address,string)"           [AbiAddressType, AbiAddressType, AbiStringType]+  , sig "log(address,uint256,bool)"             [AbiAddressType, AbiUIntType 256, AbiBoolType]+  , sig "log(address,uint256,address)"          [AbiAddressType, AbiUIntType 256, AbiAddressType]+  , sig "log(address,uint256,uint256)"          [AbiAddressType, AbiUIntType 256, AbiUIntType 256]+  , sig "log(address,uint256,string)"           [AbiAddressType, AbiUIntType 256, AbiStringType]+  , sig "log(address,string,bool)"              [AbiAddressType, AbiStringType, AbiBoolType]+  , sig "log(address,string,address)"           [AbiAddressType, AbiStringType, AbiAddressType]+  , sig "log(address,string,uint256)"           [AbiAddressType, AbiStringType, AbiUIntType 256]+  , sig "log(address,string,string)"            [AbiAddressType, AbiStringType, AbiStringType]+  , sig "log(uint256,bool,bool)"                [AbiUIntType 256, AbiBoolType, AbiBoolType]+  , sig "log(uint256,bool,address)"             [AbiUIntType 256, AbiBoolType, AbiAddressType]+  , sig "log(uint256,bool,uint256)"             [AbiUIntType 256, AbiBoolType, AbiUIntType 256]+  , sig "log(uint256,bool,string)"              [AbiUIntType 256, AbiBoolType, AbiStringType]+  , sig "log(uint256,address,bool)"             [AbiUIntType 256, AbiAddressType, AbiBoolType]+  , sig "log(uint256,address,address)"          [AbiUIntType 256, AbiAddressType, AbiAddressType]+  , sig "log(uint256,address,uint256)"          [AbiUIntType 256, AbiAddressType, AbiUIntType 256]+  , sig "log(uint256,address,string)"           [AbiUIntType 256, AbiAddressType, AbiStringType]+  , sig "log(uint256,uint256,bool)"             [AbiUIntType 256, AbiUIntType 256, AbiBoolType]+  , sig "log(uint256,uint256,address)"          [AbiUIntType 256, AbiUIntType 256, AbiAddressType]+  , sig "log(uint256,uint256,uint256)"          [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256]+  , sig "log(uint256,uint256,string)"           [AbiUIntType 256, AbiUIntType 256, AbiStringType]+  , sig "log(uint256,string,bool)"              [AbiUIntType 256, AbiStringType, AbiBoolType]+  , sig "log(uint256,string,address)"           [AbiUIntType 256, AbiStringType, AbiAddressType]+  , sig "log(uint256,string,uint256)"           [AbiUIntType 256, AbiStringType, AbiUIntType 256]+  , sig "log(uint256,string,string)"            [AbiUIntType 256, AbiStringType, AbiStringType]+  , sig "log(string,bool,bool)"                 [AbiStringType, AbiBoolType, AbiBoolType]+  , sig "log(string,bool,address)"              [AbiStringType, AbiBoolType, AbiAddressType]+  , sig "log(string,bool,uint256)"              [AbiStringType, AbiBoolType, AbiUIntType 256]+  , sig "log(string,bool,string)"               [AbiStringType, AbiBoolType, AbiStringType]+  , sig "log(string,address,bool)"              [AbiStringType, AbiAddressType, AbiBoolType]+  , sig "log(string,address,address)"           [AbiStringType, AbiAddressType, AbiAddressType]+  , sig "log(string,address,uint256)"           [AbiStringType, AbiAddressType, AbiUIntType 256]+  , sig "log(string,address,string)"            [AbiStringType, AbiAddressType, AbiStringType]+  , sig "log(string,uint256,bool)"              [AbiStringType, AbiUIntType 256, AbiBoolType]+  , sig "log(string,uint256,address)"           [AbiStringType, AbiUIntType 256, AbiAddressType]+  , sig "log(string,uint256,uint256)"           [AbiStringType, AbiUIntType 256, AbiUIntType 256]+  , sig "log(string,uint256,string)"            [AbiStringType, AbiUIntType 256, AbiStringType]+  , sig "log(string,string,bool)"               [AbiStringType, AbiStringType, AbiBoolType]+  , sig "log(string,string,address)"            [AbiStringType, AbiStringType, AbiAddressType]+  , sig "log(string,string,uint256)"            [AbiStringType, AbiStringType, AbiUIntType 256]+  , sig "log(string,string,string)"             [AbiStringType, AbiStringType, AbiStringType]+  -- four params (most common combinations)+  , sig "log(bool,bool,bool,bool)"                      [AbiBoolType, AbiBoolType, AbiBoolType, AbiBoolType]+  , sig "log(bool,bool,bool,address)"                   [AbiBoolType, AbiBoolType, AbiBoolType, AbiAddressType]+  , sig "log(bool,bool,bool,uint256)"                   [AbiBoolType, AbiBoolType, AbiBoolType, AbiUIntType 256]+  , sig "log(bool,bool,bool,string)"                    [AbiBoolType, AbiBoolType, AbiBoolType, AbiStringType]+  , sig "log(address,address,address,address)"          [AbiAddressType, AbiAddressType, AbiAddressType, AbiAddressType]+  , sig "log(address,address,address,uint256)"          [AbiAddressType, AbiAddressType, AbiAddressType, AbiUIntType 256]+  , sig "log(address,address,address,string)"           [AbiAddressType, AbiAddressType, AbiAddressType, AbiStringType]+  , sig "log(uint256,uint256,uint256,uint256)"          [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256, AbiUIntType 256]+  , sig "log(uint256,uint256,uint256,string)"           [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256, AbiStringType]+  , sig "log(string,string,string,string)"              [AbiStringType, AbiStringType, AbiStringType, AbiStringType]+  , sig "log(string,string,string,uint256)"             [AbiStringType, AbiStringType, AbiStringType, AbiUIntType 256]+  , sig "log(string,string,string,address)"             [AbiStringType, AbiStringType, AbiStringType, AbiAddressType]+  , sig "log(string,string,string,bool)"                [AbiStringType, AbiStringType, AbiStringType, AbiBoolType]+  , sig "log(string,uint256,uint256,uint256)"           [AbiStringType, AbiUIntType 256, AbiUIntType 256, AbiUIntType 256]+  , sig "log(string,address,address,address)"           [AbiStringType, AbiAddressType, AbiAddressType, AbiAddressType]+  , sig "log(string,bool,bool,bool)"                    [AbiStringType, AbiBoolType, AbiBoolType, AbiBoolType]+  , sig "log(string,string,uint256,uint256)"            [AbiStringType, AbiStringType, AbiUIntType 256, AbiUIntType 256]+  , sig "log(string,uint256,string,uint256)"            [AbiStringType, AbiUIntType 256, AbiStringType, AbiUIntType 256]+  , sig "log(string,address,uint256,uint256)"           [AbiStringType, AbiAddressType, AbiUIntType 256, AbiUIntType 256]+  , sig "log(string,uint256,address,uint256)"           [AbiStringType, AbiUIntType 256, AbiAddressType, AbiUIntType 256]+  , sig "log(string,bool,string,string)"                [AbiStringType, AbiBoolType, AbiStringType, AbiStringType]+  , sig "log(string,string,address,address)"            [AbiStringType, AbiStringType, AbiAddressType, AbiAddressType]+  , sig "log(string,address,string,address)"            [AbiStringType, AbiAddressType, AbiStringType, AbiAddressType]+  , sig "log(string,address,address,string)"            [AbiStringType, AbiAddressType, AbiAddressType, AbiStringType]+  , sig "log(string,uint256,uint256,string)"            [AbiStringType, AbiUIntType 256, AbiUIntType 256, AbiStringType]+  , sig "log(string,uint256,string,string)"             [AbiStringType, AbiUIntType 256, AbiStringType, AbiStringType]+  , sig "log(string,string,uint256,string)"             [AbiStringType, AbiStringType, AbiUIntType 256, AbiStringType]+  , sig "log(string,address,uint256,string)"            [AbiStringType, AbiAddressType, AbiUIntType 256, AbiStringType]+  , sig "log(string,uint256,address,string)"            [AbiStringType, AbiUIntType 256, AbiAddressType, AbiStringType]+  , sig "log(string,address,string,uint256)"            [AbiStringType, AbiAddressType, AbiStringType, AbiUIntType 256]+  , sig "log(string,string,address,uint256)"            [AbiStringType, AbiStringType, AbiAddressType, AbiUIntType 256]+  , sig "log(string,address,string,string)"             [AbiStringType, AbiAddressType, AbiStringType, AbiStringType]+  , sig "log(string,bool,address,address)"              [AbiStringType, AbiBoolType, AbiAddressType, AbiAddressType]+  , sig "log(string,bool,uint256,uint256)"              [AbiStringType, AbiBoolType, AbiUIntType 256, AbiUIntType 256]+  , sig "log(string,bool,string,uint256)"               [AbiStringType, AbiBoolType, AbiStringType, AbiUIntType 256]+  , sig "log(string,bool,uint256,string)"               [AbiStringType, AbiBoolType, AbiUIntType 256, AbiStringType]+  , sig "log(string,bool,address,string)"               [AbiStringType, AbiBoolType, AbiAddressType, AbiStringType]+  , sig "log(string,bool,string,address)"               [AbiStringType, AbiBoolType, AbiStringType, AbiAddressType]+  , sig "log(string,bool,uint256,address)"              [AbiStringType, AbiBoolType, AbiUIntType 256, AbiAddressType]+  , sig "log(string,bool,address,uint256)"              [AbiStringType, AbiBoolType, AbiAddressType, AbiUIntType 256]+  , sig "log(string,bool,bool,string)"                  [AbiStringType, AbiBoolType, AbiBoolType, AbiStringType]+  , sig "log(string,bool,bool,address)"                 [AbiStringType, AbiBoolType, AbiBoolType, AbiAddressType]+  , sig "log(string,bool,bool,uint256)"                 [AbiStringType, AbiBoolType, AbiBoolType, AbiUIntType 256]+  ]+  where+    sig :: ByteString -> [AbiType] -> (FunctionSelector, (Text, [AbiType]))+    sig s types = (abiKeccak s, (pack (Char8.unpack s), types))
src/EVM/Dapp.hs view
@@ -13,9 +13,9 @@ import Data.List (find, sort) import Data.Map (Map) import Data.Map qualified as Map-import Data.Maybe (mapMaybe)+import Data.Maybe (mapMaybe, isJust) import Data.Sequence qualified as Seq-import Data.Text (Text, isPrefixOf, pack)+import Data.Text (Text, pack) import Data.Text.Encoding (encodeUtf8) import Data.Vector qualified as V import Optics.Core@@ -47,6 +47,13 @@   , labels :: Map Addr Text   } +data TestMethodInfo = TestMethodInfo+  { contract :: SolcContract+  , methodSignature :: Sig+  }++type TestMethodFilter = TestMethodInfo -> Bool+ dappInfo :: FilePath -> BuildOutput -> DappInfo dappInfo root (BuildOutput (Contracts cs) sources) =   let@@ -80,43 +87,30 @@ emptyDapp :: DappInfo emptyDapp = dappInfo "" mempty --- Dapp unit tests are detected by searching within abi methods--- that begin with "check" or "prove", that are in a contract with--- the "IS_TEST()" abi marker, for a given regular expression.------ The regex is matched on the full test method name, including path--- and contract, i.e. "path/to/file.sol:TestContract.test_name()".+-- Unit tests are detected by searching within abi methods+-- in a contract with the "IS_TEST()" abi marker.  unitTestMarkerAbi :: FunctionSelector unitTestMarkerAbi = abiKeccak (encodeUtf8 "IS_TEST()") -mkSig :: Text -> Method -> Maybe Sig-mkSig prefix method-  | prefix `isPrefixOf` testname = Just (Sig testname argtypes)-  | otherwise = Nothing+mkSig :: Method -> Sig+mkSig method = Sig method.name argtypes   where-    testname = method.name     argtypes = snd <$> method.inputs -findUnitTests :: Text -> Text -> ([SolcContract] -> [(Text, [Sig])])-findUnitTests prefix match =-  concatMap $ \c ->-    case Map.lookup unitTestMarkerAbi c.abiMap of-      Nothing -> []-      Just _  ->-        let testNames = unitTestMethodsFiltered prefix (regexMatches match) c-        in [(c.contractName, testNames) | not (BS.null c.runtimeCode) && not (null testNames)]+findUnitTests :: TestMethodFilter -> SolcContract -> [Sig]+findUnitTests methodFilter c = if isTestContract c then contractMethodsFiltered methodFilter c else []+  where+    isTestContract c' = not (BS.null c'.runtimeCode) && (isJust $ Map.lookup unitTestMarkerAbi c'.abiMap) -unitTestMethodsFiltered :: Text -> (Text -> Bool) -> (SolcContract -> [Sig])-unitTestMethodsFiltered prefix matcher c =-  let testName (Sig n _) = c.contractName <> "." <> n-  in filter (matcher . testName) (unitTestMethods prefix c)+contractMethodsFiltered :: TestMethodFilter -> SolcContract -> [Sig]+contractMethodsFiltered testMethodFilter c = filter (\sig -> testMethodFilter (TestMethodInfo c sig)) $ contractMethods c -unitTestMethods :: Text -> SolcContract -> [Sig]-unitTestMethods prefix =+contractMethods :: SolcContract -> [Sig]+contractMethods =   (.abiMap)   >>> Map.elems-  >>> mapMaybe (mkSig prefix)+  >>> map mkSig  traceSrcMap :: DappInfo -> Trace -> Maybe SrcMap traceSrcMap dapp trace = srcMap dapp trace.contract trace.opIx@@ -166,9 +160,35 @@     Nothing -> Left "<no source map>"     Just sm ->       case srcMapCodePos dapp.sources sm of-        Nothing -> Left "<source not found>"         Just (fileName, lineIx) ->           Right (pack fileName <> ":" <> pack (show lineIx))+        -- srcmap points to a file not in the source cache (e.g. compiler-+        -- generated panic handler).  Walk backwards through the contract's+        -- srcmap to find the nearest entry with a valid source location —+        -- this typically resolves to the assert/require that triggered the+        -- error.+        Nothing ->+          case fallbackSrcPos dapp trace of+            Just (fileName, lineIx) ->+              Right (pack fileName <> ":" <> pack (show lineIx))+            Nothing -> Left "<source not found>"++-- | Walk backwards from the current opIx to find the nearest srcmap entry+-- whose file index exists in the source cache.+fallbackSrcPos :: DappInfo -> Trace -> Maybe (FilePath, Int)+fallbackSrcPos dapp trace = do+  sol <- findSrc trace.contract dapp+  let srcmaps = case trace.contract.code of+        InitCode _ _ -> sol.creationSrcmap+        RuntimeCode _ -> sol.runtimeSrcmap+        UnknownCode _ -> mempty+  go (trace.opIx - 1) srcmaps+  where+    go i srcmaps+      | i < 0     = Nothing+      | otherwise = case Seq.lookup i srcmaps >>= srcMapCodePos dapp.sources of+          Just pos -> Just pos+          Nothing  -> go (i - 1) srcmaps  srcMapCodePos :: SourceCache -> SrcMap -> Maybe (FilePath, Int) srcMapCodePos cache sm =
src/EVM/Effects.hs view
@@ -46,6 +46,9 @@   , verb             :: Int   , simp             :: Bool   , onlyDeployed     :: Bool+  , earlyAbort       :: Bool+  , mergeMaxBudget   :: Int        -- ^ Max instructions for speculative merge exploration+  , maxDynSize       :: Int        -- ^ Max byte length for concretized dynamic types (bytes, string)   }   deriving (Show, Eq) @@ -65,6 +68,9 @@   , verb = 0   , simp = True   , onlyDeployed = False+  , earlyAbort = False+  , mergeMaxBudget = 100+  , maxDynSize = 64   }  -- Write to the console
src/EVM/Exec.hs view
@@ -2,7 +2,6 @@  import EVM hiding (createAddress) import EVM.Concrete (createAddress)-import EVM.FeeSchedule (feeSchedule) import EVM.Types  import Control.Monad.Trans.State.Strict (get, State)@@ -18,33 +17,13 @@  vmForEthrunCreation :: VMOps t => ByteString -> ST RealWorld (VM t) vmForEthrunCreation creationCode =-  (makeVm $ VMOpts+  (makeVm $ defaultVMOpts     { contract = initialContract (InitCode creationCode mempty)-    , otherContracts = []-    , calldata = mempty-    , value = Lit 0-    , baseState = EmptyBase     , address = createAddress ethrunAddress 1     , caller = LitAddr ethrunAddress     , origin = LitAddr ethrunAddress-    , coinbase = LitAddr 0-    , number = Lit 0-    , timestamp = Lit 0     , blockGaslimit = 0-    , gasprice = 0     , prevRandao = 42069-    , gas = toGas 0xffffffffffffffff-    , gaslimit = 0xffffffffffffffff-    , baseFee = 0-    , priorityFee = 0-    , maxCodeSize = 0xffffffff-    , schedule = feeSchedule-    , chainId = 1-    , create = False-    , txAccessList = mempty-    , allowFFI = False-    , freshAddresses = 0-    , beaconRoot = 0     }) <&> set (#env % #contracts % at (LitAddr ethrunAddress))              (Just (initialContract (RuntimeCode (ConcreteRuntimeCode "")))) 
src/EVM/Expr.hs view
@@ -4,17 +4,146 @@    Helper functions for working with Expr instances.    All functions here will return a concrete result if given a concrete input. -}-module EVM.Expr where+module EVM.Expr+  ( -- * Constants+    maxLit+  , maxLitSigned+  , minLitSigned -import Prelude hiding (LT, GT)+    -- * Stack Ops+  , op1+  , op2+  , op3+  , normArgs++    -- * Arithmetic+  , add+  , sub+  , mul+  , div+  , sdiv+  , mod+  , smod+  , addmod+  , mulmod+  , exp+  , sex++    -- * Comparisons / Booleans+  , lt+  , gt+  , leq+  , geq+  , slt+  , sgt+  , eq+  , iszero++    -- * Bit operations+  , and+  , or+  , xor+  , not+  , shl+  , shr+  , sar+  , clz++    -- * Props+  , peq+  , pleq++    -- * Buffers+  , readByte+  , readBytes+  , readWord+  , maxBytes+  , copySlice+  , writeByte+  , writeWord+  , bufLength+  , bufLengthEnv+  , minLength+  , concretePrefix+  , take+  , drop+  , toList+  , fromList+  , simplifyReads++    -- * Storage+  , readStorage'+  , readStorage+  , writeStorage+  , concStoreContains+  , getAddr+  , getLogicalIdx++    -- * Storage patterns / helpers+  , pattern MappingSlot+  , pattern ArraySlotWithOffs+  , pattern ArraySlotWithOffs2+  , pattern ArraySlotZero+  , idsDontMatch+  , slotPos+  , litToArrayPreimage+  , litToKeccak++    -- * Decomposition+  , safeToDecomposeProp+  , safeToDecompose+  , decomposeStorage++    -- * Simplification+  , simplify+  , simplifyProps+  , simplifyProp++    -- * Conversions+  , litAddr+  , litCode+  , exprToAddr+  , wordToAddr++    -- * Other helpers+  , isLitWord+  , isSuccess+  , isFailure+  , isPartial+  , isSymAddr+  , indexWord+  , padByte+  , joinBytes+  , eqByte+  , min+  , max+  , containsNode+  , inRange+  , preImages+  , constPropagate+  , concKeccakSimpExpr+  , concKeccakProps+  , concKeccakSimpProps+  , concKeccakOnePass+  , checkLHSConstProp+  , checkLHSConst+  , maybeLitByteSimp+  , maybeLitWordSimp+  , maybeLitAddrSimp+  , maybeConcStoreSimp+  ) where++import Prelude hiding (LT, GT, exp, drop, take, not, and, or, div, mod, min, max)+import Prelude qualified (div, mod, not, take, min, max) import Control.Monad (unless, when) import Control.Monad.ST (ST) import Control.Monad.State (put, get, execState, State)-import Data.Bits hiding (And, Xor)+import Data.Bits hiding (And, Xor, xor)+import Data.Bits qualified (xor) import Data.ByteString (ByteString) import Data.ByteString qualified as BS import Data.DoubleWord (Int256, Word256(Word256), Word128(Word128))-import Data.List+import Data.List (sort, find) import Data.Map qualified as LMap import Data.Map.Strict qualified as Map import Data.Maybe (mapMaybe, isJust, fromMaybe)@@ -30,8 +159,6 @@ import Witch (unsafeInto, into, tryInto) import Data.Containers.ListUtils (nubOrd) -import Optics.Core- import EVM.Traversals import EVM.Types @@ -202,6 +329,8 @@      else        fromIntegral $ shiftR asSigned (fromIntegral x)) +clz :: Expr EWord -> Expr EWord+clz = op1 CLZ (\x -> if x == 0 then 256 else fromIntegral $ countLeadingZeros x)  -- Props @@ -210,6 +339,7 @@ peq (LitAddr x) (LitAddr y) = PBool (x == y) peq (LitByte x) (LitByte y) = PBool (x == y) peq (ConcreteBuf x) (ConcreteBuf y) = PBool (x == y)+peq (ConcreteStore x) (ConcreteStore y) = PBool (x == y) peq a b   | a == b = PBool True   | otherwise = let args = sort [a, b]@@ -246,15 +376,25 @@   = if x == idx     then val     else readByte i src+readByte i@(Lit x) (WriteByte (And (Lit idx) _) _ src)+  | x > idx = readByte i src readByte i@(Lit x) (WriteWord (Lit idx) val src)   = if x - idx < 32     then case val of            (Lit _) -> indexWord (Lit $ x - idx) val            _ -> IndexWord (Lit $ x - idx) val     else readByte i src--- reading a byte that is lower than the dstOffset of a CopySlice, so it's just reading from dst-readByte i@(Lit x) (CopySlice _ (Lit dstOffset) _ _ dst) | dstOffset > x =-  readByte i dst+-- reading a byte that is before the CopySlice destination region, so just read from dst+-- We must ensure dstOffset + size does not wrap past maxBound.+-- When size is symbolic, we bound dstOffset to ensure no wrapping is possible.+readByte i@(Lit x) (CopySlice _ (Lit dstOffset) _ _ dst)+  | dstOffset > x+  , dstOffset <= (maxBound :: W256) - maxBytes+  = readByte i dst+readByte i@(Lit x) (CopySlice _ (Lit dstOffset) (Lit size) _ dst)+  | dstOffset > x+  , dstOffset + size >= dstOffset+  = readByte i dst readByte i@(Lit x) (CopySlice (Lit srcOffset) (Lit dstOffset) (Lit size) src dst)   = if x - dstOffset < size     then readByte (Lit $ x - (dstOffset - srcOffset)) src@@ -276,12 +416,59 @@ readBytes (Prelude.min 32 -> n) idx buf   = joinBytes [readByte (add idx (Lit . unsafeInto $ i)) buf | i <- [0 .. n - 1]] +-- | Static upper bound on a word expression (unsigned, no wrap), or Nothing.+upperBound :: Expr EWord -> Maybe W256+upperBound e = case (simplifyNoLitToKeccak e) of+  Lit n         -> Just n+  And (Lit m) _ -> Just m+  Mod _ (Lit b)+    | b == 0    -> Just 0+    | otherwise -> Just (b - 1)+  Div a (Lit b)+    | b == 0    -> Just 0+    | otherwise -> upperBound a >>= \ua -> Just (ua `Prelude.div` b)+  Mul (Lit a) b -> mulBound a b+  Add (Lit a) b -> addBound a b+  SHR (Lit n) b+    | n >= 256  -> Just 0+    | otherwise -> upperBound b >>= \ub ->+        Just (ub `shiftR` fromIntegral n)+  _ -> Nothing+  where+    mulBound a b+      | a == 0    = Just 0+      | otherwise = upperBound b >>= \ub ->+          if ub == 0 then Just 0+          else if ub <= (maxBound :: W256) `Prelude.div` a then Just (a * ub)+          else Nothing+    addBound a b = upperBound b >>= \ub ->+      if a <= (maxBound :: W256) - ub then Just (a + ub) else Nothing++-- | True if the WriteWord at @wIdx@ is provably disjoint from the 32-byte+--   read at @i@. Sound mod 2^256: requires both windows to fit below 2^256.+writeDisjointFromReadWord :: W256 -> Expr EWord -> Bool+writeDisjointFromReadWord i = \case+  Add (Lit c) x+    | Just ux <- upperBound x ->+        let cI        = toInteger c+            uxI       = toInteger ux+            iI        = toInteger i+            maxI      = toInteger (maxBound :: W256)+            writeMaxI = cI + uxI + 31+            readMaxI  = iI + 31+        in readMaxI <= maxI+           && writeMaxI <= maxI+           && (cI > readMaxI || writeMaxI < iI)+  _ -> False+ -- | Reads the word starting at idx from the given buf readWord :: Expr EWord -> Expr Buf -> Expr EWord readWord idx buf@(AbstractBuf _) = ReadWord idx buf readWord idx b@(WriteWord idx' val buf)   -- the word we are trying to read exactly matches a WriteWord   | idx == idx' = val+  -- WriteWord provably disjoint via interval bounds (Add (Lit c) X, X bounded)+  | Lit i <- idx, writeDisjointFromReadWord i idx' = readWord idx buf   | otherwise = case (idx, idx') of     (Lit i, Lit i') ->       if i' - i >= 32 && i' - i <= (maxBound :: W256) - 31@@ -294,9 +481,28 @@     _ -> readWordFromBytes idx b readWord i@(Lit idx) (WriteByte (Lit idx') _ buf)   | idx' < idx || (idx' >= idx + 32 && idx <= (maxBound :: W256) - 32) = readWord i buf--- reading a Word that is lower than the dstOffset-32 of a CopySlice, so it's just reading from dst-readWord i@(Lit x) (CopySlice _ (Lit dstOffset) _ _ dst) | dstOffset >= x+32 = readWord i dst-readWord i@(Lit x) (CopySlice _ (Lit dstOffset) (Lit size) _ dst) | x >= dstOffset + size = readWord i dst+-- reading a Word that is before the CopySlice destination region, so just read from dst+-- We must ensure x+32 and dstOffset+size do not wrap past maxBound.+-- When size is symbolic, we bound dstOffset to ensure no wrapping is possible.+readWord i@(Lit x) (CopySlice _ (Lit dstOffset) _ _ dst)+  | dstOffset >= x + 32+  , x + 32 >= x+  , dstOffset <= (maxBound :: W256) - maxBytes+  = readWord i dst+readWord i@(Lit x) (CopySlice _ (Lit dstOffset) (Lit size) _ dst)+  | dstOffset >= x + 32+  , x + 32 >= x+  , dstOffset + size >= dstOffset+  = readWord i dst+-- reading a Word that is past the end of the CopySlice destination region+readWord i@(Lit x) (CopySlice _ (Lit dstOffset) _ _ dst)+  | x >= dstOffset + maxBytes+  , dstOffset + maxBytes >= dstOffset+  = readWord i dst+readWord i@(Lit x) (CopySlice _ (Lit dstOffset) (Lit size) _ dst)+  | x >= dstOffset + size+  , dstOffset + size >= dstOffset+  = readWord i dst readWord (Lit idx) b@(CopySlice (Lit srcOff) (Lit dstOff) (Lit size) src dst)   -- the region we are trying to read is enclosed in the sliced region   | (idx - dstOff) < size && 32 <= size - (idx - dstOff) = readWord (Lit $ srcOff + (idx - dstOff)) src@@ -309,12 +515,24 @@ -- Attempts to read a concrete word from a buffer by reading 32 individual bytes and joining them together -- returns an abstract ReadWord expression if a concrete word cannot be constructed readWordFromBytes :: Expr EWord -> Expr Buf -> Expr EWord-readWordFromBytes (Lit idx) (ConcreteBuf bs) =-  case tryInto idx of-    Left _ -> Lit 0-    Right i -> Lit $ word $ padRight 32 $ BS.take 32 $ BS.drop i bs+readWordFromBytes (Lit idx) (ConcreteBuf bs)+  =  case tryInto idx of+      Right i -> Lit $ word $ padRight 32 $ BS.take 32 $ BS.drop i bs+      Left _ -> if noOverflow then Lit 0 else Lit . word $ wrappedBytes+        where+          noOverflow = idx <= (maxBound :: W256) - 31+          wrappedBytes = zeroes <> bytesFromBeginning+          fromEnd :: Int = fromIntegral ((maxBound :: W256) - idx + 1)+          fromBeginning = 32 - fromEnd+          zeroes = BS.replicate fromEnd 0+          bytesFromBeginning = padRight 32 $ BS.take fromBeginning bs++ readWordFromBytes idx buf@(AbstractBuf _) = ReadWord idx buf-readWordFromBytes i@(Lit idx) buf = let+readWordFromBytes i@(Lit idx) buf+  -- idx+31 must not wrap past maxBound (would make [idx..idx+31] empty)+  | idx + 31 < idx = ReadWord i buf+  | otherwise = let     bytes = [readByte (Lit i') buf | i' <- [idx .. idx + 31]]   in if all isLitByte bytes      then Lit (bytesToW256 . mapMaybe maybeLitByteSimp $ bytes)@@ -355,19 +573,20 @@ -- Fully concrete copy copySlice a@(Lit srcOffset) b@(Lit dstOffset) c@(Lit size) d@(ConcreteBuf src) e@(ConcreteBuf dst)   | dstOffset < maxBytes-  , size < maxBytes =-      let hd = padRight (unsafeInto dstOffset) $ BS.take (unsafeInto dstOffset) dst-          sl = if srcOffset > unsafeInto (BS.length src)-            then BS.replicate (unsafeInto size) 0-            else padRight (unsafeInto size) $ BS.take (unsafeInto size) (BS.drop (unsafeInto srcOffset) src)-          tl = BS.drop (unsafeInto dstOffset + unsafeInto size) dst-      in ConcreteBuf $ hd <> sl <> tl+  , size < maxBytes+  , srcOffset + size >= srcOffset -- srcOffset must not wrap+  = let hd = padRight (unsafeInto dstOffset) $ BS.take (unsafeInto dstOffset) dst+        sl = if srcOffset > unsafeInto (BS.length src)+          then BS.replicate (unsafeInto size) 0+          else padRight (unsafeInto size) $ BS.take (unsafeInto size) (BS.drop (unsafeInto srcOffset) src)+        tl = BS.drop (unsafeInto dstOffset + unsafeInto size) dst+    in ConcreteBuf $ hd <> sl <> tl   | otherwise = CopySlice a b c d e  -- concrete indices & abstract src (may produce a concrete result if we are -- copying from a concrete region of src) copySlice s@(Lit srcOffset) d@(Lit dstOffset) sz@(Lit size) src ds@(ConcreteBuf dst)-  | dstOffset < maxBytes, size < maxBytes, srcOffset + (size-1) > srcOffset = let+  | dstOffset < maxBytes, size < maxBytes, srcOffset + (size-1) >= srcOffset = let     hd = padRight (unsafeInto dstOffset) $ BS.take (unsafeInto dstOffset) dst     sl = [readByte (Lit i) src | i <- [srcOffset .. srcOffset + (size - 1)]]     tl = BS.drop (unsafeInto dstOffset + unsafeInto size) dst@@ -376,6 +595,9 @@        else CopySlice s d sz src ds   | otherwise = CopySlice s d sz src ds +copySlice srcOff dstOff (Lit size) (WriteWord srcOff2 val _) dstBuff+  | size < 32 && srcOff == srcOff2 = copySlice (Lit 0) dstOff (Lit size) (WriteWord (Lit 0) val (ConcreteBuf "")) dstBuff+ -- abstract indices copySlice srcOffset dstOffset size src dst = CopySlice srcOffset dstOffset size src dst @@ -521,11 +743,6 @@           _ -> pure (i, v)  -word256At :: Expr EWord -> Lens (Expr Buf) (Expr Buf) (Expr EWord) (Expr EWord)-word256At i = lens getter setter where-  getter = readWord i-  setter m x = writeWord i x m- -- | Returns the first n bytes of buf take :: W256 -> Expr Buf -> Expr Buf take n = slice (Lit 0) (Lit n)@@ -1051,13 +1268,28 @@     go (IsZero (IsZero (Eq x y))) = eq x y     go (IsZero (IsZero a)) = lt (Lit 0) a     go (IsZero (Xor x y)) = eq x y+    go (IsZero (Sub a b)) = eq a b+    go (IsZero (Or a b)) = EVM.Expr.and (iszero a) (iszero b)     go (IsZero a) = iszero a +    -- ITE (if-then-else) simplification for path merging+    go (ITE (Lit 0) _ f) = f+    go (ITE (Lit _) t _) = t+    go (ITE _ t f) | t == f = t+    go (ITE c (ITE c' a _) d) | c == c' = ITE c a d -- nested same condition in then+    go (ITE c a (ITE c' _ d)) | c == c' = ITE c a d -- nested same condition in else+     -- syntactic Eq reduction     go (Eq (Lit a) (Lit b))       | a == b = Lit 1       | otherwise = Lit 0     go (Eq (Lit 0) (Sub a b)) = eq a b+    go (Eq (Lit 0) (Xor a b)) = eq a b+    go (Eq (Add a b) c)+      | a == c = iszero b+      | b == c = iszero a+    go (Eq (Sub a b) c) | a == c = iszero b+     go (Eq (Lit 0) a) = iszero a     go (Eq a b)       | a == b = Lit 1@@ -1096,6 +1328,7 @@      -- LT     go (EVM.Types.LT _ (Lit 0)) = Lit 0+    go (EVM.Types.LT (Lit a) _) | a == maxLit = Lit 0     go (EVM.Types.LT a (Lit 1)) = iszero a     go (EVM.Types.LT a b) = lt a b @@ -1116,7 +1349,9 @@      -- Mod     go (Mod _ (Lit 0)) = Lit 0+    go (Mod _ (Lit 1)) = Lit 0     go (SMod _ (Lit 0)) = Lit 0+    go (SMod _ (Lit 1)) = Lit 0     go (Mod a b) | a == b = Lit 0     go (SMod a b) | a == b = Lit 0     go (Mod (Lit 0) _) = Lit 0@@ -1201,6 +1436,13 @@       | b == c = a       | otherwise = sub (add a b) c +    -- Order sub/add around+    go (Sub (Sub a b) c) = sub a (add b c)+    go (Sub a (Sub b c)) = add (sub a b) c++    -- Negation: ~x + 1 = 0 - x (two's complement)+    go (Add (Lit 1) (Not b)) = Sub (Lit 0) b+     -- add / sub identities     go (Add a b)       | b == (Lit 0) = a@@ -1212,11 +1454,25 @@       | otherwise = sub a b      -- XOR normalization+    go (Xor a b) | a == b = Lit 0+    go (Xor (Lit 0) a) = a+    go (Xor (Lit a) b) | a == maxLit = EVM.Expr.not b     go (Xor a  b) = EVM.Expr.xor a b +    -- Not simplification+    go (EVM.Types.Not (EVM.Types.Not a)) = a+    go (Not a) = EVM.Expr.not a++    -- EqByte     go (EqByte a b) = eqByte a b      -- SHL / SHR / SAR+    go (SHL (Lit a) _) | a >= 256 = Lit 0+    go (SHR (Lit a) _) | a >= 256 = Lit 0+    go (SHL (Lit n) (SHR (Lit m) x)) -- zero out LSB+      | n == m && n < 256 = EVM.Expr.and (Lit (shiftL maxLit (fromIntegral n))) x+    go (SHR (Lit n) (SHL (Lit m) x)) -- zero out MSB+      | n == m && n < 256 = EVM.Expr.and (Lit (shiftR maxLit (fromIntegral n))) x     go (SHL a v)       | a == (Lit 0) = v       | v == (Lit 0) = v@@ -1230,8 +1486,12 @@        | a == (Lit 0) = v        | v == (Lit 0) = v        | otherwise = sar a v+    go (CLZ v) = clz v      -- Bitwise AND & OR. These MUST preserve bitwise equivalence+    go (And a (Or b _)) | a == b = a+    go (And a (Or _ b)) | a == b = a+     go (And a b)       | a == b = a       | b == (Not a) || a == (Not b) = Lit 0@@ -1239,14 +1499,15 @@       | a == (Lit maxLit) = b       | b == (Lit maxLit) = a       | otherwise = EVM.Expr.and a b+    go (Or a (And b _)) | a == b = a+    go (Or a (And _ b)) | a == b = a     go (Or a b)       | a == b = a       | a == (Lit 0) = b       | b == (Lit 0) = a+      | a == (Lit maxLit) || b == (Lit maxLit) = Lit maxLit       | otherwise = EVM.Expr.or a b -    -- Double NOT is a no-op, since it's a bitwise inversion-    go (EVM.Types.Not (EVM.Types.Not a)) = a      -- Some trivial min / max eliminations     go (Max a b) = EVM.Expr.max a b@@ -1259,6 +1520,7 @@     go (Mul a b) = case (a, b) of                      (Lit 0, _) -> Lit 0                      (Lit 1, _) -> b+                     (Lit v, _) | v == maxLit -> Sub (Lit 0) b                      _ -> mul a b      -- Some trivial (s)div eliminations@@ -1396,10 +1658,17 @@     go (POr x (PBool False)) = x     go (POr (PBool False) x) = x +    -- Absoption rules+    go (PAnd a (POr b _)) | a == b = a+    go (PAnd a (POr _ b)) | a == b = a+    go (POr a (PAnd b _)) | a == b = a+    go (POr a (PAnd _ b)) | a == b = a+     -- Imply     go (PImpl _ (PBool True)) = PBool True     go (PImpl (PBool True) b) = b     go (PImpl (PBool False) _) = PBool True+    go (PImpl a b) | a == b = PBool True       go p = p@@ -1636,9 +1905,6 @@ max (Lit 0) y = y max x (Lit 0) = x max x y = normArgs Max Prelude.max x y--allLit :: [Expr Byte] -> Bool-allLit = all isLitByte  -- | True if the given expression contains any node that satisfies the -- input predicate
src/EVM/FeeSchedule.hs view
@@ -51,6 +51,7 @@   , g_warm_storage_read :: n   , g_access_list_address :: n   , g_access_list_storage_key :: n+  , g_txdatafloor :: n   } deriving Show  feeSchedule :: Num n => FeeSchedule n@@ -105,4 +106,5 @@   , g_warm_storage_read = 100   , g_access_list_address = 2400   , g_access_list_storage_key = 1900+  , g_txdatafloor = 10   }
src/EVM/Fetch.hs view
@@ -47,7 +47,7 @@ import Data.Aeson.Encode.Pretty (encodePretty) import qualified Data.ByteString.Lazy as BSL import Data.Bifunctor (first)-import Control.Exception (try, SomeException)+import Control.Exception (SomeException, SomeAsyncException(..), try, catches, Handler(..), throwIO)  import Data.Aeson hiding (Error) import Data.Aeson.Optics@@ -69,7 +69,11 @@ import Control.Monad (when) import EVM.Effects import qualified EVM.Expr as Expr+import Control.Concurrent (threadDelay) import Control.Concurrent.MVar (MVar, newMVar, readMVar, modifyMVar_)+import Data.IORef (IORef, newIORef, readIORef, atomicModifyIORef')+import Data.Time.Clock (UTCTime, getCurrentTime, diffUTCTime, addUTCTime)+import System.Random (randomRIO)  type Fetcher t m = App m => Query t -> m (EVM t ()) @@ -91,6 +95,9 @@   -- These are NOT persisted to disk   , failedContracts :: MVar (Set Addr)   , failedSlots     :: MVar (Set (Addr, W256))+  -- Shared rate limit cooldown across workers. When any worker hits a+  -- rate limit, it sets a deadline; other workers wait before retrying.+  , rpcThrottle     :: IORef (Maybe UTCTime)   }  data FetchCache = FetchCache@@ -276,6 +283,64 @@       <*> v .: "maxCodeSize"       <*> pure feeSchedule +-- | Wait if there's an active cooldown from a recent rate limit.+waitForCooldown :: IORef (Maybe UTCTime) -> IO ()+waitForCooldown ref = do+  mDeadline <- readIORef ref+  case mDeadline of+    Nothing -> pure ()+    Just deadline -> do+      now <- getCurrentTime+      let diff = diffUTCTime deadline now+      when (diff > 0) $+        threadDelay (ceiling (realToFrac diff * 1_000_000 :: Double))++-- | Set a cooldown deadline. Keeps the longer of existing vs new deadline.+setCooldown :: IORef (Maybe UTCTime) -> Int -> IO ()+setCooldown ref delayUs = do+  now <- getCurrentTime+  let newDeadline = addUTCTime (realToFrac delayUs / 1_000_000) now+  atomicModifyIORef' ref $ \existing ->+    let deadline = case existing of+          Just d | d > newDeadline -> d+          _ -> newDeadline+    in (Just deadline, ())++-- | Retry an IO action on exception with exponential backoff + jitter.+-- Uses a shared cooldown so all workers back off together.+-- Only retries synchronous exceptions (network errors, HTTP 429, etc.).+-- Re-throws all async exceptions (Timeout, ThreadKilled).+-- RPC-level errors (Left) are NOT retried — they pass through.+withRetry :: Bool -> IORef (Maybe UTCTime) -> IO (Either Text a) -> IO (Either Text a)+withRetry debug throttle action = go 1+  where+  maxRetries = 5 :: Int+  go attempt = do+    waitForCooldown throttle+    result <- catches (Right <$> action)+      [ Handler $ \(SomeAsyncException e) -> throwIO e+      , Handler $ \(e :: SomeException) -> pure $ Left e+      ]+    case result of+      Right r -> pure r+      Left e+        | attempt <= maxRetries -> do+            let baseDelayUs = 500_000 * (2 ^ (attempt - 1)) :: Int+            jitter <- randomRIO (0, baseDelayUs)+            let delayUs = baseDelayUs + jitter+            setCooldown throttle delayUs+            when debug $ putStrLn $+              "RPC retry " <> show attempt <> "/" <> show maxRetries+              <> " in " <> show (delayUs `div` 1000) <> "ms"+            threadDelay delayUs+            go (attempt + 1)+        | otherwise ->+            pure $ Left $ pack $ show e++-- | Like fetchWithSession but with retry + shared cooldown from Session.+fetchWithRetry :: Bool -> Text -> Session -> Value -> IO (Either Text Value)+fetchWithRetry debug url session = withRetry debug session.rpcThrottle . fetchWithSession url session.sess+ fetchWithSession :: Text -> NetSession.Session -> Value -> IO (Either Text Value) fetchWithSession url sess x = do   r <- asValue =<< NetSession.post sess (unpack url) x@@ -306,7 +371,7 @@         -- Attempt fetch         when (conf.debug) $ putStrLn $ "-> Fetching contract at " ++ show addr         let fetch :: Show a => RpcQuery a -> IO (Either Text a)-            fetch = fetchQuery n (fetchWithSession url sess.sess)+            fetch = fetchQuery n (fetchWithRetry conf.debug url sess)          codeRes <- fetch (QueryCode addr)         nonceRes <- fetch (QueryNonce addr)@@ -373,7 +438,7 @@       else do         -- Attempt fetch         when (conf.debug) $ putStrLn $ "-> Fetching slot " <> show slot <> " at " <> show addr-        ret <- fetchSlotWithSession sess.sess n url addr slot+        ret <- fetchQuery n (fetchWithRetry conf.debug url sess) (QuerySlot addr slot)         case ret of           Right val -> do             -- Success: cache it@@ -436,7 +501,7 @@ internalBlockFetch :: Config -> Session -> BlockNumber -> Text -> IO (Maybe Block) internalBlockFetch conf sess n url = do   when (conf.debug) $ putStrLn $ "Fetching block " ++ show n ++ " from " ++ unpack url-  ret <- fetchQuery n (fetchWithSession url sess.sess) QueryBlock+  ret <- fetchQuery n (fetchWithRetry conf.debug url sess) QueryBlock   case ret of     Left _ -> pure Nothing     Right b -> do@@ -489,16 +554,17 @@   -- Initialize ephemeral failure tracking   failedContracts <- liftIO $ newMVar Set.empty   failedSlots <- liftIO $ newMVar Set.empty-  pure $ Session sess latestBlockNum cache cacheDir failedContracts failedSlots+  rpcThrottle <- liftIO $ newIORef Nothing+  pure $ Session sess latestBlockNum cache cacheDir failedContracts failedSlots rpcThrottle  mkSessionWithoutCache :: App m => m Session mkSessionWithoutCache = mkSession Nothing Nothing  -- Only used for testing (test.hs, BlockchainTests.hs)-zero :: Natural -> Maybe Natural -> Fetcher t m-zero smtjobs smttimeout q = do+zero :: Natural -> Maybe Natural -> Natural -> Fetcher t m+zero smtjobs smttimeout maxMemoryMB q = do   sess <- mkSessionWithoutCache-  withSolvers Z3 smtjobs 1 smttimeout $ \s ->+  withSolvers Z3 smtjobs smttimeout maxMemoryMB $ \s ->     oracle s (Just sess) noRpc q  noRpcFetcher :: forall t m . App m => SolverGroup -> Fetcher t m@@ -569,7 +635,7 @@             when (conf.debug) $ liftIO $ putStrLn $ "Fetching slot " <> (show slot) <> " at " <> (show addr)             let (block, url) = fromJust rpcInfo.blockNumURL             n <- liftIO $ getLatestBlockNum conf sess block url-            ret <- liftIO $ fetchSlotWithSession sess.sess n url addr slot+            ret <- liftIO $ fetchQuery n (fetchWithRetry conf.debug url sess) (QuerySlot addr slot)             case ret of               Right val -> do                 liftIO $ modifyMVar_ sess.sharedCache $ \c ->
src/EVM/Format.hs view
@@ -5,6 +5,7 @@   , formatSomeExpr   , formatPartial   , formatPartialDetailed+  , formatCaveat   , formatProp   , formatState   , formatError@@ -39,9 +40,10 @@  import EVM (traceForest, traceForest', traceContext, cheatCode) import EVM.ABI (getAbiSeq, parseTypeName, AbiValue(..), AbiType(..), SolError(..), Indexed(..), Event(..))+import EVM.ConsoleLog (formatConsoleLog) import EVM.Dapp (DappContext(..), DappInfo(..), findSrc, showTraceLocation) import EVM.Expr qualified as Expr-import EVM.Solidity (SolcContract(..), Method(..), SrcMap (..), WarningData (..), SourceCache(..))+import EVM.Solidity (SolcContract(..), Method(..)) import EVM.Types import EVM.Expr (maybeLitWordSimp, maybeLitAddrSimp) @@ -69,8 +71,6 @@ import Hexdump (prettyHex) import Numeric (showHex) import Witch (into, unsafeInto, tryFrom)-import Data.Vector.Storable qualified as VS-import Data.Sequence qualified as Seq  data Signedness = Signed | Unsigned   deriving (Show)@@ -330,6 +330,8 @@     ReturnTrace out (CreationContext {}) ->       let l = Expr.bufLength out       in "← " <> formatExpr l <> " bytes of code"+    ConsoleLog buf ->+      "\x1b[36m" <> formatConsoleLog buf <> "\x1b[0m"     EntryTrace t ->       t     FrameTrace (CreationContext { address }) ->@@ -440,6 +442,7 @@   NonceOverflow -> "Nonce overflow"   BadCheatCode reason a -> "Bad cheat code: " <>  reason <> " sig: " <> show a   NonexistentFork a -> "Fork ID does not exist: " <> show a+  AssumeCheatFailed -> "Assume failed"  prettyvmresult :: Expr End -> String prettyvmresult (Failure _ _ (Revert (ConcreteBuf ""))) = "Revert"@@ -523,10 +526,16 @@       , "program counter: " <> pack (show pc)]     ] +-- | Render a program-wide soundness caveat (see 'Caveat').+formatCaveat :: Caveat -> Text+formatCaveat = \case+  DynArgBounded maxSz ->+    "Dynamic (bytes/string) arguments were bounded to " <> pack (show maxSz) <>+    " bytes; counterexamples requiring longer inputs may be missed." -formatPartialDetailed :: Maybe (WarningData t) -> PartialExec -> Text-formatPartialDetailed warnData p =-  let toTxt addr pc = pack $ getSrcInfo warnData addr pc+formatPartialDetailed :: Maybe SrcLookup -> Map.Map (Expr EAddr) Contract -> PartialExec -> Text+formatPartialDetailed srcLookupM contracts p =+  let toTxt addr pc = pack $ runSrcLookup srcLookupM contracts addr pc   in case p of     UnexpectedSymbolicArg {..} -> "Unexpected symbolic arguments to opcode: " <> T.pack opcode <> toTxt addr pc     MaxIterationsReached {..}  -> "Max iterations reached" <> toTxt addr pc@@ -535,20 +544,6 @@     PrecompileMissing {..}     -> "Precompile at address " <> pack (show preAddr) <> " does not exist, called from" <> toTxt addr pc     BranchTooDeep {..}         -> "Branches too deep" <> toTxt addr pc -getSrcInfo :: Maybe (WarningData t) -> Expr EAddr -> Int -> String-getSrcInfo Nothing addr pc = " at addr: " <> show addr <> " at pc: " <> show pc-getSrcInfo (Just dat) addr pc = fromMaybe (getSrcInfo Nothing addr pc) $ do-  contr <- Map.lookup addr dat.vm.env.contracts-  pcOp <- contr.opIxMap VS.!? pc-  sMap <- Seq.lookup pcOp dat.solcContr.runtimeSrcmap-  (fname, fcontent) <- Map.lookup sMap.file dat.sourceCache.files-  let eols = BS.count 10 $ BS.take sMap.offset fcontent  -- 10 is \n-  let str = " in file \"" <> fname <> "\" on line " <> show (eols+1)-  case (BS.length fcontent > (sMap.offset + sMap.length)) of-    False -> pure str-    True  -> let relevant = BS.take sMap.length $ BS.drop sMap.offset fcontent-      in pure $ str <> " : " <> show relevant- formatSomeExpr :: SomeExpr -> Text formatSomeExpr (SomeExpr e) = formatExpr $ Expr.simplify e @@ -654,6 +649,7 @@       Eq a b -> fmt "Eq" [a, b]       EqByte a b -> fmt "EqByte" [a, b]       IsZero a -> fmt "IsZero" [a]+      ITE c t el -> fmt "ITE" [c, t, el]        And a b -> fmt "And" [a, b]       Or a b -> fmt "Or" [a, b]@@ -662,6 +658,7 @@       SHL a b -> fmt "SHL" [a, b]       SHR a b -> fmt "SHR" [a, b]       SAR a b -> fmt "SAR" [a, b]+      CLZ a -> fmt "CLZ" [a]        e@Origin -> T.pack (show e)       e@Coinbase -> T.pack (show e)@@ -837,7 +834,6 @@        -- Hashes       Keccak b -> fmt "Keccak" [b]-      SHA256 b -> fmt "SHA256" [b]       where         fmt nm args = T.unlines           [ "(" <> nm
+ src/EVM/Merge.hs view
@@ -0,0 +1,142 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- | State merging for symbolic execution+--+-- This module provides functions for merging execution paths during symbolic+-- execution. Instead of forking on every JUMPI, we can speculatively execute+-- both paths and merge them using ITE (If-Then-Else) expressions when:+--+-- 1. Both paths converge to the same PC (forward jump pattern)+-- 2. Neither path has side effects (storage, memory, logs unchanged)+-- 3. Both paths have the same stack depth+--+-- This reduces path explosion from 2^N to linear in many common patterns.++module EVM.Merge+  ( tryMergeForwardJump+  ) where++import Control.Monad (when)+import Control.Monad.State.Strict (get, put)+import Debug.Trace (traceM)+import Optics.Core+import Optics.State++import EVM.Effects (Config(..))+import EVM.Expr qualified as Expr+import EVM.Types++-- | Execute instructions speculatively until target PC is reached or+-- we hit budget limit/SMT query/RPC call/revert/error.+speculateLoopOuter+  :: Config+  -> EVM Symbolic ()  -- ^ Single-step executor+  -> Int              -- ^ Target PC+  -> EVM Symbolic (Maybe (VM Symbolic))+speculateLoopOuter conf exec1Step targetPC = do+    -- Initialize merge state for this speculation+    let budget = conf.mergeMaxBudget+    modifying #mergeState $ \ms -> ms { msActive = True , msRemainingBudget = budget }+    res <- speculateLoop conf exec1Step targetPC+    -- Reset merge state+    assign #mergeState defaultMergeState+    pure res++-- | Inner loop for speculative execution with budget tracking+speculateLoop+  :: Config+  -> EVM Symbolic ()  -- ^ Single-step executor+  -> Int              -- ^ Target PC+  -> EVM Symbolic (Maybe (VM Symbolic))+speculateLoop conf exec1Step targetPC = do+    ms <- use #mergeState+    if ms.msRemainingBudget <= 0+      then pure Nothing  -- Budget exhausted+      else do+        pc <- use (#state % #pc)+        result <- use #result+        case result of+          Just _ -> pure Nothing  -- Hit RPC call/revert/SMT query/etc.+          Nothing+            | pc == targetPC -> Just <$> get  -- Reached target+            | otherwise -> do+                -- Decrement budget and execute one instruction+                modifying #mergeState $ \s -> s { msRemainingBudget = subtract 1 s.msRemainingBudget }+                exec1Step+                speculateLoop conf exec1Step targetPC++-- | Try to merge a forward jump (skip block pattern) for Symbolic execution+-- Returns True if merge succeeded, False if we should fall back to forking+-- SOUNDNESS: Both paths (jump and fall-through) must converge to the same PC,+-- have the same stack depth, and have no side effects. Only then can we merge.+tryMergeForwardJump+  :: Config+  -> EVM Symbolic ()  -- ^ Single-step executor+  -> Int              -- ^ Current PC+  -> Int              -- ^ Jump target PC+  -> Expr EWord       -- ^ Branch condition+  -> [Expr EWord]     -- ^ Stack after popping JUMPI args+  -> EVM Symbolic Bool+tryMergeForwardJump conf exec1Step currentPC jumpTarget cond stackAfterPop = do+  -- Only handle forward jumps (skip block pattern)+  if jumpTarget <= currentPC+  then pure False  -- Not a forward jump+  else do+    vm0 <- get++    -- Skip merge if memory is mutable (ConcreteMemory)+    case vm0.state.memory of+      ConcreteMemory _ -> pure False+      SymbolicMemory _ -> do+        -- True branch (jump taken): Just sets PC to target, no execution needed+        let trueStack = stackAfterPop  -- Stack after popping JUMPI args++        -- False branch (fall through): Execute until we reach jump target+        assign' (#state % #stack) stackAfterPop+        modifying' (#state % #pc) (+ 1)  -- Move past JUMPI+        maybeVmFalse <- speculateLoopOuter conf exec1Step jumpTarget++        case maybeVmFalse of+          Nothing -> put vm0 >> pure False -- can't merge: EVM error, SMT/RPC query, over-budget+          Just vmFalse -> do+            let falseStack = vmFalse.state.stack+                soundnessOK = checkNoSideEffects vm0 vmFalse++            -- Check merge conditions: same stack depth AND no side effects+            if length trueStack == length falseStack && soundnessOK+              then do+                -- Merge stacks using ITE expressions, simplifying to prevent growth+                let condSimp = Expr.simplify cond+                    mergeExpr t f = Expr.simplify $ ITE condSimp t f+                    mergedStack = zipWith mergeExpr trueStack falseStack+                -- Use vm0 as base and update only PC and stack+                when conf.debug $ traceM $ "Merged forward jump"+                  <> runSrcLookup vm0.srcLookup vm0.env.contracts vm0.state.contract jumpTarget+                put vm0+                assign (#state % #pc) jumpTarget+                assign (#state % #stack) mergedStack+                assign #result Nothing+                assign (#mergeState % #msActive) False+                pure True+              else put vm0 >> pure False -- can't merge: stack depth or state differs++-- | Check that execution had no side effects (storage, memory, logs, etc.)+checkNoSideEffects :: VM Symbolic -> VM Symbolic -> Bool+checkNoSideEffects vm0 vmAfter =+  let memoryUnchanged = case (vm0.state.memory, vmAfter.state.memory) of+        (SymbolicMemory m1, SymbolicMemory m2) -> m1 == m2+        _ -> False+      memorySizeUnchanged = vm0.state.memorySize == vmAfter.state.memorySize+      returndataUnchanged = vm0.state.returndata == vmAfter.state.returndata+      storageUnchanged = vm0.env.contracts == vmAfter.env.contracts+      logsUnchanged = vm0.logs == vmAfter.logs+      constraintsUnchanged = vm0.constraints == vmAfter.constraints+      keccakUnchanged = vm0.keccakPreImgs == vmAfter.keccakPreImgs+      freshVarUnchanged = vm0.freshVar == vmAfter.freshVar+      framesUnchanged = length vm0.frames == length vmAfter.frames+      subStateUnchanged = vm0.tx.subState == vmAfter.tx.subState+  in memoryUnchanged && memorySizeUnchanged && returndataUnchanged+     && storageUnchanged && logsUnchanged && constraintsUnchanged+     && keccakUnchanged && freshVarUnchanged+     && framesUnchanged && subStateUnchanged
src/EVM/Op.hs view
@@ -45,6 +45,7 @@     0x1b -> "SHL"     0x1c -> "SHR"     0x1d -> "SAR"+    0x1e -> "CLZ"     0x20 -> "SHA3"     0x30 -> "ADDRESS"     --@@ -206,6 +207,7 @@   OpShl -> "SHL"   OpShr -> "SHR"   OpSar -> "SAR"+  OpClz -> "CLZ"   OpSha3 -> "SHA3"   OpAddress -> "ADDRESS"   OpBalance -> "BALANCE"@@ -273,6 +275,7 @@ readOp x xs =   (\n -> Expr.readBytes (into n) (Lit 0) (Expr.fromList $ V.fromList xs)) <$> getOp x +{-# INLINE getOp #-} getOp :: Word8 -> GenericOp Word8 getOp x | x >= 0x80 && x <= 0x8f = OpDup (x - 0x80 + 1) getOp x | x >= 0x90 && x <= 0x9f = OpSwap (x - 0x90 + 1)@@ -305,6 +308,7 @@   0x1b -> OpShl   0x1c -> OpShr   0x1d -> OpSar+  0x1e -> OpClz   0x20 -> OpSha3   0x30 -> OpAddress   0x31 -> OpBalance
src/EVM/SMT.hs view
@@ -31,6 +31,7 @@ import Prelude hiding (LT, GT, Foldable(..))  import Control.Monad+import Control.Monad.Trans.Maybe (MaybeT(..)) import Data.Containers.ListUtils (nubOrd, nubInt) import Data.ByteString (ByteString) import Data.ByteString qualified as BS@@ -475,6 +476,11 @@   IsZero a -> do     cond <- op2 "=" a (Lit 0)     pure $ "(ite " <> cond `sp` one `sp` zero <> ")"+  ITE c t f -> do+    condEnc <- exprToSMT c+    thenEnc <- exprToSMT t+    elseEnc <- exprToSMT f+    pure $ "(ite (distinct " <> condEnc `sp` zero <> ") " <> thenEnc `sp` elseEnc <> ")"   And a b -> op2 "bvand" a b   Or a b -> op2 "bvor" a b   Xor a b -> op2 "bvxor" a b@@ -482,6 +488,7 @@   SHL a b -> op2 "bvshl" b a   SHR a b -> op2 "bvlshr" b a   SAR a b -> op2 "bvashr" b a+  CLZ a -> op1 "clz256" a   SEx a b -> op2 "signext" a b   Div a b -> op2CheckZero "bvudiv" a b   SDiv a b -> op2CheckZero "bvsdiv" a b@@ -511,10 +518,6 @@     enc <- exprToSMT a     sz  <- exprToSMT $ Expr.bufLength a     pure $ "(keccak " <> enc <> " " <> sz <> ")"-  SHA256 a -> do-    enc <- exprToSMT a-    sz  <- exprToSMT $ Expr.bufLength a-    pure $ "(sha256 " <> enc <> " " <> sz <> ")"    TxValue -> pure $ fromString "txvalue"   Balance a -> pure $ fromString "balance_" <> formatEAddr a@@ -742,9 +745,6 @@ parseSC (SCBinary a) = readOrError Numeric.readBin a parseSC sc = internalError $ "cannot parse: " <> show sc -parseErr :: (Show a) => a -> b-parseErr res = internalError $ "cannot parse solver response: " <> show res- parseVar :: TS.Text -> Expr EWord parseVar = Var @@ -772,67 +772,59 @@   | Just a <- TS.stripPrefix "codehash_" name = CodeHash (parseEAddr a)   | otherwise = internalError $ "cannot parse " <> (TS.unpack name) <> " into an Expr" -getAddrs :: (TS.Text -> Expr EAddr) -> (Text -> IO Text) -> [TS.Text] -> IO (Map (Expr EAddr) Addr)-getAddrs parseName getVal names = Map.mapKeys parseName <$> foldM (getOne parseAddr getVal) mempty names+type ValGetter = Text -> MaybeIO Text -getVars :: (TS.Text -> Expr EWord) -> (Text -> IO Text) -> [TS.Text] -> IO (Map (Expr EWord) W256)-getVars parseName getVal names = Map.mapKeys parseName <$> foldM (getOne parseW256 getVal) mempty names+getAddrs :: (TS.Text -> Expr EAddr) -> ValGetter -> [TS.Text] -> MaybeIO (Map (Expr EAddr) Addr)+getAddrs parseName getVal names = do+  m <- foldM (getOne parseAddr getVal) mempty names+  pure $ Map.mapKeys parseName m -getOne :: (SpecConstant -> a) -> (Text -> IO Text) -> Map TS.Text a -> TS.Text -> IO (Map TS.Text a)+getVars :: (TS.Text -> Expr EWord) -> ValGetter -> [TS.Text] -> MaybeIO (Map (Expr EWord) W256)+getVars parseName getVal names = do+  m <- foldM (getOne parseW256 getVal) mempty names+  pure $ Map.mapKeys parseName m++getOne :: (SpecConstant -> a) -> ValGetter -> Map TS.Text a -> TS.Text -> MaybeIO (Map TS.Text a) getOne parseVal getVal acc name = do   raw <- getVal (T.fromStrict name)-  let-    parsed = case parseCommentFreeFileMsg getValueRes (T.toStrict raw) of-      Right (ResSpecific (valParsed :| [])) -> valParsed-      r -> parseErr r-    val = case parsed of-      (TermQualIdentifier (-        Unqualified (IdSymbol symbol)),-        TermSpecConstant sc)-          -> if symbol == name-             then parseVal sc-             else internalError "solver did not return model for requested value"-      r -> parseErr r+  val <- hoistMaybe $ do+    Right (ResSpecific (valParsed :| [])) <- pure $ parseCommentFreeFileMsg getValueRes (T.toStrict raw)+    (TermQualIdentifier (Unqualified (IdSymbol symbol)), TermSpecConstant sc) <- pure valParsed+    guard (symbol == name)+    pure $ parseVal sc   pure $ Map.insert name val acc  -- | Queries the solver for models for each of the expressions representing the -- max read index for a given buffer-queryMaxReads :: (Text -> IO Text) -> Map Text (Expr EWord)  -> IO (Map Text W256)+queryMaxReads :: ValGetter -> Map Text (Expr EWord) -> MaybeIO (Map Text W256) queryMaxReads getVal maxReads = mapM (queryValue getVal) maxReads  -- | Gets the initial model for each buffer, these will often be much too large for our purposes-getBufs :: (Text -> IO Text) -> [Text] -> IO (Map (Expr Buf) BufModel)+getBufs :: ValGetter -> [Text] -> MaybeIO (Map (Expr Buf) BufModel) getBufs getVal bufs = foldM getBuf mempty bufs   where-    getLength :: Text -> IO W256+    getLength :: Text -> MaybeIO W256     getLength name = do       val <- getVal (name <> "_length ")-      pure $ case parseCommentFreeFileMsg getValueRes (T.toStrict val) of-        Right (ResSpecific (parsed :| [])) -> case parsed of-          (TermQualIdentifier (Unqualified (IdSymbol symbol)), (TermSpecConstant sc))-            -> if symbol == (T.toStrict $ name <> "_length")-               then parseW256 sc-               else internalError "solver did not return model for requested value"-          res -> parseErr res-        res -> parseErr res+      hoistMaybe $ do+        Right (ResSpecific (parsed :| [])) <- pure $ parseCommentFreeFileMsg getValueRes (T.toStrict val)+        (TermQualIdentifier (Unqualified (IdSymbol symbol)), TermSpecConstant sc) <- pure parsed+        guard (symbol == T.toStrict (name <> "_length"))+        pure $ parseW256 sc -    getBuf :: Map (Expr Buf) BufModel -> Text -> IO (Map (Expr Buf) BufModel)+    getBuf :: Map (Expr Buf) BufModel -> Text -> MaybeIO (Map (Expr Buf) BufModel)     getBuf acc name = do       len <- getLength name       val <- getVal name--      buf <- case parseCommentFreeFileMsg getValueRes (T.toStrict val) of-        Right (ResSpecific (valParsed :| [])) -> case valParsed of-          (TermQualIdentifier (Unqualified (IdSymbol symbol)), term)-            -> if (T.fromStrict symbol) == name-               then pure $ parseBuf len term-               else internalError "solver did not return model for requested value"-          res -> internalError $ "cannot parse solver response: " <> show res-        res -> internalError $ "cannot parse solver response: " <> show res+      buf <- hoistMaybe $ do+        Right (ResSpecific (valParsed :| [])) <- pure $ parseCommentFreeFileMsg getValueRes (T.toStrict val)+        (TermQualIdentifier (Unqualified (IdSymbol symbol)), term) <- pure valParsed+        guard (T.fromStrict symbol == name)+        parseBuf len term       pure $ Map.insert (AbstractBuf $ T.toStrict name) buf acc -    parseBuf :: W256 -> Term -> BufModel-    parseBuf len = Comp . go mempty+    parseBuf :: W256 -> Term -> Maybe BufModel+    parseBuf len term = Comp <$> go mempty term       where         go env = \case           -- constant arrays@@ -843,71 +835,65 @@                 :| [SortSymbol (IdIndexed "BitVec" (IxNumeral "8" :| []))]               )             )) ((TermSpecConstant val :| [])))-            -> Base (parseW8 val) len+            -> Just $ Base (parseW8 val) len            -- writing a byte over some array           (TermApplication             (Unqualified (IdSymbol "store"))             (base :| [TermSpecConstant idx, TermSpecConstant val])-            ) -> let-              pbase = go env base-              pidx = parseW256 idx-              pval = parseW8 val-            in Write pval pidx pbase+            ) -> do+              pbase <- go env base+              let pidx = parseW256 idx+                  pval = parseW8 val+              Just $ Write pval pidx pbase            -- binding a new name-          (TermLet ((VarBinding name bound) :| []) term) -> let-              pbound = go env bound-            in go (Map.insert name pbound env) term+          (TermLet ((VarBinding name' bound) :| []) term') -> do+              pbound <- go env bound+              go (Map.insert name' pbound env) term'            -- looking up a bound name-          (TermQualIdentifier (Unqualified (IdSymbol name))) -> case Map.lookup name env of-            Just t -> t-            Nothing -> internalError $ "could not find "-                            <> (TS.unpack name)-                            <> " in environment mapping"-          p -> parseErr p+          (TermQualIdentifier (Unqualified (IdSymbol name'))) ->+            Map.lookup name' env +          _ -> Nothing+ -- | Takes a Map containing all reads from a store with an abstract base, as -- well as the concrete part of the storage prestate and returns a fully -- concretized storage getStore-  :: (Text -> IO Text)+  :: ValGetter   -> StorageReads-  -> IO (Map (Expr EAddr) (Map W256 W256))-getStore getVal (StorageReads innerMap) =-  fmap Map.fromList $ forM (Map.toList innerMap) $ \((addr, idx), slots) -> do+  -> MaybeIO (Map (Expr EAddr) (Map W256 W256))+getStore getVal (StorageReads innerMap) = do+  results <- forM (Map.toList innerMap) $ \((addr, idx), slots) -> do     let name = toLazyText (storeName addr idx)     raw <- getVal name-    let parsed = case parseCommentFreeFileMsg getValueRes (T.toStrict raw) of-                   Right (ResSpecific (valParsed :| [])) -> valParsed-                   r -> parseErr r-        -- first interpret SMT term as a function-        fun = case parsed of-                (TermQualIdentifier (Unqualified (IdSymbol symbol)), term) ->-                  if symbol == (T.toStrict name)-                  then interpret1DArray Map.empty term-                  else internalError "solver did not return model for requested value"-                r -> parseErr r--    -- then create a map by adding only the locations that are read by the program+    fun <- hoistMaybe $ do+      Right (ResSpecific (valParsed :| [])) <- pure $ parseCommentFreeFileMsg getValueRes (T.toStrict raw)+      (TermQualIdentifier (Unqualified (IdSymbol symbol)), term) <- pure valParsed+      guard (symbol == T.toStrict name)+      pure $ interpret1DArray Map.empty term+    -- create a map by adding only the locations that are read by the program     store <- foldM (\m slot -> do       slot' <- queryValue getVal slot-      pure $ Map.insert slot' (fun slot') m) Map.empty slots+      pure $ Map.insert slot' (fun slot') m) Map.empty (Set.toList slots)     pure (addr, store)+  pure $ Map.fromList results  -- | Ask the solver to give us the concrete value of an arbitrary abstract word-queryValue :: (Text -> IO Text) -> Expr EWord -> IO W256+queryValue :: ValGetter -> Expr EWord -> MaybeIO W256 queryValue _ (Lit w) = pure w queryValue getVal w = do   -- this exprToSMT should never fail, since we have already ran the solver   let expr = toLazyText $ fromRight' $ exprToSMT w   raw <- getVal expr-  let valTxt = fromMaybe (internalError $ "failed to parse value from get-val response: " <> show raw) $ extractValue raw-  case parseString specConstant (T.toStrict valTxt) of-    Right sc -> pure $ parseW256 sc-    r -> parseErr r+  hoistMaybe $ do+    valTxt <- extractValue raw+    Right sc <- pure $ parseString specConstant (T.toStrict valTxt)+    pure $ parseW256 sc   where+    extractValue :: Text -> Maybe Text     extractValue getValResponse = (T.stripSuffix "))") $ snd $ T.breakOnEnd " " $ T.stripEnd getValResponse  -- | Interpret an N-dimensional array as a value of type a.@@ -951,3 +937,7 @@         Just t -> interpretW256 env t         Nothing -> internalError "unknown identifier, cannot parse array"     interpretW256 _ t = internalError $ "cannot parse array value. Unexpected term: " <> (show t)+++hoistMaybe :: Monad m => Maybe a -> MaybeT m a+hoistMaybe = MaybeT . pure
src/EVM/SMT/SMTLIB.hs view
@@ -27,10 +27,7 @@     "(define-sort Addr () (_ BitVec 160))",     "(define-sort Buf () (Array Word Byte))"     ]) <> [(SMTComment "slot -> value"), SMTCommand "(define-sort Storage () (Array Word Word))"]-    <> (fmap SMTCommand [-        "(declare-fun keccak (Buf Word) Word)",-        "(declare-fun sha256 (Buf Word) Word)"-    ])+    <> (fmap SMTCommand [ "(declare-fun keccak (Buf Word) Word)" ])   macros = fmap SMTCommand [     "(define-fun max ((a (_ BitVec 256)) (b (_ BitVec 256))) (_ BitVec 256) (ite (bvult a b) b a))",     "(define-fun indexWord31 ((w Word)) Byte ((_ extract 7 0) w))",@@ -203,8 +200,44 @@       \(ite (= b (_ bv28 256)) ((_ sign_extend 24 ) ((_ extract 231  0) val))\       \(ite (= b (_ bv29 256)) ((_ sign_extend 16 ) ((_ extract 239  0) val))\       \(ite (= b (_ bv30 256)) ((_ sign_extend 8  ) ((_ extract 247  0) val)) val)\-      \)))))))))))))))))))))))))))))))"-    ]+      \)))))))))))))))))))))))))))))))",+    "(define-fun clz256 ((x (_ BitVec 256))) (_ BitVec 256)\+      \(let ((x128 (ite (bvuge x     #x0000000000000000000000000000000100000000000000000000000000000000)\+      \                (bvlshr x        (_ bv128 256)) x))\+      \      (n128 (ite (bvuge x     #x0000000000000000000000000000000100000000000000000000000000000000)\+      \                (_ bv0 256) (_ bv128 256))))\+      \(let ((x64  (ite (bvuge x128  #x0000000000000000000000000000000000000000000000010000000000000000)\+      \                (bvlshr x128     (_ bv64 256)) x128))\+      \      (n64  (ite (bvuge x128  #x0000000000000000000000000000000000000000000000010000000000000000)\+      \               n128  (bvadd n128 (_ bv64 256)))))\+      \(let ((x32  (ite (bvuge x64   #x0000000000000000000000000000000000000000000000000000000100000000)\+      \                (bvlshr x64      (_ bv32 256)) x64))\+      \      (n32  (ite (bvuge x64   #x0000000000000000000000000000000000000000000000000000000100000000)\+      \               n64   (bvadd n64  (_ bv32 256)))))\+      \(let ((x16  (ite (bvuge x32   #x0000000000000000000000000000000000000000000000000000000000010000)\+      \                (bvlshr x32      (_ bv16 256)) x32))\+      \      (n16  (ite (bvuge x32   #x0000000000000000000000000000000000000000000000000000000000010000)\+      \               n32   (bvadd n32  (_ bv16 256)))))\+      \(let ((x8   (ite (bvuge x16   #x0000000000000000000000000000000000000000000000000000000000000100)\+      \                (bvlshr x16      (_ bv8 256)) x16))\+      \      (n8   (ite (bvuge x16   #x0000000000000000000000000000000000000000000000000000000000000100)\+      \               n16   (bvadd n16  (_ bv8 256)))))\+      \(let ((x4   (ite (bvuge x8    #x0000000000000000000000000000000000000000000000000000000000000010)\+      \                (bvlshr x8       (_ bv4 256)) x8))\+      \      (n4   (ite (bvuge x8    #x0000000000000000000000000000000000000000000000000000000000000010)\+      \               n8    (bvadd n8   (_ bv4 256)))))\+      \(let ((x2   (ite (bvuge x4    #x0000000000000000000000000000000000000000000000000000000000000004)\+      \                (bvlshr x4       (_ bv2 256)) x4))\+      \      (n2   (ite (bvuge x4    #x0000000000000000000000000000000000000000000000000000000000000004)\+      \               n4    (bvadd n4   (_ bv2 256)))))\+      \(let ((x1   (ite (bvuge x2    #x0000000000000000000000000000000000000000000000000000000000000002)\+      \                (bvlshr x2       (_ bv1 256)) x2))\+      \      (n1   (ite (bvuge x2    #x0000000000000000000000000000000000000000000000000000000000000002)\+      \               n2    (bvadd n2   (_ bv1 256)))))\+      \(ite           (bvuge x1 (_ bv1 256))\+      \               n1    (bvadd n1   (_ bv1 256)))\+      \)))))))))"+   ]   toText :: SMTEntry -> Text
src/EVM/SMT/Types.hs view
@@ -1,12 +1,15 @@ module EVM.SMT.Types where -import Data.Text.Lazy (Text)-import Data.Text.Lazy.Builder+import Control.Monad.Trans.Maybe (MaybeT) import Data.Map (Map) import Data.Map qualified as Map (unionWith) import Data.Set (Set)+import Data.Text.Lazy (Text)+import Data.Text.Lazy.Builder  import EVM.Types++type MaybeIO = MaybeT IO  data SMTEntry = SMTCommand Builder | SMTComment Builder   deriving (Eq)
src/EVM/Sign.hs view
@@ -48,7 +48,7 @@     r = unsafeInto $ sign_r sig     s = unsafeInto lowS -    -- this is a little bit sad, but cryptonite doesn't give us back a v value+    -- this is a little bit sad, but crypton doesn't give us back a v value     -- so we compute it by guessing one, and then seeing if that gives us the right answer from ecrecover     v = if ecrec 28 r s hash == deriveAddr sk         then 28
src/EVM/Solidity.hs view
@@ -40,7 +40,6 @@   , containsLinkerHole   , makeSourceCache   , getContractsMap-  , WarningData(..) ) where  import EVM.ABI@@ -77,7 +76,6 @@ import Data.Text (pack, intercalate) import Data.Text qualified as T import Data.Text.Encoding (encodeUtf8, decodeUtf8)-import Data.Text.IO (writeFile) import Data.Vector (Vector) import Data.Vector qualified as Vector import Data.Word (Word8)@@ -232,12 +230,6 @@   modifierDepth :: {-# UNPACK #-} !Int } deriving (Show, Eq, Ord, Generic) -data WarningData t = WarningData-  {solcContr :: SolcContract,-   sourceCache :: SourceCache,-   vm :: VM t-  }- data SrcMapParseState   = F1 String Int   | F2 Int String Int@@ -250,6 +242,9 @@ data CodeType = Creation | Runtime   deriving (Show, Eq, Ord) +type ContractName = Text+type SourceCode = Text+ -- Obscure but efficient parser for the Solidity sourcemap format. makeSrcMaps :: Text -> Maybe (Seq SrcMap) makeSrcMaps = (\case (_, Fe, _) -> Nothing; x -> Just (done x))@@ -391,31 +386,24 @@       bytecode = c ^?! key "evm" ^?! key (if deployed then "deployedBytecode" else "bytecode") ^?! key "object" % _String   pure $ toCode contractName bytecode -solidity-  :: (MonadUnliftIO m)-  => Text -> Text -> m (Maybe ByteString)-solidity contract src = liftIO $ do-  json <- solc Solidity src False-  case readStdJSON json of-      Right (Contracts sol) -> pure $ Map.lookup ("hevm.sol:" <> contract) sol <&> (.creationCode)-      Left e -> internalError $ "unable to parse solidity output:\n" <> (T.unpack json) <> "\n" <> show e+solidity :: MonadIO m => ContractName -> SourceCode -> m (Maybe ByteString)+solidity contract src = solcCode contract src Creation False -solcRuntime'-  :: App m-  => Text -> Text -> Bool -> m (Maybe ByteString)-solcRuntime' contract src viaIR = do-  conf <- readConfig-  liftIO $ do-    json <- solc Solidity src viaIR-    when conf.dumpExprs $ liftIO $ Data.Text.IO.writeFile "compiled_code.json" json-    case readStdJSON json of-      Right (Contracts sol) -> pure $ Map.lookup ("hevm.sol:" <> contract) sol <&> (.runtimeCode)-      Left _ -> internalError $ "unable to parse solidity output:\n" <> (T.unpack json)+solcRuntime' :: MonadIO m => ContractName -> SourceCode -> Bool -> m (Maybe ByteString)+solcRuntime' contract src viaIR = solcCode contract src Runtime viaIR -solcRuntime-  :: App m-  => Text -> Text -> m (Maybe ByteString)+solcRuntime :: MonadIO m => ContractName -> SourceCode -> m (Maybe ByteString) solcRuntime contract src = solcRuntime' contract src False++solcCode :: MonadIO m => ContractName -> SourceCode -> CodeType -> Bool -> m (Maybe ByteString)+solcCode contract src codeType viaIR = liftIO $ do+  json <- solc Solidity src viaIR+  case readStdJSON json of+    Right (Contracts sol) -> pure $ Map.lookup ("hevm.sol:" <> contract) sol+      <&> case codeType of+            Creation -> (.creationCode)+            Runtime -> (.runtimeCode)+    Left e -> internalError $ "unable to parse solidity output:\n" <> (T.unpack json) <> "\n" <> show e  functionAbi :: Text -> IO Method functionAbi f = do
src/EVM/Solvers.hs view
@@ -2,16 +2,32 @@     Module: EVM.Solvers     Description: Solver orchestration -}-module EVM.Solvers where+{-# LANGUAGE CPP #-}+module EVM.Solvers+  ( withSolvers+  , Solver(..)+  , SolverGroup(..)+  , Task+  , MultiSol(..)+  , checkSat+  , checkSatWithProps+  , checkMulti+  , defMemLimit+  ) where ++ import Prelude hiding (LT, GT)  import GHC.Natural import GHC.IO.Handle (Handle, hFlush, hSetBuffering, BufferMode(..)) import Control.Concurrent.Chan (Chan, newChan, writeChan, readChan) import Control.Concurrent (forkIO, killThread)+import Control.Concurrent.QSem (QSem, newQSem, waitQSem, signalQSem)+import Control.Exception (bracket, bracket_, try, IOException) import Control.Concurrent.STM (writeTChan, newTChan, TChan, tryReadTChan, atomically) import Control.Monad+import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT) import Control.Monad.State.Strict import Control.Monad.IO.Unlift import Data.Map (Map)@@ -19,6 +35,7 @@ import Data.Set (Set, isSubsetOf, fromList, toList) import Data.Maybe (fromMaybe, isJust, fromJust) import Data.Either (isLeft)+import Data.Foldable (for_) import Data.Text qualified as TStrict import Data.Text.Lazy (Text) import Data.Text.Lazy qualified as T@@ -36,6 +53,11 @@ import EVM.SMT import EVM.Types ++-- In megabytes, i.e. 1GB+defMemLimit :: Natural+defMemLimit = 1024+ -- | Supported solvers data Solver   = Z3@@ -54,11 +76,10 @@  -- | A running solver instance data SolverInstance = SolverInstance-  { solvertype :: Solver-  , stdin      :: Handle-  , stdout     :: Handle-  , process    :: ProcessHandle-  }+  Solver -- solver type+  Handle -- stdin+  Handle -- stdout+  ProcessHandle  -- | A channel representing a group of solvers newtype SolverGroup = SolverGroup (Chan Task)@@ -76,16 +97,14 @@   deriving (Show, Eq)  data MultiData = MultiData-  { smt2 :: SMT2-  , multiSol :: MultiSol-  , resultChan :: Chan (Maybe [W256])-  }+  SMT2+  MultiSol+  (Chan (Maybe [W256])) -- result channel  data SingleData = SingleData-  { smt2 :: SMT2-  , props :: Maybe [Prop]-  , resultChan :: Chan SMTResult-  }+  SMT2+  (Maybe [Prop])+  (Chan SMTResult) -- result channel  -- returns True if a is a superset of any of the sets in bs supersetAny :: Set Prop -> [Set Prop] -> Bool@@ -131,150 +150,160 @@         fullPath = path </> "query-" <> postfix <> ".smt2"     T.writeFile fullPath content -withSolvers :: App m => Solver -> Natural -> Natural -> Maybe Natural -> (SolverGroup -> m a) -> m a-withSolvers solver count threads timeout cont = do-    -- spawn solvers-    instances <- mapM (const $ liftIO $ spawnSolver solver threads timeout) [1..count]+withSolvers :: App m => Solver -> Natural -> Maybe Natural -> Natural -> (SolverGroup -> m a) -> m a+withSolvers solver count timeout maxMemory cont = do     -- spawn orchestration thread     taskq <- liftIO newChan     cacheq <- liftIO . atomically $ newTChan-    availableInstances <- liftIO newChan-    liftIO $ forM_ instances (writeChan availableInstances)-    orchestrate' <- toIO $ orchestrate taskq cacheq availableInstances [] 0+    sem <- liftIO $ newQSem (fromIntegral count)+    orchestrate' <- toIO $ orchestrate taskq cacheq sem [] 0     orchestrateId <- liftIO $ forkIO orchestrate'      -- run continuation with task queue     res <- cont (SolverGroup taskq)      -- cleanup and return results-    liftIO $ mapM_ (stopSolver) instances     liftIO $ killThread orchestrateId     pure res   where-    orchestrate :: App m => Chan Task -> TChan CacheEntry -> Chan SolverInstance -> [Set Prop] -> Int -> m b-    orchestrate taskq cacheq avail knownUnsat fileCounter = do+    orchestrate :: App m => Chan Task -> TChan CacheEntry -> QSem -> [Set Prop] -> Int -> m b+    orchestrate taskq cacheq sem knownUnsat fileCounter = do       conf <- readConfig       mx <- liftIO . atomically $ tryReadTChan cacheq       case mx of         Just (CacheEntry props)  -> do           let knownUnsat' = (fromList props):knownUnsat           when conf.debug $ liftIO $ putStrLn "   adding UNSAT cache"-          orchestrate taskq cacheq avail knownUnsat' fileCounter+          orchestrate taskq cacheq sem knownUnsat' fileCounter         Nothing -> do           task <- liftIO $ readChan taskq           case task of             TaskSingle (SingleData _ props r) | isJust props && supersetAny (fromList (fromJust props)) knownUnsat -> do               liftIO $ writeChan r Qed               when conf.debug $ liftIO $ putStrLn "   Qed found via cache!"-              orchestrate taskq cacheq avail knownUnsat fileCounter+              orchestrate taskq cacheq sem knownUnsat fileCounter             _ -> do-              inst <- liftIO $ readChan avail               runTask' <- case task of-                TaskSingle (SingleData smt2 props r) -> toIO $ getOneSol smt2 props r cacheq inst avail fileCounter-                TaskMulti (MultiData smt2 multiSol r) -> toIO $ getMultiSol smt2 multiSol r inst avail fileCounter+                TaskSingle (SingleData smt2 props r) -> toIO $ getOneSol solver timeout maxMemory smt2 props r cacheq sem fileCounter+                TaskMulti (MultiData smt2 multiSol r) -> toIO $ getMultiSol solver timeout maxMemory smt2 multiSol r sem fileCounter               _ <- liftIO $ forkIO runTask'-              orchestrate taskq cacheq avail knownUnsat (fileCounter + 1)+              orchestrate taskq cacheq sem knownUnsat (fileCounter + 1) -getMultiSol :: forall m. (MonadIO m, ReadConfig m) => SMT2 -> MultiSol -> (Chan (Maybe [W256])) -> SolverInstance -> Chan SolverInstance -> Int -> m ()-getMultiSol smt2@(SMT2 cmds cexvars _) multiSol r inst availableInstances fileCounter = do+getMultiSol :: forall m. (MonadIO m, ReadConfig m) => Solver -> Maybe Natural -> Natural -> SMT2 -> MultiSol -> Chan (Maybe [W256]) -> QSem -> Int -> m ()+getMultiSol solver timeout maxMemory smt2@(SMT2 cmds cexvars _) multiSol r sem fileCounter = do   conf <- readConfig-  when conf.dumpQueries $ liftIO $ writeSMT2File smt2 "." (show fileCounter)-  -- reset solver and send all lines of provided script-  out <- liftIO $ do-    resetRes <- sendScript inst $ SMTScript [SMTCommand "(reset)"]-    case resetRes of-      e@(Left _) -> pure e-      _ -> sendScript inst cmds-  case out of-    Left err -> liftIO $ do-      when conf.debug $ putStrLn $ "Unable to write SMT to solver: " <> (T.unpack err)-      writeChan r Nothing-    Right _ -> do-      sat <- liftIO $ sendCommand inst $ SMTCommand "(check-sat)"-      when conf.dumpQueries $ liftIO $ writeSMT2File smt2 "." (show fileCounter <> "-origquery")-      subRun [] smt2 sat-  -- put the instance back in the list of available instances-  liftIO $ writeChan availableInstances inst-  where+  let     maskFromBytesCount k       | k <= 32 = (2 ^ (8 * k) - 1)       | otherwise = internalError "Byte length exceeds 256-bit capacity"-    subRun :: (MonadIO m, ReadConfig m) => [W256] -> SMT2 -> Text -> m ()-    subRun vals fullSmt sat = do-      conf <- readConfig+    subRun :: SolverInstance -> [W256] -> SMT2 -> Text -> IO ()+    subRun inst vals fullSmt sat = do       case sat of-        "unsat" -> liftIO $ do+        "unsat" -> do           when conf.debug $ putStrLn $ "No more solutions to query, returning: " <> show vals-          liftIO $ writeChan r (Just vals)-        "timeout" -> liftIO $ do+          writeChan r (Just vals)+        "timeout" -> do            when conf.debug $ putStrLn "Timeout inside SMT solver."            writeChan r Nothing-        "unknown" -> liftIO $ do+        "unknown" -> do            when conf.debug $ putStrLn "Unknown result by SMT solver."            dumpUnsolved fullSmt fileCounter conf.dumpUnsolved            writeChan r Nothing         "sat" -> do-          if length vals >= multiSol.maxSols then liftIO $ do+          if length vals >= multiSol.maxSols then do             when conf.debug $ putStrLn "Too many solutions to symbolic query."             writeChan r Nothing           else do-            cex <- liftIO $ getModel inst cexvars-            case Map.lookup (Var (TStrict.pack multiSol.var)) cex.vars of-              Just v -> do-                let hexMask = maskFromBytesCount multiSol.numBytes-                    maskedVal = v .&. hexMask-                    toSMT n = Data.Text.Lazy.Builder.Int.decimal n-                    maskedVar = "(bvand " <> fromString multiSol.var <> " (_ bv" <> toSMT hexMask <> " 256))"-                    restrict = SMTCommand $ "(assert (not (= " <> maskedVar <> " (_ bv" <> toSMT maskedVal <> " 256))))"-                    newSmt = fullSmt <> SMT2 (SMTScript [restrict]) mempty mempty-                when conf.debug $ liftIO $ putStrLn $ "Got one solution to symbolic query, val: 0x" <> (showHex maskedVal "") <>-                  " now have " <> show (length vals + 1) <> " solution(s), max is: " <> show multiSol.maxSols-                when conf.dumpQueries $ liftIO $ writeSMT2File newSmt "." (show fileCounter <> "-sol" <> show (length vals))-                out <- liftIO $ sendCommand inst restrict-                case out of-                  "success" -> do-                    out2 <- liftIO $ sendCommand inst  (SMTCommand "(check-sat)")-                    subRun (maskedVal:vals) newSmt out2-                  err -> liftIO $ do-                    when conf.debug $ putStrLn $ "Unable to write SMT to solver: " <> (T.unpack err)-                    writeChan r Nothing-              Nothing -> internalError $ "variable " <>  multiSol.var <> " not part of model (i.e. cex) ... that's not possible"-        err -> liftIO $ do-          when conf.debug $ putStrLn $ "Unable to write SMT to solver: " <> (T.unpack err)+            mcex <- getModel inst cexvars+            case mcex of+              Nothing -> do+                when conf.debug $ putStrLn "Solver died while extracting model."+                writeChan r Nothing+              Just cex -> case Map.lookup (Var (TStrict.pack multiSol.var)) cex.vars of+                Just v -> do+                  let hexMask = maskFromBytesCount multiSol.numBytes+                      maskedVal = v .&. hexMask+                      toSMT n = Data.Text.Lazy.Builder.Int.decimal n+                      maskedVar = "(bvand " <> fromString multiSol.var <> " (_ bv" <> toSMT hexMask <> " 256))"+                      restrict = SMTCommand $ "(assert (not (= " <> maskedVar <> " (_ bv" <> toSMT maskedVal <> " 256))))"+                      newSmt = fullSmt <> SMT2 (SMTScript [restrict]) mempty mempty+                  when conf.debug $ putStrLn $ "Got one solution to symbolic query, val: 0x" <> (showHex maskedVal "") <>+                    " now have " <> show (length vals + 1) <> " solution(s), max is: " <> show multiSol.maxSols+                  when conf.dumpQueries $ writeSMT2File newSmt "." (show fileCounter <> "-sol" <> show (length vals))+                  out <- sendCommand inst restrict+                  case out of+                    "success" -> do+                      out2 <- sendCommand inst  (SMTCommand "(check-sat)")+                      subRun inst (maskedVal:vals) newSmt out2+                    err -> do+                      when conf.debug $ putStrLn $ "Error while writing SMT to solver: " <> (T.unpack err)+                      writeChan r Nothing+                Nothing -> internalError $ "variable " <>  multiSol.var <> " not part of model (i.e. cex) ... that's not possible"+        err -> do+          when conf.debug $ putStrLn $ "Error while writing SMT to solver: " <> (T.unpack err)           writeChan r Nothing+  liftIO $ bracket_+    (waitQSem sem)+    (signalQSem sem)+    (do+      when conf.dumpQueries $ writeSMT2File smt2 "." (show fileCounter)+      bracket+        (spawnSolver solver timeout maxMemory)+        (stopSolver)+        (\inst -> do+          out <- sendScript inst cmds+          case out of+            Left err -> do+              when conf.debug $ putStrLn $ "Issue while writing SMT to solver (maybe it got killed)?: " <> (T.unpack err)+              writeChan r Nothing+            Right _ -> do+              sat <- sendCommand inst $ SMTCommand "(check-sat)"+              when conf.dumpQueries $ writeSMT2File smt2 "." (show fileCounter <> "-origquery")+              subRun inst [] smt2 sat+        )+    ) -getOneSol :: (MonadIO m, ReadConfig m) => SMT2 -> Maybe [Prop] -> Chan SMTResult -> TChan CacheEntry -> SolverInstance -> Chan SolverInstance -> Int -> m ()-getOneSol smt2@(SMT2 cmds cexvars _) props r cacheq inst availableInstances fileCounter = do+getOneSol :: (MonadIO m, ReadConfig m) => Solver -> Maybe Natural -> Natural -> SMT2 -> Maybe [Prop] -> Chan SMTResult -> TChan CacheEntry -> QSem -> Int -> m ()+getOneSol solver timeout maxMemory smt2@(SMT2 cmds cexvars _) props r cacheq sem fileCounter = do   conf <- readConfig-  liftIO $ do-    when (conf.dumpQueries) $ writeSMT2File smt2 "." (show fileCounter)-    -- reset solver and send all lines of provided script-    out <- do-      resetRes <- sendScript inst $ SMTScript [SMTCommand "(reset)"]-      case resetRes of-        e@(Left _) -> pure e-        _ -> sendScript inst cmds-    case out of-      -- if we got an error then return it-      Left e -> writeChan r (Error $ "Error while writing SMT to solver: " <> T.unpack e)-      -- otherwise call (check-sat), parse the result, and send it down the result channel-      Right () -> do-        sat <- sendCommand inst $ SMTCommand "(check-sat)"-        res <- do-            case sat of-              "unsat" -> do-                when (isJust props) $ liftIO . atomically $ writeTChan cacheq (CacheEntry (fromJust props))-                pure Qed-              "timeout" -> pure $ Unknown "Result timeout by SMT solver"-              "unknown" -> do-                dumpUnsolved smt2 fileCounter conf.dumpUnsolved-                pure $ Unknown "Result unknown by SMT solver"-              "sat" -> Cex <$> getModel inst cexvars-              _ -> pure . Error $ "Unable to parse SMT solver output: " <> T.unpack sat-        writeChan r res--    -- put the instance back in the list of available instances-    writeChan availableInstances inst+  liftIO $ bracket_+    (waitQSem sem)+    (signalQSem sem)+    (do+      when (conf.dumpQueries) $ writeSMT2File smt2 "." (show fileCounter)+      bracket+        (spawnSolver solver timeout maxMemory)+        (stopSolver)+        (\inst -> do+          out <- sendScript inst cmds+          case out of+            Left e -> writeChan r (Unknown $ "Issue while writing SMT to solver (maybe it got killed?): " <> T.unpack e)+            Right () -> do+              sat <- sendCommand inst $ SMTCommand "(check-sat)"+              res <- do+                  case sat of+                    "unsat" -> do+                      when (isJust props) $ liftIO . atomically $ writeTChan cacheq (CacheEntry (fromJust props))+                      pure Qed+                    "timeout" -> pure $ Unknown "Result timeout by SMT solver"+                    "unknown" -> do+                      dumpUnsolved smt2 fileCounter conf.dumpUnsolved+                      pure $ Unknown "Result unknown by SMT solver"+                    "sat" -> do+                      mmodel <- getModel inst cexvars+                      case mmodel of+                        Just model -> pure $ Cex model+                        Nothing -> pure $ Unknown "Solver died while extracting model"+                    _ -> let  supportIssue =+                                  ("does not yet support" `T.isInfixOf` sat)+                                  || ("unsupported" `T.isInfixOf` sat)+                                  || ("not support" `T.isInfixOf` sat)+                      in case supportIssue of+                       True -> pure . Error $ "SMT solver reported unsupported operation: " <> T.unpack sat+                       False -> pure . Unknown $ "Unable to parse SMT solver output (maybe it got killed?): " <> T.unpack sat+              writeChan r res+        )+    )  dumpUnsolved :: SMT2 -> Int -> Maybe FilePath -> IO () dumpUnsolved fullSmt fileCounter dump = do@@ -282,8 +311,8 @@      Just path -> writeSMT2File fullSmt path $ "unsolved-" <> show fileCounter      Nothing -> pure () -getModel :: SolverInstance -> CexVars -> IO SMTCex-getModel inst cexvars = do+getModel :: SolverInstance -> CexVars -> IO (Maybe SMTCex)+getModel inst cexvars = runMaybeT $ do   -- get an initial version of the model from the solver   initialModel <- getRaw   -- check the sizes of buffer models and shrink if needed@@ -291,10 +320,11 @@   then pure initialModel   else do     -- get concrete values for each buffers max read index-    hints <- capHints <$> queryMaxReads (getValue inst) cexvars.buffers-    snd <$> runStateT (shrinkModel hints) initialModel+    hints <- queryMaxReads (getValue inst) cexvars.buffers+    let cappedHints = capHints hints+    lift (execStateT (shrinkModel cappedHints) initialModel)   where-    getRaw :: IO SMTCex+    getRaw :: MaybeIO SMTCex     getRaw = do       vars <- getVars parseVar (getValue inst) (fmap T.toStrict cexvars.calldata)       addrs <- getAddrs parseEAddr (getValue inst) (fmap T.toStrict cexvars.addrs)@@ -332,23 +362,30 @@     shrinkBuf :: Text -> W256 -> StateT SMTCex IO ()     shrinkBuf buf hint = do       let encBound = "(_ bv" <> Data.Text.Lazy.Builder.Int.decimal hint <> " 256)"-      answer <- liftIO $ do-        checkCommand inst $ SMTCommand "(push 1)"-        checkCommand inst $ SMTCommand ("(assert (bvule " <> (fromLazyText buf) <> "_length " <> encBound <> "))")-        sendCommand inst $ SMTCommand "(check-sat)"-      case answer of-        "sat" -> do-          model <- liftIO getRaw-          put model-        "unsat" -> do-          liftIO $ checkCommand inst $ SMTCommand "(pop 1)"-          let nextHint = if hint == 0 then 1 else hint * 2-          if nextHint < hint || nextHint > 1_073_741_824-            then pure () -- overflow or over 1GB-            else shrinkBuf buf nextHint-        _ -> do -- unexpected answer -> clean up and do not change the model-          liftIO $ checkCommand inst $ SMTCommand "(pop 1)"-          pure ()+      -- Try to push and assert; if either fails, solver died, keep current model+      setup <- liftIO $ runMaybeT $ do+        MaybeT $ checkCommand inst $ SMTCommand "(push 1)"+        MaybeT $ checkCommand inst $ SMTCommand ("(assert (bvule " <> (fromLazyText buf) <> "_length " <> encBound <> "))")+      case setup of+        Nothing -> pure ()  -- solver died during setup, keep current model+        Just () -> do+          answer <- liftIO $ sendCommand inst $ SMTCommand "(check-sat)"+          case answer of+            "sat" -> do+              mmodel <- liftIO $ runMaybeT getRaw+              for_ mmodel put  -- solver died, keep current model+            "unsat" -> do+              mpop <- liftIO $ checkCommand inst $ SMTCommand "(pop 1)"+              case mpop of+                Nothing -> pure ()  -- solver died, keep current model+                Just () -> do+                  let nextHint = if hint == 0 then 1 else hint * 2+                  if nextHint < hint || nextHint > 1_073_741_824+                    then pure () -- overflow or over 1GB+                    else shrinkBuf buf nextHint+            _ -> do -- unexpected answer -> clean up and do not change the model+              _ <- liftIO $ checkCommand inst $ SMTCommand "(pop 1)"+              pure ()       -- we set a pretty arbitrary upper limit (of 32) to decide if we need to do some shrinking@@ -361,24 +398,22 @@           -- TODO: do I need to check the write idx here?           (Write _ idx next) -> idx <= 32 && go (Comp next) -mkTimeout :: Maybe Natural -> Text-mkTimeout t = T.pack $ show $ (1000 *)$ case t of-  Nothing -> 300 :: Natural-  Just t' -> t' +-- Takes Maybe seconds, returns seconds+mkTimeout :: Maybe Natural -> Natural+mkTimeout = fromMaybe 300+ -- | Arguments used when spawning a solver instance-solverArgs :: Solver -> Natural -> Maybe Natural -> [Text]-solverArgs solver threads timeout = case solver of+solverArgs :: Solver -> Maybe Natural -> [Text]+solverArgs solver timeout = case solver of   Bitwuzla ->     [ "--lang=smt2"     , "--produce-models"-    , "--time-limit-per=" <> mkTimeout timeout-    , "--bv-solver=preprop"+    , "--time-limit-per=" <> millisecs     , "--bv-output-format=16"     ]   Z3 ->     [ "-st"-    , "smt.threads=" <> (T.pack $ show threads)     , "-in" ]   CVC5 ->     [ "--lang=smt"@@ -386,22 +421,58 @@     , "--print-success"     , "--interactive"     , "--incremental"-    , "--tlimit-per=" <> mkTimeout timeout+    , "--tlimit-per=" <> millisecs     , "--arrays-exp"     ]   EmptySolver -> []   Custom _ -> []+  where millisecs = T.pack $ show $ 1000 * (mkTimeout timeout)  -- | Spawns a solver instance, and sets the various global config options that we use for our queries-spawnSolver :: Solver -> Natural -> Maybe (Natural) -> IO SolverInstance-spawnSolver solver threads timeout = do+spawnSolver :: Solver -> Maybe (Natural) -> Natural -> IO SolverInstance+#if defined(mingw32_HOST_OS)+spawnSolver solver timeout _ = do   (readout, writeout) <- createPipe-  let cmd-        = (proc (show solver) (fmap T.unpack $ solverArgs solver threads timeout ))+  let solverCmd = show solver+      solverArgsStr = fmap T.unpack $ solverArgs solver timeout+      -- Windows: no ulimit available+      cmd = (proc solverCmd solverArgsStr)             { std_in = CreatePipe             , std_out = UseHandle writeout             , std_err = UseHandle writeout             }+#elif defined(darwin_HOST_OS)+spawnSolver solver timeout _ = do+  (readout, writeout) <- createPipe+  let timeoutSeconds = mkTimeout timeout+      solverCmd = show solver+      solverArgsStr = fmap T.unpack $ solverArgs solver timeout+      -- macOS: memory limit not possible, but CPU limits work+      -- ulimit -t sets RLIMIT_CPU (kernel-enforced CPU time limit in seconds)+      shellCmd = "sh"+      shellArgs = ["-c", "ulimit -t " ++ show timeoutSeconds ++ "; exec \"$0\" \"$@\"", solverCmd] ++ solverArgsStr+      cmd = (proc shellCmd shellArgs)+            { std_in = CreatePipe+            , std_out = UseHandle writeout+            , std_err = UseHandle writeout+            }+#else+spawnSolver solver timeout maxMemoryMB = do+  (readout, writeout) <- createPipe+  let timeoutSeconds = mkTimeout timeout+      solverCmd = show solver+      solverArgsStr = fmap T.unpack $ solverArgs solver timeout+      -- Linux: both CPU and memory limits work+      -- ulimit -v sets RLIMIT_AS (kernel-enforced virtual memory limit in KB)+      shellCmd = "sh"+      maxMemoryKB = maxMemoryMB * 1024  -- Convert MB to KB for ulimit -v+      shellArgs = ["-c", "ulimit -t " ++ show timeoutSeconds ++ "; ulimit -v " ++ show maxMemoryKB ++ "; exec \"$0\" \"$@\"", solverCmd] ++ solverArgsStr+      cmd = (proc shellCmd shellArgs)+            { std_in = CreatePipe+            , std_out = UseHandle writeout+            , std_err = UseHandle writeout+            }+#endif   (Just stdin, Nothing, Nothing, process) <- createProcess cmd   hSetBuffering stdin (BlockBuffering (Just 1000000))   let solverInstance = SolverInstance solver stdin readout process@@ -414,7 +485,6 @@     EmptySolver -> pure solverInstance     Z3 -> do       _ <- sendCommand solverInstance $ SMTCommand "(set-option :print-success true)"-      _ <- sendCommand solverInstance $ SMTCommand ("(set-option :timeout " <> (fromLazyText $ mkTimeout timeout) <> ")")       pure solverInstance     Custom _ -> pure solverInstance @@ -435,39 +505,48 @@         "success" -> go cs         e -> pure $ Left $ "Solver returned an error:\n" <> e <> "\nwhile sending the following command: " <> toLazyText command -checkCommand :: SolverInstance -> SMTEntry -> IO ()+-- | Returns Nothing if the solver died or returned an error+checkCommand :: SolverInstance -> SMTEntry -> IO (Maybe ()) checkCommand inst cmd = do   res <- sendCommand inst cmd-  case res of-    "success" -> pure ()-    _ -> internalError $ "Unexpected solver output: " <> T.unpack res+  pure $ if res == "success" then Just () else Nothing   -- | Strips trailing \r, if present stripCarriageReturn :: Text -> Text stripCarriageReturn t = fromMaybe t $ T.stripSuffix "\r" t +-- | Helper function for when we want to try an IO action and get Nothing on exception+tryIO :: forall a. IO a -> MaybeT IO a+tryIO action = MaybeT $ either (const Nothing) Just <$> (try action :: IO (Either IOException a))+ -- | Sends a string to the solver and appends a newline, returns the first available line from the output buffer+-- Returns "unknown" if the solver process has died (timeout, crash, etc.) sendCommand :: SolverInstance -> SMTEntry -> IO Text sendCommand _ (SMTComment _) = internalError "Attempting to send a comment as a command to SMT solver" sendCommand (SolverInstance _ stdin stdout _) (SMTCommand cmd) = do-  T.hPutStrLn stdin $ toLazyText cmd-  hFlush stdin-  stripCarriageReturn <$> (T.hGetLine stdout)+  result <- runMaybeT $ do+    tryIO $ T.hPutStrLn stdin $ toLazyText cmd+    tryIO $ hFlush stdin+    tryIO $ stripCarriageReturn <$> T.hGetLine stdout+  pure $ fromMaybe "unknown" result  -- | Returns a string representation of the model for the requested variable-getValue :: SolverInstance -> Text -> IO Text+-- Returns Nothing if the solver process has died (timeout, crash, etc.)+getValue :: SolverInstance -> Text -> MaybeIO Text getValue (SolverInstance _ stdin stdout _) var = do-  T.hPutStrLn stdin (T.append (T.append "(get-value (" var) "))")-  hFlush stdin-  fmap (T.unlines . reverse) (readSExpr stdout)+  tryIO $ T.hPutStrLn stdin (T.append (T.append "(get-value (" var) "))")+  tryIO $ hFlush stdin+  val <- readSExpr stdout+  pure $ (T.unlines . reverse) val  -- | Reads lines from h until we have a balanced sexpr-readSExpr :: Handle -> IO [Text]+-- Returns Nothing if the solver process has died or parsing fails+readSExpr :: Handle -> MaybeIO [Text] readSExpr h = go 0 0 []   where     go 0 0 _ = do-      line <- T.hGetLine h+      line <- tryIO $ T.hGetLine h       let cleanLine = stripCarriageReturn line           ls = T.length $ T.filter (== '(') cleanLine           rs = T.length $ T.filter (== ')') cleanLine@@ -475,7 +554,7 @@          then pure [cleanLine]          else go ls rs [cleanLine]     go ls rs prev = do-      line <- T.hGetLine h+      line <- tryIO $ T.hGetLine h       let cleanLine = stripCarriageReturn line           ls' = T.length $ T.filter (== '(') cleanLine           rs' = T.length $ T.filter (== ')') cleanLine
src/EVM/SymExec.hs view
@@ -11,9 +11,9 @@ import Control.Concurrent.Chan (Chan, newChan, writeChan, readChan) import Control.Concurrent.Spawn (parMapIO, pool) import Control.Concurrent.STM (writeTChan, newTChan, TChan, readTChan, atomically, isEmptyTChan, STM)-import Control.Concurrent.STM.TVar (TVar, newTVarIO, modifyTVar, readTVar, writeTVar)+import Control.Concurrent.STM.TVar (TVar, newTVarIO, modifyTVar, readTVar, readTVarIO, writeTVar) import Control.Concurrent.STM.TMVar (TMVar, putTMVar, takeTMVar, newEmptyTMVarIO)-import Control.Monad (when, forM_, forM, forever)+import Control.Monad (when, unless, forM_, forM, forever, void) import Control.Monad.Loops (whileM) import Control.Monad.IO.Unlift (MonadUnliftIO, toIO, withRunInIO) import Control.Monad.Operational qualified as Operational@@ -48,13 +48,12 @@ import Text.Printf (printf) import Witch (into, unsafeInto) -import EVM (makeVm, abstractContract, initialContract, getCodeLocation, isValidJumpDest)+import EVM (makeVm, abstractContract, initialContract, getCodeLocation, isValidJumpDest, defaultVMOpts) import EVM.Exec (exec) import EVM.Fetch qualified as Fetch import EVM.ABI import EVM.Effects import EVM.Expr qualified as Expr-import EVM.FeeSchedule (feeSchedule) import EVM.Format (formatExpr, formatPartial, formatPartialDetailed, showVal, indent, formatBinary, formatProp, formatState, formatError) import EVM.SMT qualified as SMT import EVM.Solvers (SolverGroup, checkSatWithProps)@@ -63,8 +62,6 @@ import EVM.Traversals (mapExpr, mapExprM, foldTerm) import EVM.Types hiding (Comp) import EVM.Types qualified-import EVM.Solidity (WarningData (..))- data LoopHeuristic   = Naive   | StackBased@@ -79,11 +76,11 @@     getIssue _ = Nothing     grouped = NE.group $ sort $ mapMaybe getIssue results -groupPartials :: Maybe (WarningData t) -> [Expr End] -> [(Integer, String)]-groupPartials warnData e = map (\g -> (into (length g), NE.head g)) grouped+groupPartials :: Maybe SrcLookup -> Map (Expr EAddr) Contract -> [Expr End] -> [(Integer, String)]+groupPartials srcLookupM contracts e = map (\g -> (into (length g), NE.head g)) grouped   where     getPartial :: Expr End -> Maybe String-    getPartial (Partial _ _ reason) = Just $ T.unpack $ formatPartialDetailed warnData reason+    getPartial (Partial _ _ reason) = Just $ T.unpack $ formatPartialDetailed srcLookupM contracts reason     getPartial _ = Nothing     grouped = NE.group $ sort $ mapMaybe getPartial e @@ -119,8 +116,8 @@   -- | Abstract calldata argument generation-symAbiArg :: Text -> AbiType -> CalldataFragment-symAbiArg name = \case+symAbiArg :: Int -> Text -> AbiType -> CalldataFragment+symAbiArg maxDynSize name = \case   AbiUIntType n ->     if n `mod` 8 == 0 && n <= 256     then St [] v@@ -136,16 +133,29 @@     then St [] v     else internalError "bad type"   AbiArrayType sz tps -> do-    Comp . V.toList . V.imap (\(T.pack . show -> i) tp -> symAbiArg (name <> "-a-" <> i) tp) $ (V.replicate sz tps)+    Comp . V.toList . V.imap (\(T.pack . show -> i) tp -> symAbiArg maxDynSize (name <> "-a-" <> i) tp) $ (V.replicate sz tps)   AbiTupleType tps ->-    Comp . V.toList . V.imap (\(T.pack . show -> i) tp -> symAbiArg (name <> "-t-" <> i) tp) $ tps+    Comp . V.toList . V.imap (\(T.pack . show -> i) tp -> symAbiArg maxDynSize (name <> "-t-" <> i) tp) $ tps+  -- Dynamic bytes/string: concretize with bounded symbolic length + abstract buffer+  AbiBytesDynamicType ->+    let bufName = AbstractBuf (name <> "-bytes")+        len = Var (name <> "-bytes-length")+        maxLit = Lit (fromIntegral maxDynSize)+        sizeBound = [PLEq len maxLit]+    in Dy sizeBound len maxDynSize bufName+  AbiStringType ->+    let bufName = AbstractBuf (name <> "-string")+        len = Var (name <> "-string-length")+        maxLit = Lit (fromIntegral maxDynSize)+        sizeBound = [PLEq len maxLit]+    in Dy sizeBound len maxDynSize bufName   t -> internalError $ "TODO: symbolic abi encoding for " <> show t   where     v = Var name  data CalldataFragment   = St [Prop] (Expr EWord)-  | Dy [Prop] (Expr EWord) (Expr Buf)+  | Dy [Prop] (Expr EWord) Int (Expr Buf) -- ^ props, symbolic length, max concrete size, buffer   | Comp [CalldataFragment]   deriving (Show, Eq) @@ -153,13 +163,13 @@ -- with concrete arguments. -- Any argument given as "<symbolic>" or omitted at the tail of the list are -- kept symbolic.-symCalldata :: App m => Text -> [AbiType] -> [String] -> Expr Buf -> m (Expr Buf, [Prop])+symCalldata :: App m => Text -> [AbiType] -> [String] -> Expr Buf -> m (Expr Buf, [Prop], [Caveat]) symCalldata sig typesignature concreteArgs base = do   conf <- readConfig   let     args = concreteArgs <> replicate (length typesignature - length concreteArgs) "<symbolic>"     mkArg :: AbiType -> String -> Int -> CalldataFragment-    mkArg typ "<symbolic>" n = symAbiArg (T.pack $ "arg" <> show n) typ+    mkArg typ "<symbolic>" n = symAbiArg conf.maxDynSize (T.pack $ "arg" <> show n) typ     mkArg typ arg _ =       case makeAbiValue typ arg of         AbiUInt _ w -> St [] . Lit . into $ w@@ -173,17 +183,38 @@     sizeConstraints       = (Expr.bufLength withSelector .>= cdLen calldatas)       .&& (Expr.bufLength withSelector .< (Lit (2 ^ conf.maxBufSize)))-  pure (withSelector, sizeConstraints : props)+    -- A dynamic (bytes/string) argument is turned into a 'Dy' fragment with its+    -- length bounded to maxDynSize. That is a restriction of the input domain+    -- (every path is still explored fully), so we record it as a program-wide+    -- 'Caveat' rather than a per-path 'Partial'. Derived from the fragments we+    -- actually built, so a concretely-supplied argument produces no caveat.+    caveats = [DynArgBounded conf.maxDynSize | any isDy calldatas]+  pure (withSelector, sizeConstraints : props, caveats)  cdLen :: [CalldataFragment] -> Expr EWord-cdLen = go (Lit 4)+cdLen frags = Expr.add (headLen frags) (tailLen frags)   where-    go acc = \case-      [] -> acc-      (hd:tl) -> case hd of-                   St _ _ -> go (Expr.add acc (Lit 32)) tl-                   Comp xs | all isSt xs -> go acc (xs <> tl)-                   _ -> internalError "unsupported"+    -- Head: 4 bytes selector + 32 bytes per top-level arg (static or dynamic pointer)+    headLen = go (Lit 4)+      where+        go acc = \case+          [] -> acc+          (hd:tl) -> case hd of+            St _ _  -> go (Expr.add acc (Lit 32)) tl+            Dy {}   -> go (Expr.add acc (Lit 32)) tl+            Comp xs | all isSt xs -> go acc (xs <> tl)+            _ -> internalError "unsupported"+    -- Tail: for each Dy fragment, 32 bytes (length word) + padded data+    tailLen = go (Lit 0)+      where+        go acc = \case+          [] -> acc+          (hd:tl) -> case hd of+            Dy _ _ maxSz _ ->+              let paddedMax = ((maxSz + 31) `Prelude.div` 32) * 32+              in go (Expr.add acc (Lit (fromIntegral (32 + paddedMax)))) tl+            Comp xs | all isSt xs -> go acc (xs <> tl)+            _ -> go acc tl  writeSelector :: Expr Buf -> Text -> Expr Buf writeSelector buf sig =@@ -193,17 +224,48 @@     writeSel idx = Expr.writeByte idx (Expr.readByte idx sel)  combineFragments :: [CalldataFragment] -> Expr Buf -> (Expr Buf, [Prop])-combineFragments fragments base = go (Lit 4) fragments (base, [])+combineFragments fragments base =+  let headBytes = countHeadBytes fragments+      tailStart = Expr.add (Lit 4) headBytes+  in go headBytes (Lit 4) tailStart (Lit 0) fragments (base, [])   where-    go :: Expr EWord -> [CalldataFragment] -> (Expr Buf, [Prop]) -> (Expr Buf, [Prop])-    go _ [] acc = acc-    go idx (f:rest) (buf, ps) =+    -- Each top-level fragment occupies 32 bytes in the head (value or offset pointer)+    countHeadBytes :: [CalldataFragment] -> Expr EWord+    countHeadBytes = foldl' countFrag (Lit 0)+      where+        countFrag acc (St _ _)  = Expr.add acc (Lit 32)+        countFrag acc (Dy {})   = Expr.add acc (Lit 32)+        countFrag acc (Comp xs) | all isSt xs = foldl' countFrag acc xs+        countFrag _ s = internalError $ "unsupported cd fragment: " <> show s++    go :: Expr EWord -> Expr EWord -> Expr EWord -> Expr EWord+       -> [CalldataFragment] -> (Expr Buf, [Prop]) -> (Expr Buf, [Prop])+    go _ _ _ _ [] acc = acc+    go headSz idx tailBase tailAcc (f:rest) (buf, ps) =       case f of         -- static fragments get written as a word in place-        St p w -> go (Expr.add idx (Lit 32)) rest (Expr.writeWord idx w buf, p <> ps)+        St p w -> go headSz (Expr.add idx (Lit 32)) tailBase tailAcc rest+                    (Expr.writeWord idx w buf, p <> ps)         -- compound fragments that contain only static fragments get written in place-        Comp xs | all isSt xs -> go idx (xs <> rest) (buf,ps)-        -- dynamic fragments are not yet supported... :/+        Comp xs | all isSt xs -> go headSz idx tailBase tailAcc (xs <> rest) (buf, ps)+        -- dynamic fragments: write offset pointer in head, data in tail+        Dy p len maxSz dynBuf ->+          let paddedMax = ((maxSz + 31) `Prelude.div` 32) * 32+              -- ABI offset is relative to the start of the args (byte 4)+              offset = Expr.add headSz tailAcc+              -- Write the offset pointer in the head area+              buf1 = Expr.writeWord idx offset buf+              -- Write the length word at the tail position+              dynPos = Expr.add tailBase tailAcc+              buf2 = Expr.writeWord dynPos len buf1+              -- Copy maxDynSize bytes from the abstract buffer using concrete+              -- size to avoid symbolic CopySlice issues+              dataPos = Expr.add dynPos (Lit 32)+              buf3 = Expr.copySlice (Lit 0) dataPos (Lit (fromIntegral maxSz)) dynBuf buf2+              -- Advance tail by 32 (length word) + paddedMax+              newTailAcc = Expr.add tailAcc (Lit (fromIntegral (32 + paddedMax)))+          in go headSz (Expr.add idx (Lit 32)) tailBase newTailAcc rest+               (buf3, p <> ps)         s -> internalError $ "unsupported cd fragment: " <> show s  isSt :: CalldataFragment -> Bool@@ -211,7 +273,11 @@ isSt (Comp fs) = all isSt fs isSt _ = False +isDy :: CalldataFragment -> Bool+isDy (Dy {}) = True+isDy _ = False + abstractVM   :: (Expr Buf, [Prop])   -> ByteString@@ -234,33 +300,16 @@   -> (Expr Buf, [Prop])   -> ST RealWorld (VM Symbolic) loadEmptySymVM x callvalue cd =-  (makeVm $ VMOpts+  (makeVm $ defaultVMOpts     { contract = initialContract x-    , otherContracts = []     , calldata = cd     , value = callvalue-    , baseState = EmptyBase     , address = SymAddr "entrypoint"     , caller = SymAddr "caller"     , origin = SymAddr "origin"     , coinbase = SymAddr "coinbase"-    , number = Lit 0-    , timestamp = Lit 0     , blockGaslimit = 0-    , gasprice = 0     , prevRandao = 42069-    , gas = ()-    , gaslimit = 0xffffffffffffffff-    , baseFee = 0-    , priorityFee = 0-    , maxCodeSize = 0xffffffff-    , schedule = feeSchedule-    , chainId = 1-    , create = False-    , txAccessList = mempty-    , allowFFI = False-    , freshAddresses = 0-    , beaconRoot = 0     })  -- Creates a symbolic VM that has symbolic storage, unlike loadEmptySymVM@@ -271,9 +320,8 @@   -> Bool   -> ST RealWorld (VM Symbolic) loadSymVM x callvalue cd create =-  (makeVm $ VMOpts+  (makeVm $ defaultVMOpts     { contract = if create then initialContract x else abstractContract x (SymAddr "entrypoint")-    , otherContracts = []     , calldata = cd     , value = callvalue     , baseState = AbstractBase@@ -281,23 +329,9 @@     , caller = SymAddr "caller"     , origin = SymAddr "origin"     , coinbase = SymAddr "coinbase"-    , number = Lit 0-    , timestamp = Lit 0     , blockGaslimit = 0-    , gasprice = 0     , prevRandao = 42069-    , gas = ()-    , gaslimit = 0xffffffffffffffff-    , baseFee = 0-    , priorityFee = 0-    , maxCodeSize = 0xffffffff-    , schedule = feeSchedule-    , chainId = 1     , create = create-    , txAccessList = mempty-    , allowFFI = False-    , freshAddresses = 0-    , beaconRoot = 0     })  -- freezes any mutable refs, making it safe to share between threads@@ -319,6 +353,8 @@       ConcreteMemory m -> SymbolicMemory . ConcreteBuf . vectorToByteString <$> VS.freeze m       m@(SymbolicMemory _) -> pure m +type PathHandler m a = Expr End -> TVar Bool -> m a+ data InterpTask m a = InterpTask   {fetcher :: Fetch.Fetcher Symbolic m   , iterConf :: IterConfig@@ -326,22 +362,28 @@   , taskQ :: Chan (InterpTask m a)   , numTasks :: TVar Natural   , stepper :: Stepper Symbolic (Expr End)-  , handler :: Expr End -> m a+  , handler :: PathHandler m a+  , shouldAbort :: TVar Bool   }  data Process m a = Process   { result :: Expr End-  , handler :: Expr End -> m a+  , handler :: PathHandler m a   } +-- returns back the input path/branch of the program+noopPathHandler :: Applicative m => PathHandler m (Expr End)+noopPathHandler x _ = pure x+ interpret :: forall m a . App m   => Fetch.Fetcher Symbolic m   -> IterConfig   -> VM Symbolic   -> Stepper Symbolic (Expr End)-  -> (Expr End -> m a)+  -> PathHandler m a   -> m [a] interpret fetcher iterConf vm stepper handler = do+  shouldAbort <- liftIO $ newTVarIO False   conf <- readConfig   taskQ <- liftIO newChan   processQ <- liftIO newChan@@ -363,11 +405,11 @@   allProcessDone <- liftIO newEmptyTMVarIO    -- spawn task orchestration thread-  taskOrchestrate' <- toIO $ taskOrchestrate taskQ availableInstances processQ numTasks numProcs+  taskOrchestrate' <- toIO $ taskOrchestrate taskQ shouldAbort availableInstances processQ numTasks numProcs   taskOrchestrateId <- liftIO $ forkIO taskOrchestrate'    -- spawn processing orchestration thread-  processOrchestrate' <- toIO $ processOrchestrate processQ availableProcs resChan numProcs numTasks allProcessDone+  processOrchestrate' <- toIO $ processOrchestrate processQ shouldAbort availableProcs resChan numProcs numTasks allProcessDone   processOrchestrateId <- liftIO $ forkIO processOrchestrate'    -- Add in the first task, further tasks will be added by the interpreters themselves@@ -379,6 +421,7 @@         , numTasks = numTasks         , stepper = stepper         , handler = handler+        , shouldAbort = shouldAbort         }   liftIO $ writeChan taskQ interpTask @@ -394,30 +437,41 @@     -- orchestrator loop     taskOrchestrate :: App m       => Chan (InterpTask m a)+      -> TVar Bool       -> Chan () -> Chan (Process m a)-      -> TVar Natural -> TVar Natural -> m b-    taskOrchestrate taskQ avail processQ numTasks numProcs = forever $ do+      -> TVar Natural -> TVar Natural -> m ()+    taskOrchestrate taskQ shouldAbort avail processQ numTasks numProcs = forever $ do       _ <- liftIO $ readChan avail       task <- liftIO $ readChan taskQ-      runTask' <- toIO $ getOneExpr task avail processQ numTasks numProcs-      liftIO $ forkIO runTask'+      abortFlag <- liftIO $ readTVarIO shouldAbort+      if abortFlag+        then liftIO $ writeChan avail ()+        else do+          runTask' <- toIO $ getOneExpr task avail processQ numTasks numProcs+          void $ liftIO $ forkIO runTask'      -- processing orchestrator loop-    processOrchestrate :: App m => Chan (Process m a) -> Chan () -> TChan a -> TVar Natural -> TVar Natural -> TMVar () -> m b-    processOrchestrate processQ availProcessors resChan numProcs numTasks allProcessDone = forever $ do-      _ <- liftIO $ readChan availProcessors+    processOrchestrate :: App m+      => Chan (Process m a) -> TVar Bool -> Chan () -> TChan a+      -> TVar Natural -> TVar Natural -> TMVar () -> m ()+    processOrchestrate processQ shouldAbort avail resChan numProcs numTasks allProcessDone = forever $ do+      _ <- liftIO $ readChan avail       proc <- liftIO $ readChan processQ-      runProcess' <- toIO $ processOne proc availProcessors resChan numProcs numTasks allProcessDone-      liftIO $ forkIO runProcess'+      abortFlag <- liftIO $ readTVarIO shouldAbort+      if abortFlag+        then liftIO $ writeChan avail ()+        else do+          runProcess' <- toIO $ processOne proc shouldAbort avail resChan numProcs numTasks allProcessDone+          void $ liftIO $ forkIO runProcess'      -- process one task-    processOne :: App m => Process m a -> Chan () -> TChan a -> TVar Natural -> TVar Natural -> TMVar () -> m ()-    processOne task availProcs resChan numProcs numTasks allProcessDone = do-      processed <- task.handler task.result+    processOne :: App m => Process m a -> TVar Bool -> Chan () -> TChan a -> TVar Natural -> TVar Natural -> TMVar () -> m ()+    processOne task shouldAbort avail resChan numProcs numTasks allProcessDone = do+      processed <- task.handler task.result shouldAbort       liftIO . atomically $ writeTChan resChan processed        -- Return instance to pool immediately after processing-      liftIO $ writeChan availProcs ()+      liftIO $ writeChan avail ()        -- Decrement and check if all done       liftIO $ atomically $ do@@ -483,9 +537,12 @@           runOne frozen newDepth (v:rest) = do             conf <- readConfig             (ra, vma) <- liftIO $ stToIO $ runStateT (continue v) frozen { result = Nothing, exploreDepth = newDepth }-            liftIO $ atomically $ modifyTVar numTasks (+1)-            when (conf.debug && conf.verb >=2) $ liftIO $ putStrLn $ "Queuing new task for ForkMany at depth " <> show newDepth-            liftIO $ writeChan taskQ t { vm = vma, stepper = (k ra) }+            -- Check abort flag before queuing new task+            abortFlag <- liftIO $ readTVarIO shouldAbort+            unless abortFlag $ do+              liftIO $ atomically $ modifyTVar numTasks (+1)+              when (conf.debug && conf.verb >=2) $ liftIO $ putStrLn $ "Queuing new task for ForkMany at depth " <> show newDepth+              liftIO $ writeChan taskQ t { vm = vma, stepper = (k ra) }             runOne frozen newDepth rest           runOne _ _ [] = internalError "unreachable"       Stepper.Fork (PleaseRunBoth continue) -> do@@ -493,9 +550,12 @@         frozen <- liftIO $ stToIO $ freezeVM vm         let newDepth = vm.exploreDepth+1         (ra, vma) <- liftIO $ stToIO $ runStateT (continue True) frozen { result = Nothing, exploreDepth = newDepth }-        liftIO $ atomically $ modifyTVar numTasks (+1)-        liftIO $ writeChan taskQ $ t { vm = vma, stepper = (k ra) }-        when (conf.debug && conf.verb >= 2) $ liftIO $ putStrLn $ "Queued new task for Fork at depth " <> show newDepth+        -- Check abort flag before queuing new task+        abortFlag <- liftIO $ readTVarIO shouldAbort+        unless abortFlag $ do+          liftIO $ atomically $ modifyTVar numTasks (+1)+          liftIO $ writeChan taskQ $ t { vm = vma, stepper = (k ra) }+          when (conf.debug && conf.verb >= 2) $ liftIO $ putStrLn $ "Queued new task for Fork at depth " <> show newDepth          (rb, vmb) <- liftIO $ stToIO $ runStateT (continue False) frozen { result = Nothing, exploreDepth = newDepth }         when (conf.debug && conf.verb >=2) $ liftIO $ putStrLn $ "Continuing task for Fork at depth " <> show newDepth@@ -608,12 +668,12 @@   -> m [Expr End] getExprEmptyStore solvers c signature' concreteArgs opts = do   conf <- readConfig-  calldata <- mkCalldata signature' concreteArgs+  (calldata, _caveats) <- mkCalldata signature' concreteArgs   preState <- liftIO $ stToIO $ loadEmptySymVM (RuntimeCode (ConcreteRuntimeCode c)) (Lit 0) calldata-  paths <- interpret (Fetch.oracle solvers Nothing opts.rpcInfo) opts.iterConf preState runExpr pure+  paths <- interpret (Fetch.oracle solvers Nothing opts.rpcInfo) opts.iterConf preState runExpr noopPathHandler   if conf.simp then (pure $ map Expr.simplify paths) else pure paths --- Used only in testing+-- Used only in testing; TODO: unify with exploreContract, and keep only one getExpr   :: App m   => SolverGroup@@ -623,10 +683,8 @@   -> VeriOpts   -> m [Expr End] getExpr solvers c signature' concreteArgs opts = do+  paths <- exploreContract solvers c signature' concreteArgs opts Nothing   conf <- readConfig-  calldata <- mkCalldata signature' concreteArgs-  preState <- liftIO $ stToIO $ abstractVM calldata c Nothing False-  paths <- interpret (Fetch.oracle solvers Nothing opts.rpcInfo) opts.iterConf preState runExpr pure   if conf.simp then (pure $ map Expr.simplify paths) else pure paths  {- | Checks if an assertion violation has been encountered@@ -668,19 +726,23 @@ panicMsg err = selector "Panic(uint256)" <> encodeAbiValue (AbiUInt 256 err)  -- | Builds a buffer representing calldata from the provided method description--- and concrete arguments-mkCalldata :: App m => Maybe Sig -> [String] -> m (Expr Buf, [Prop])+-- and concrete arguments, together with any program-wide soundness caveats+-- incurred while building it (see 'Caveat').+mkCalldata :: App m => Maybe Sig -> [String] -> m ((Expr Buf, [Prop]), [Caveat]) mkCalldata Nothing _ = do   conf <- readConfig-  pure ( AbstractBuf "txdata"-       -- assert that the length of the calldata is never more than 2^64-       -- this is way larger than would ever be allowed by the gas limit-       -- and avoids spurious counterexamples during abi decoding-       -- TODO: can we encode calldata as an array with a smaller length?-       , [Expr.bufLength (AbstractBuf "txdata") .< (Lit (2 ^ conf.maxBufSize))]+  pure ( ( AbstractBuf "txdata"+         -- assert that the length of the calldata is never more than 2^64+         -- this is way larger than would ever be allowed by the gas limit+         -- and avoids spurious counterexamples during abi decoding+         -- TODO: can we encode calldata as an array with a smaller length?+         , [Expr.bufLength (AbstractBuf "txdata") .< (Lit (2 ^ conf.maxBufSize))]+         )+       , []        )-mkCalldata (Just (Sig name types)) args =-  symCalldata name types args (AbstractBuf "txdata")+mkCalldata (Just (Sig name types)) args = do+  (buf, props, caveats) <- symCalldata name types args (AbstractBuf "txdata")+  pure ((buf, props), caveats)  -- Used only in testing verifyContract :: forall m . App m@@ -693,7 +755,7 @@   -> Postcondition   -> m ([Expr End], [VerifyResult]) verifyContract solvers theCode signature' concreteArgs opts maybepre post = do-  calldata <- mkCalldata signature' concreteArgs+  (calldata, _caveats) <- mkCalldata signature' concreteArgs   preState <- liftIO $ stToIO $ abstractVM calldata theCode maybepre False   let fetcher = Fetch.oracle solvers Nothing opts.rpcInfo   verify solvers fetcher opts preState post Nothing@@ -708,10 +770,10 @@   -> Maybe Precondition   -> m [Expr End] exploreContract solvers theCode signature' concreteArgs opts maybepre = do-  calldata <- mkCalldata signature' concreteArgs+  (calldata, _caveats) <- mkCalldata signature' concreteArgs   preState <- liftIO $ stToIO $ abstractVM calldata theCode maybepre False   let fetcher = Fetch.oracle solvers Nothing opts.rpcInfo-  executeVM fetcher opts.iterConf preState pure+  executeVM fetcher opts.iterConf preState noopPathHandler  -- | Stepper that parses the result of Stepper.runFully into an Expr End runExpr :: Stepper.Stepper Symbolic (Expr End)@@ -776,7 +838,7 @@   -- | Symbolically execute the VM and return the representention of the execution-executeVM :: forall m a . App m => Fetch.Fetcher Symbolic m -> IterConfig -> VM Symbolic -> (Expr End -> m a) -> m [a]+executeVM :: forall m a . App m => Fetch.Fetcher Symbolic m -> IterConfig -> VM Symbolic -> (Expr End -> TVar Bool -> m a) -> m [a] executeVM fetcher iterConfig preState handlePath = interpret fetcher iterConfig preState runExpr handlePath  -- | Symbolically execute the VM and check all endstates against the@@ -821,7 +883,7 @@     putStrLn $ "   Keccak preimages in state: " <> (show $ length preState.keccakPreImgs)     putStrLn $ "   Exploring call " <> call -  results <- executeVM fetcher opts.iterConf preState $ \leaf -> do+  results <- executeVM fetcher opts.iterConf preState $ \leaf shouldAbort -> do     -- Extract partial if applicable     let mPartial = case leaf of           Partial _ _ p -> Just (p, leaf)@@ -835,7 +897,10 @@         when (conf.debug && conf.verb >=2) $ liftIO $ putStrLn $ "   Checking leaf with props: " <> show props <> " SMT result: " <> show res         -- Call custom handler if provided (for immediate Cex processing/validation/printing)         case (cexHandler, res) of-          (Just handler, cex@(Cex _)) -> handler preState cex leaf+          (Just handler, cex@(Cex _)) -> do+            handler preState cex leaf+            when conf.earlyAbort $ liftIO $ atomically $ writeTVar shouldAbort True+          (_, (Cex _)) -> when conf.earlyAbort $ liftIO $ atomically $ writeTVar shouldAbort True           _ -> pure ()         pure (res, leaf)       else pure (Qed, leaf)@@ -846,7 +911,7 @@     putStrLn $ "   Exploration and solving finished, " <> show (length results) <> " branch(es) checked in call " <> call <> " of which partial: "                 <> show (length smtResults)     let cexs = filter (\(res, _) -> not . isQed $ res) smtResults-    putStrLn $ "   Found " <> show (length cexs) <> " counterexample(s) in call " <> call+    putStrLn $ "   Found " <> show (length cexs) <> " potential counterexample(s) in call " <> call    pure (smtResults, catMaybes partials)   where@@ -908,7 +973,7 @@   conf <- readConfig   case bytecodeA == bytecodeB of     True -> liftIO $ do-      putStrLn "bytecodeA and bytecodeB are identical"+      when conf.debug $ putStrLn "bytecodeA and bytecodeB are identical"       pure mempty     False -> do       when conf.debug $ liftIO $ do@@ -933,7 +998,7 @@     getBranches bs = do       let bytecode = if BS.null bs then BS.pack [0] else bs       prestate <- liftIO $ stToIO $ abstractVM calldata bytecode Nothing create-      interpret (Fetch.oracle solvers sess Fetch.noRpc) opts.iterConf prestate runExpr pure+      interpret (Fetch.oracle solvers sess Fetch.noRpc) opts.iterConf prestate runExpr noopPathHandler     filterQeds (EqIssues res partials) = EqIssues (filter (\(r, _) -> not . isQed $ r) res) partials  rewriteFresh :: Text -> [Expr a] -> [Expr a]@@ -956,7 +1021,7 @@         liftIO $ printPartialIssues branchesB "codeB"        let allPairs = [(a,b) | a <- branchesA, b <- branchesB]-      liftIO $ putStrLn $ "Found " <> show (length allPairs) <> " total pairs of endstates"+      when conf.debug $ liftIO $ putStrLn $ "Found " <> show (length allPairs) <> " total pairs of endstates"        when conf.dumpEndStates $ liftIO $         putStrLn $ "endstates in bytecodeA: " <> show (length branchesA)@@ -965,9 +1030,9 @@       ps <- forM allPairs $ uncurry distinct       let differingEndStates = sortBySize $ mapMaybe (view _1) ps       let knownIssues = foldr ((<>) . (view _2)) mempty ps-      liftIO $ putStrLn $ "Asking the SMT solver for " <> (show $ length differingEndStates) <> " pairs"-      when conf.dumpEndStates $ forM_ (zip differingEndStates [(1::Integer)..]) (\(x, i) ->-        liftIO $ T.writeFile ("prop-checked-" <> show i <> ".prop") (T.pack $ show x))+      when conf.debug $ liftIO $ putStrLn $ "Asking the SMT solver for " <> (show $ length differingEndStates) <> " pairs"+      when conf.dumpEndStates $ forM_ (zip differingEndStates [(1::Integer)..]) (\((props, msg), i) ->+        liftIO $ T.writeFile ("prop-checked-" <> show i <> ".prop") (T.pack $ show props <> msg))        procs <- liftIO getNumProcessors       newDifferences <- checkAll differingEndStates procs@@ -1079,7 +1144,7 @@               <> " with constraints: " <> (T.unpack . T.unlines $ map formatProp aProps)             liftIO $ putStrLn $ "create deployed code B: " <> bsToHex codeB               <> " with constraints: " <> (T.unpack . T.unlines $ map formatProp bProps)-          calldata <- mkCalldata Nothing []+          (calldata, _) <- mkCalldata Nothing []           equivalenceCheck solvers sess codeA codeB defaultVeriOpts calldata False         _ -> internalError $ "Symbolic code returned from constructor." <> " A: " <> show simpA <> " B: " <> show simpB 
src/EVM/Transaction.hs view
@@ -1,6 +1,6 @@ module EVM.Transaction where -import EVM (initialContract, ceilDiv)+import EVM (initialContract, ceilDiv, collision) import EVM.Expr qualified as Expr import EVM.FeeSchedule import EVM.Format (hexText)@@ -34,10 +34,14 @@   = LegacyTransaction   | AccessListTransaction   | EIP1559Transaction+  | EIP4844Transaction+  | EIP7702Transaction   deriving (Show, Eq, Generic)  instance JSON.ToJSON TxType where   toJSON t = case t of+               EIP7702Transaction    -> "0x4" -- permanently sets the code for an EOA+               EIP4844Transaction    -> "0x3" -- Proto-Danksharding                EIP1559Transaction    -> "0x2" -- EIP1559                LegacyTransaction     -> "0x1" -- EIP2718                AccessListTransaction -> "0x1" -- EIP2930@@ -107,6 +111,7 @@   where hash = keccak' (signingData tx)         v    = tx.v         v'   = if v == 27 || v == 28 then v+               else if v >= 35 then 28 - (v `mod` 2) -- EIP155                else 27 + v  sign :: Integer -> Transaction -> Transaction@@ -123,6 +128,8 @@       else normalData     AccessListTransaction -> eip2930Data     EIP1559Transaction -> eip1559Data+    EIP4844Transaction -> eip4844Data+    EIP7702Transaction -> eip7702Data   where v          = tx.v         to'        = case tx.toAddr of           Just a  -> BS $ word160Bytes a@@ -165,7 +172,6 @@           , BS tx.txdata           , rlpAccessList           ]-         eip2930Data = cons 0x01 $ rlpList           [ rlpWord256 tx.chainId           , rlpWord256 tx.nonce@@ -176,6 +182,31 @@           , BS tx.txdata           , rlpAccessList           ]+        eip4844Data = cons 0x03 $ rlpList+          [ rlpWord256 tx.chainId+          , rlpWord256 tx.nonce+          , rlpWord256 maxPrio+          , rlpWord256 maxFee+          , rlpWord256 (into tx.gasLimit)+          , to'+          , rlpWord256 tx.value+          , BS tx.txdata+          , rlpAccessList+          , rlpWord256 $ undefined -- TODO EIP4844 max_fee_per_blob_gas+          , undefined -- TODO EIP4844 blob_versioned_hashes+          ]+        eip7702Data = cons 0x04 $ rlpList+          [ rlpWord256 tx.chainId+          , rlpWord256 tx.nonce+          , rlpWord256 maxPrio+          , rlpWord256 maxFee+          , rlpWord256 (into tx.gasLimit)+          , to'+          , rlpWord256 tx.value+          , BS tx.txdata+          , rlpAccessList+          , undefined -- TODO EIP4844 authorization_list+          ]  accessListPrice :: FeeSchedule Word64 -> [AccessListEntry] -> Word64 accessListPrice fs al =@@ -198,6 +229,20 @@       initcodeCost = fs.g_initcodeword * unsafeInto (ceilDiv (BS.length calldata) 32)   in baseCost + zeroCost * (unsafeInto zeroBytes) + nonZeroCost * (unsafeInto nonZeroBytes) +-- | EIP-7623: Calculate floor gas cost based on calldata tokens+-- tokens = zero_bytes + (nonzero_bytes * 4)+-- floor_gas = 21000 + 10 * tokens+-- then gas_used = max(txGasCost, floor_gas)+txdataFloorGas :: FeeSchedule Word64 -> Transaction -> Word64+txdataFloorGas fs tx =+  let calldata     = tx.txdata+      zeroBytes    = BS.count 0 calldata+      nonZeroBytes = BS.length calldata - zeroBytes+      baseCost     = fs.g_transaction+      floorCost    = fs.g_txdatafloor+      tokens       = unsafeInto zeroBytes + unsafeInto nonZeroBytes * 4+  in baseCost + floorCost * tokens+ instance FromJSON AccessListEntry where   parseJSON (JSON.Object val) = do     accessAddress_ <- addrField val "address"@@ -219,7 +264,7 @@     toAddr   <- addrFieldMaybe val "to"     v        <- wordField val "v"     value    <- wordField val "value"-    txType   <- fmap (read :: String -> Int) <$> (val JSON..:? "type")+    txType   <- fmap (read :: String -> Int) <$> val JSON..:? "type"     case txType of       Just 0x00 -> pure $ Transaction tdata gasLimit gasPrice nonce r s toAddr v value LegacyTransaction [] Nothing Nothing 1       Just 0x01 -> do@@ -228,6 +273,14 @@       Just 0x02 -> do         accessListEntries <- (val JSON..: "accessList") >>= parseJSONList         pure $ Transaction tdata gasLimit gasPrice nonce r s toAddr v value EIP1559Transaction accessListEntries maxPrio maxFee 1+      Just 0x03 -> do+        accessListEntries <- (val JSON..: "accessList") >>= parseJSONList+        -- TODO: capture max_fee_per_blob_gas and blob_versioned_hashes EIP4844+        pure $ Transaction tdata gasLimit gasPrice nonce r s toAddr v value EIP4844Transaction accessListEntries maxPrio maxFee 1+      Just 0x04 -> do+        accessListEntries <- (val JSON..: "accessList") >>= parseJSONList+        -- TODO: capture authorization_list EIP7702+        pure $ Transaction tdata gasLimit gasPrice nonce r s toAddr v value EIP7702Transaction accessListEntries maxPrio maxFee 1       Just _ -> fail "unrecognized custom transaction type"       Nothing -> pure $ Transaction tdata gasLimit gasPrice nonce r s toAddr v value LegacyTransaction [] Nothing Nothing 1   parseJSON invalid =@@ -267,13 +320,21 @@     preState = setupTx origin coinbase gasPrice gasLimit vm.env.contracts     oldBalance = view (accountAt toAddr % #balance) preState     creation = vm.tx.isCreate-    initState =-        ((Map.adjust (over #balance (`Expr.sub` value))) origin)-      . (Map.adjust (over #balance (Expr.add value))) toAddr-      . (if creation-         then Map.insert toAddr (toContract & (set #balance oldBalance))-         else touchAccount toAddr)-      $ preState+    -- Check for collision at target address for CREATE transactions+    hasCollision = creation && collision (Map.lookup toAddr preState)+    -- For collision: don't transfer value, don't create contract+    initState = if hasCollision+      then touchAccount toAddr preState+      else ((Map.adjust (over #balance (`Expr.sub` value))) origin)+         . (Map.adjust (over #balance (Expr.add value))) toAddr+         . (if creation+            then Map.insert toAddr (toContract & (set #balance oldBalance))+            else touchAccount toAddr)+         $ preState+    applyWhen p f x = if p then f x else x   in     vm & #env % #contracts .~ initState        & #tx % #txReversion .~ preState+       -- For collision: set code to empty so exec1 immediately stops and calls finalize+       -- (don't set #result directly, as that bypasses finalize which handles gas payment)+       & applyWhen hasCollision (#state % #code .~ RuntimeCode (ConcreteRuntimeCode ""))
src/EVM/Traversals.hs view
@@ -123,6 +123,7 @@       e@(SGT a b) -> f e <> (go a) <> (go b)       e@(Eq a b) -> f e <> (go a) <> (go b)       e@(IsZero a) -> f e <> (go a)+      e@(ITE c t el) -> f e <> (go c) <> (go t) <> (go el)        -- bits @@ -133,11 +134,11 @@       e@(SHL a b) -> f e <> (go a) <> (go b)       e@(SHR a b) -> f e <> (go a) <> (go b)       e@(SAR a b) -> f e <> (go a) <> (go b)+      e@(CLZ a) -> f e <> (go a)        -- Hashes        e@(Keccak a) -> f e <> (go a)-      e@(SHA256 a) -> f e <> (go a)        -- block context @@ -448,6 +449,11 @@   IsZero a -> do     a' <- mapExprM f a     f (IsZero a')+  ITE c t el -> do+    c' <- mapExprM f c+    t' <- mapExprM f t+    el' <- mapExprM f el+    f (ITE c' t' el')    -- bits @@ -478,6 +484,9 @@     a' <- mapExprM f a     b' <- mapExprM f b     f (SAR a' b')+  CLZ a -> do+    a' <- mapExprM f a+    f (CLZ a')     -- Hashes@@ -485,10 +494,6 @@   Keccak a -> do     a' <- mapExprM f a     f (Keccak a')--  SHA256 a -> do-    a' <- mapExprM f a-    f (SHA256 a')    -- block context 
src/EVM/Types.hs view
@@ -259,6 +259,9 @@   Eq             :: Expr EWord -> Expr EWord -> Expr EWord   IsZero         :: Expr EWord -> Expr EWord +  -- conditional (if-then-else for path merging)+  ITE            :: Expr EWord -> Expr EWord -> Expr EWord -> Expr EWord+   -- bits    And            :: Expr EWord -> Expr EWord -> Expr EWord@@ -268,11 +271,11 @@   SHL            :: Expr EWord -> Expr EWord -> Expr EWord   SHR            :: Expr EWord -> Expr EWord -> Expr EWord   SAR            :: Expr EWord -> Expr EWord -> Expr EWord+  CLZ            :: Expr EWord -> Expr EWord    -- Hashes    Keccak         :: Expr Buf -> Expr EWord-  SHA256         :: Expr Buf -> Expr EWord    -- block context @@ -558,6 +561,7 @@   | NonceOverflow   | BadCheatCode String FunctionSelector   | NonexistentFork Int+  | AssumeCheatFailed   deriving (Show, Eq, Ord)  evmErrToString :: EvmError -> String@@ -596,6 +600,15 @@   | BranchTooDeep         { pc :: Int, addr :: Expr EAddr}   deriving (Show, Eq, Ord) +-- | A program-wide soundness caveat. Unlike a 'PartialExec', a caveat does not+-- mark an execution path that we failed to explore: every reachable path was+-- explored fully. It records that the *space of inputs* was restricted, so the+-- result is only valid within that restriction. It is therefore attached to the+-- verification result as a whole, not to an individual 'Expr End' leaf.+data Caveat+  = DynArgBounded { maxSize :: Int }  -- ^ dynamic (bytes/string) calldata args were bounded to this many bytes+  deriving (Show, Eq, Ord)+ -- | Effect types used by the vm implementation for side effects & control flow data Effect t where   Query :: Query t -> Effect t@@ -662,6 +675,36 @@  -- VM State ---------------------------------------------------------------------------------------- +-- | State tracking for speculative merge execution+data MergeState = MergeState+  { msActive          :: Bool   -- ^ Inside speculative execution+  , msRemainingBudget :: Int    -- ^ Instructions remaining in budget+  } deriving (Show, Eq, Generic)++defaultMergeState :: MergeState+defaultMergeState = MergeState False 0++-- | An active vm.expectRevert / vm.expectPartialRevert expectation set by a+-- cheat call. Consumed when the matching frame returns/reverts.+data ExpectedRevert = ExpectedRevert+  { reason         :: Maybe (Expr Buf)+  -- ^ Nothing matches any revert data; Just expected matches concrete bytes.+  , depth          :: Int+  -- ^ length of vm.frames at the cheat call. The matching boundary is the+  -- frame popping back down to this depth (i.e. the next outer subcall).+  , partialMatch   :: Bool+  -- ^ True for expectPartialRevert (compares only first 4 bytes of actual).+  , reverter       :: Maybe (Expr EAddr)+  -- ^ Nothing matches any reverter; Just want enforces actualReverter equals+  -- this address.+  , actualReverter :: Maybe (Expr EAddr)+  -- ^ The first contract observed reverting in a non-CREATE frame after the+  -- expectation was set. Captured on the first FrameReverted that arrives in a+  -- CALL frame; subsequent reverts (bubble-ups) do not overwrite it. Used at+  -- the matching boundary to compare against the expected reverter.+  }+  deriving (Show, Generic)+ data VMType = Symbolic | Concrete  type family Gas (t :: VMType) = r | r -> t where@@ -686,6 +729,7 @@   , config         :: RuntimeConfig   , forks          :: Seq ForkState   , currentFork    :: Int+  , srcLookup      :: Maybe SrcLookup   , labels         :: Map Addr Text   , osEnv          :: Map String String   , freshVar       :: Int@@ -693,6 +737,9 @@   --   during symbolic execution. See e.g. OpStaticcall   , exploreDepth   :: Int   , keccakPreImgs  :: Set (ByteString, W256)+  , mergeState     :: MergeState+  , expectedRevert :: Maybe ExpectedRevert+  -- ^ Active expectRevert/expectPartialRevert expectation, if any.   }   deriving (Generic) @@ -716,6 +763,19 @@   | AbstractBase   deriving (Show) +-- | A callback for looking up source location info given the contracts map,+-- an address, and a PC+newtype SrcLookup = SrcLookup (Map (Expr EAddr) Contract -> Expr EAddr -> Int -> String)++instance Show SrcLookup where+  show _ = "<SrcLookup Info>"++-- | Run a SrcLookup to get source location info, with a fallback for when+-- no SrcLookup is available.+runSrcLookup :: Maybe SrcLookup -> Map (Expr EAddr) Contract -> Expr EAddr -> Int -> String+runSrcLookup Nothing _ addr pc = " at addr: " <> show addr <> " at pc: " <> show pc+runSrcLookup (Just (SrcLookup f)) contracts addr pc = f contracts addr pc+ -- | Configuration options that need to be consulted at runtime data RuntimeConfig = RuntimeConfig   { allowFFI :: Bool@@ -809,6 +869,7 @@   , subState    :: SubState   , isCreate    :: Bool   , txReversion :: Map (Expr EAddr) Contract+  , txdataFloorGas :: Word64   }   deriving (Show) @@ -945,6 +1006,7 @@   | ErrorTrace EvmError   | EntryTrace Text   | ReturnTrace (Expr Buf) FrameContext+  | ConsoleLog (Expr Buf)   deriving (Eq, Ord, Show, Generic)  -- | Wrapper type containing vm traces and the context needed to pretty print them properly@@ -992,6 +1054,8 @@   , allowFFI :: Bool   , freshAddresses :: Int   , beaconRoot :: W256+  , parentHash :: W256      -- EIP-2935 parent block hash+  , txdataFloorGas :: Word64   }  deriving instance Show (VMOpts Symbolic)@@ -1030,6 +1094,7 @@   | OpShl   | OpShr   | OpSar+  | OpClz   | OpSha3   | OpAddress   | OpBalance@@ -1214,6 +1279,9 @@ newtype FunctionSelector = FunctionSelector { unFunctionSelector :: Word32 }   deriving (Bits, Num, Eq, Ord, Real, Enum, Integral) instance Show FunctionSelector where show s = "0x" <> showHex s ""+instance Read FunctionSelector where+  readsPrec _ ('0':'x':s) = first FunctionSelector <$> readHex s+  readsPrec _ s = first FunctionSelector <$> readHex s   -- ByteString wrapper ------------------------------------------------------------------------------
src/EVM/UnitTest.hs view
@@ -11,7 +11,6 @@ import EVM.Effects import EVM.Exec import EVM.Expr qualified as Expr-import EVM.FeeSchedule (feeSchedule) import EVM.Fetch qualified as Fetch import EVM.Format import EVM.Solidity@@ -23,7 +22,7 @@ import EVM.Tracing qualified as Tracing import EVM.Expr (maybeLitWordSimp) -import Control.Monad (void, when, forM, forM_)+import Control.Monad (void, when, unless, forM, forM_) import Control.Monad.ST (RealWorld, ST, stToIO) import Control.Monad.State.Strict (execState, get, put, liftIO, runStateT) import Optics.Core@@ -48,6 +47,8 @@ import System.IO (hFlush, stdout) import Witch (unsafeInto, into) import Data.Vector qualified as V+import Data.Vector.Storable qualified as VS+import Data.Sequence qualified as Seq import Data.Char (ord)  data UnitTestOptions = UnitTestOptions@@ -57,8 +58,7 @@   , maxIter       :: Maybe Integer   , askSmtIters   :: Integer   , smtTimeout    :: Maybe Natural-  , match         :: Text-  , prefix        :: Text+  , methodFilter  :: TestMethodFilter   , dapp          :: DappInfo   , testParams    :: TestVMParams   , ffiAllowed    :: Bool@@ -121,15 +121,14 @@ -- | Returns tuple of (No Cex, No warnings) unitTest :: App m => UnitTestOptions -> BuildOutput -> m (Bool, Bool) unitTest opts bo@(BuildOutput (Contracts cs) _) = do-  let unitTestContrs = findUnitTests opts.prefix opts.match $ Map.elems cs+  let unitTestContrs = [(c, methods) | c <- Map.elems cs, let methods = findUnitTests opts.methodFilter c, not (null methods)]   conf <- readConfig   when conf.debug $ liftIO $ do     putStrLn $ "Found " ++ show (length unitTestContrs) ++ " unit test contract(s) to test:"-    let x = map (\(a,b) -> "  --> " <> a <> "  ---  functions: " <> (Text.pack $ show b)) unitTestContrs+    let x = map (\(a,b) -> "  --> " <> a.contractName <> "  ---  functions: " <> (Text.pack $ show b)) unitTestContrs     putStrLn $ unlines $ map Text.unpack x-  results <- concatMapM (runUnitTestContract opts bo) unitTestContrs-  when conf.debug $ liftIO $ putStrLn $ "unitTest individual results: " <> show results-  let (firsts, seconds) = unzip results+  results <- mapM (runUnitTestContract opts bo) unitTestContrs+  let (firsts, seconds) = unzip $ concat results   pure (and firsts, and seconds)  -- | Assuming a constructor is loaded, this stepper will run the constructor@@ -205,33 +204,29 @@   :: App m   => UnitTestOptions   -> BuildOutput-  -> (Text, [Sig])+  -> (SolcContract, [Sig])   -> m [(Bool, Bool)] runUnitTestContract-  opts@(UnitTestOptions {..}) buildOut (name, testSigs) = do-  liftIO $ putStrLn $ "Checking " ++ show (length testSigs) ++ " function(s) in contract " ++ unpack name--  -- Look for the wanted contract by name from the Solidity info-  case Map.lookup name (getContractsMap buildOut.contracts) of-    Nothing -> internalError $ "Contract " ++ unpack name ++ " not found"-    Just solcContr -> do-      -- Construct the initial VM and begin the contract's constructor-      vm0 :: VM Concrete <- liftIO $ stToIO $ initialUnitTestVm opts solcContr-      vm1 <- Stepper.interpret (Fetch.oracle solvers (Just sess) rpcInfo) vm0 $ do-        Stepper.enter name-        initializeUnitTest opts solcContr-        Stepper.evm get+  opts@(UnitTestOptions {..}) buildOut (contract, testSigs) = do+  unless (Map.member contract.contractName (getContractsMap buildOut.contracts)) $ internalError $ "Contract " ++ unpack contract.contractName ++ " not found"+  liftIO $ putStrLn $ "Checking " ++ show (length testSigs) ++ " function(s) in contract " ++ unpack contract.contractName+  -- Construct the initial VM and begin the contract's constructor+  vm0 :: VM Concrete <- liftIO $ stToIO $ initialUnitTestVm opts contract+  vm1 <- Stepper.interpret (Fetch.oracle solvers (Just sess) rpcInfo) vm0 $ do+    Stepper.enter contract.contractName+    initializeUnitTest opts contract+    Stepper.evm get -      writeTraceDapp dapp vm1-      failOut <- failOutput vm1 opts "setUp()"-      case vm1.result of-        Just (VMFailure _) -> liftIO $ do-          Text.putStrLn "   \x1b[31m[BAIL]\x1b[0m setUp() "-          tick $ indentLines 3 failOut-          pure [(True, False)]-        Just (VMSuccess _) -> do-          forM testSigs $ \s -> symRun opts vm1 s solcContr buildOut.sources-        _ -> internalError "setUp() did not end with a result"+  writeTraceDapp dapp vm1+  failOut <- failOutput vm1 opts "setUp()"+  case vm1.result of+    Just (VMFailure _) -> liftIO $ do+      Text.putStrLn "   \x1b[31m[BAIL]\x1b[0m setUp() "+      tick $ indentLines 3 failOut+      pure [(True, False)]+    Just (VMSuccess _) -> do+      forM testSigs $ \s -> symRun opts vm1 s buildOut.sources+    _ -> internalError "setUp() did not end with a result"  dsTestFailedSym :: Map (Expr 'EAddr) (Expr EContract) -> VM t -> Prop dsTestFailedSym store vm =@@ -247,11 +242,12 @@  -- Define the thread spawner for symbolic tests -- Returns tuple of (No Cex, No warnings)-symRun :: forall m . App m => UnitTestOptions -> VM Concrete -> Sig -> SolcContract -> SourceCache -> m (Bool, Bool)-symRun opts@UnitTestOptions{..} vm sig@(Sig testName types) solcContr sourceCache = do+symRun :: forall m . App m => UnitTestOptions -> VM Concrete -> Sig -> SourceCache -> m (Bool, Bool)+symRun opts@UnitTestOptions{..} vm sig@(Sig testName types) sourceCache = do     let cs = callSig sig     liftIO $ putStrLn $ "\x1b[96m[RUNNING]\x1b[0m " <> Text.unpack cs-    cd <- symCalldata cs types [] (AbstractBuf "txdata")+    (cdBuf, cdProps, caveats) <- symCalldata cs types [] (AbstractBuf "txdata")+    let cd = (cdBuf, cdProps)     let shouldFail = "proveFail" `isPrefixOf` cs      -- define postcondition depending on `shouldFail`@@ -285,7 +281,8 @@      -- check postconditions against vm     let fetcherSym = Fetch.oracle solvers (Just sess) rpcInfo-    (ends, results) <- verify solvers fetcherSym (makeVeriOpts opts) (symbolify vm') postcondition (Just $ cexHandler cd fetcherConc)+    let symVm = symbolify vm' & set #srcLookup (Just $ makeSrcLookup dapp sourceCache)+    (ends, results) <- verify solvers fetcherSym (makeVeriOpts opts) symVm postcondition (Just $ cexHandler cd fetcherConc)     conf <- readConfig     when (conf.debug) $ liftIO $ do       putStrLn $ "   \x1b[94m[EXPLORATION COMPLETE]\x1b[0m " <> Text.unpack testName <> " -- explored " <> show (length ends) <> " paths."@@ -298,7 +295,11 @@     when (conf.debug && conf.verb >=2) $ liftIO $ do       putStrLn $ "Collected END-s:\n" <> prettyvmresults ends       putStrLn $ "Collected verification results: " <> show results-    let warnings = any Expr.isPartial ends || any isUnknown results || any isError results+    -- Caveats (e.g. dynamic args bounded to maxDynSize) restrict the input+    -- domain rather than leaving a path unexplored, so they are not Partials;+    -- they still count as a warning so a bounded run is not reported as a clean+    -- pass (avoids a false sense of security).+    let warnings = any Expr.isPartial ends || any isUnknown results || any isError results || not (null caveats)     let allReverts = not . (any Expr.isSuccess) $ ends     let unexpectedAllRevert = allReverts && not shouldFail     when conf.debug $ liftIO $ putStrLn $ "   symRun -- (cex,warnings,unexpectedAllRevert): " <> show (any isCex results, warnings, unexpectedAllRevert)@@ -319,8 +320,9 @@     when (unexpectedAllRevert && (warnings || (any isCex results))) $ do       -- if we display a FAILED due to Cex/warnings, we should also mention everything reverted       liftIO $ putStrLn $ "   \x1b[33m[WARNING]\x1b[0m " <> Text.unpack testName <> " all branches reverted\n"-    let warnData = Just $ WarningData solcContr sourceCache vm'-    liftIO $ printWarnings warnData ends results $ "the test " <> Text.unpack testName+    let sl = Just $ makeSrcLookup dapp sourceCache+    liftIO $ printWarnings sl vm'.env.contracts ends results $ "the test " <> Text.unpack testName+    liftIO $ printCaveats caveats     pure (not (any isCex results), not (warnings || unexpectedAllRevert))     where       cexHandler :: (Expr 'Buf, [Prop])@@ -364,15 +366,24 @@               lhs = LitByte (c2w a)               rhs = Expr.readByte (Lit (fromIntegral n)) b ---printWarnings :: Maybe (WarningData t) -> GetUnknownStr b => [Expr 'End] -> [ProofResult a b] -> String -> IO ()-printWarnings warnData e results testName = do+printWarnings :: Maybe SrcLookup -> Map (Expr EAddr) Contract -> GetUnknownStr b => [Expr 'End] -> [ProofResult a b] -> String -> IO ()+printWarnings srcLookupM contracts e results testName = do   when (any isUnknown results || any isError results || any Expr.isPartial e) $ do     putStrLn $ "   \x1b[33m[WARNING]\x1b[0m hevm was only able to partially explore " <> testName <> " due to: ";     forM_ (groupIssues (filter isError results)) $ \(num, str) -> putStrLn $ "      " <> show num <> "x -> " <> str     forM_ (groupIssues (filter isUnknown results)) $ \(num, str) -> putStrLn $ "      " <> show num <> "x -> " <> str-    forM_ (groupPartials warnData e) $ \(num, str) -> putStrLn $ "      " <> show num <> "x -> " <> str+    forM_ (groupPartials srcLookupM contracts e) $ \(num, str) -> putStrLn $ "      " <> show num <> "x -> " <> str   putStrLn "" +-- | Print any program-wide soundness caveats (see 'Caveat'). Unlike+-- 'printWarnings' this is not about unexplored paths: it tells the user the+-- input domain was restricted, so a "no counterexample" result is only valid+-- within that restriction.+printCaveats :: [Caveat] -> IO ()+printCaveats caveats =+  forM_ caveats $ \c ->+    putStrLn $ "   \x1b[33m[CAVEAT]\x1b[0m " <> Text.unpack (formatCaveat c)+ getReproFailure :: App m => Sig -> Expr Buf -> SMTCex -> m (Err ReproducibleCex) getReproFailure sig@(Sig testName _) cd cex = do   bs <- calldataFromCex cex cd sig@@ -524,13 +535,10 @@   vm <- get   put $ initTx vm -initialUnitTestVm :: VMOps t => UnitTestOptions -> SolcContract -> ST RealWorld (VM t)+initialUnitTestVm :: forall t. VMOps t => UnitTestOptions -> SolcContract -> ST RealWorld (VM t) initialUnitTestVm (UnitTestOptions {..}) theContract = do-  vm <- makeVm $ VMOpts+  vm <- makeVm $ (defaultVMOpts @t)            { contract = initialContract (InitCode theContract.creationCode mempty)-           , otherContracts = []-           , calldata = mempty-           , value = Lit 0            , address = testParams.address            , caller = testParams.caller            , origin = testParams.origin@@ -545,20 +553,35 @@            , priorityFee = testParams.priorityFee            , maxCodeSize = testParams.maxCodeSize            , prevRandao = testParams.prevrandao-           , schedule = feeSchedule            , chainId = testParams.chainId            , create = True-           , baseState = EmptyBase-           , txAccessList = mempty -- TODO: support unit test access lists???            , allowFFI = ffiAllowed-           , freshAddresses = 0-           , beaconRoot = 0            }   let creator =         initialContract (RuntimeCode (ConcreteRuntimeCode ""))           & set #nonce (Just 1)           & set #balance (Lit testParams.balanceCreate)   pure $ vm & set (#env % #contracts % at (LitAddr ethrunAddress)) (Just creator)++-- | Build a SrcLookup callback that resolves (address, pc) to source location info+makeSrcLookup :: DappInfo -> SourceCache -> SrcLookup+makeSrcLookup dapp srcCache = SrcLookup $ \contracts addr pc ->+  let fallback = " at addr: " <> show addr <> " at pc: " <> show pc+  in fromMaybe fallback $ do+    contr <- Map.lookup addr contracts+    pcOp <- contr.opIxMap VS.!? pc+    sol <- findSrc contr dapp+    sMap <- case contr.code of+      RuntimeCode _ -> Seq.lookup pcOp sol.runtimeSrcmap+      InitCode _ _ -> Seq.lookup pcOp sol.creationSrcmap+      UnknownCode _ -> Nothing+    (fname, fcontent) <- Map.lookup sMap.file srcCache.files+    let eols = BS.count '\n' $ BS.take sMap.offset fcontent+    let str = " in file \"" <> fname <> "\" on line " <> show (eols+1)+    case (BS.length fcontent > (sMap.offset + sMap.length)) of+      False -> pure str+      True  -> let relevant = BS.take sMap.length $ BS.drop sMap.offset fcontent+        in pure $ str <> " : " <> show relevant  paramsFromRpc :: forall m . App m => Fetch.RpcInfo -> Fetch.Session -> m TestVMParams paramsFromRpc rpcInfo sess = do
+ test/EVM/ConcreteExecution/ConcreteExecutionTests.hs view
@@ -0,0 +1,198 @@+{-# LANGUAGE QuasiQuotes #-}++module EVM.ConcreteExecution.ConcreteExecutionTests (tests) where++import Control.Monad.ST (stToIO)+import Data.ByteString (ByteString)+import Data.ByteString qualified as BS+import Data.Functor ((<&>))+import Data.Maybe (fromMaybe)+import Data.String.Here+import Data.Text (Text)+import Data.Text qualified as T+import Data.Tree (flatten)+import Data.Vector qualified as V++import EVM.ABI+import EVM.Effects qualified as Effects+import EVM.Fetch qualified as Fetch+import EVM.Solidity (solcRuntime)+import EVM.Solvers (defMemLimit)+import EVM.Stepper qualified+import EVM.Transaction (initTx)+import EVM.Types+import EVM (initialContract, makeVm, defaultVMOpts, traceForest)+import Test.Tasty+import Test.Tasty.HUnit++type SourceCode = Text+type ContractName = Text+type FunctionName = Text+type Args = [AbiValue]+type ExecResult = Either EvmError (Expr Buf)++compileOneContract :: SourceCode -> ContractName -> IO ByteString+compileOneContract sourceCode contractName =+  solcRuntime contractName sourceCode <&> fromMaybe (internalError $ "Contract " <> (show contractName) <> " not present in the given source code")+++-- | Set up and run a single contract call, returning the final VM state.+setupAndRunCall :: SourceCode -> ContractName -> FunctionName -> Args -> EVM.Stepper.Stepper Concrete a -> IO a+setupAndRunCall sourceCode contractName functionName abiArgs stepper = do+  runtimeCode <- compileOneContract sourceCode contractName+  let functionSignature = functionName <> "(" <> T.intercalate "," (map (abiTypeSolidity . abiValueType) abiArgs) <> ")"+  let callData = abiMethod functionSignature (AbiTuple $ V.fromList abiArgs)+  let contractWithCode = initialContract (RuntimeCode $ ConcreteRuntimeCode runtimeCode)+  initialVM <- stToIO $ makeVm $ defaultVMOpts+    { contract = contractWithCode+    , calldata = (ConcreteBuf callData, [])+    }+  let withInitializedTransactionVM = EVM.Transaction.initTx initialVM+  let fetcher = Fetch.zero 0 Nothing defMemLimit+  Effects.runApp $ EVM.Stepper.interpret fetcher withInitializedTransactionVM stepper++executeSingleCall :: SourceCode -> ContractName -> FunctionName -> Args -> IO ExecResult+executeSingleCall src name func args = setupAndRunCall src name func args EVM.Stepper.execFully++executeSingleCallVM :: SourceCode -> ContractName -> FunctionName -> Args -> IO (VM Concrete)+executeSingleCallVM src name func args = setupAndRunCall src name func args EVM.Stepper.runFully++tests :: TestTree+tests = testGroup "Concrete execution tests"+  [ testCase "return-updated-input-argument" $ expectValue (AbiUInt 8 3) =<< executeSingleCall simpleUncheckedArithmeticExample "C" "a" [AbiUInt 8 1]+  , testCase "silent-overflow-in-unchecked-arithmetic" $ expectValue (AbiUInt 8 245) =<< executeSingleCall simpleUncheckedArithmeticExample "C" "a" [AbiUInt 8 250]+  , testCase "revert-on-arithmetic-overflow" $ expectRevert =<< executeSingleCall simpleCheckedArithmeticExample "C" "a" [AbiUInt 8 250]+  , testCase "cheat-assume-satisfied" $ expectValue (AbiUInt 8 42) =<< executeSingleCall assumeCheatCodeExample "C" "a" [AbiUInt 8 42]+  , testCase "cheat-assume-failing" $ expectError AssumeCheatFailed =<< executeSingleCall assumeCheatCodeExample "C" "a" [AbiUInt 8 5]+  , testCase "console-log-does-not-revert" $ do+      res <- executeSingleCall consoleLogExample "C" "a" [AbiUInt 256 42]+      _ <- expectSuccess res+      pure ()+  , testCase "console-log-produces-trace" $ do+      vm <- executeSingleCallVM consoleLogExample "C" "a" [AbiUInt 256 42]+      let traces = concatMap flatten (traceForest vm)+          hasConsoleLog = any (\t -> case t.tracedata of ConsoleLog _ -> True; _ -> False) traces+      assertBool "Expected a ConsoleLog trace" hasConsoleLog+  , testCase "console-log-extcodesize-nonzero" $ do+      res <- executeSingleCall consoleLogExtcodesizeExample "C" "a" []+      val <- expectSuccess res+      let obtained = decodeAbiValue (AbiUIntType 256) (BS.fromStrict val)+      assertEqual "extcodesize of console addr should be nonzero" (AbiUInt 256 1) obtained+  , testCase "console-log-returns-correct-value" $ do+      res <- executeSingleCall consoleLogExample "C" "a" [AbiUInt 256 99]+      val <- expectSuccess res+      let obtained = decodeAbiValue (AbiUIntType 256) (BS.fromStrict val)+      assertEqual "return value should be x+1" (AbiUInt 256 100) obtained+  , testCase "console-log-multiple-calls" $ do+      vm <- executeSingleCallVM consoleLogMultipleExample "C" "a" [AbiUInt 256 5]+      let traces = concatMap flatten (traceForest vm)+          consoleLogs = filter (\t -> case t.tracedata of ConsoleLog _ -> True; _ -> False) traces+      assertEqual "Should have 2 console.log traces" 2 (length consoleLogs)+  ]++expectValue :: AbiValue -> ExecResult -> IO ()+expectValue expectedVal res = do+  let abiType = abiValueType expectedVal+  bs <- expectSuccess res+  let obtainedVal = decodeAbiValue abiType (BS.fromStrict bs)+  assertEqual "Incorrect result of concrete execution" expectedVal obtainedVal++expectSuccess :: ExecResult -> IO ByteString+expectSuccess res = do+  buf <- assertRight res+  case buf of+    ConcreteBuf bs -> pure bs+    _ -> assertFailure $ "Unexpected symbolic buffer: " <> show buf++expectRevert :: ExecResult -> IO ()+expectRevert res = do+  err <- assertLeft res+  case err of+    Revert _ -> pure ()+    _ -> assertFailure "Revert was expected, but got a different EVM error"++expectError :: EvmError -> ExecResult -> IO ()+expectError expectedErr res = do+  err <- assertLeft res+  assertEqual "Incorrect error obtained from concrete execution" expectedErr err++assertLeft :: Either e a -> IO e+assertLeft = \case+  Left e -> pure e+  Right _ -> assertFailure "Left we expected, but got Right"++assertRight :: Show e => Either e a -> IO a+assertRight = \case+  Left e -> assertFailure $ "Unexpected error: " <> show e+  Right x -> pure x++simpleUncheckedArithmeticExample :: Text+simpleUncheckedArithmeticExample = [here|+  contract C {+    function a(uint8 x) public returns (uint8 b) {+      unchecked { b = x*2+1; }+    }+  }+|]++simpleCheckedArithmeticExample :: Text+simpleCheckedArithmeticExample = [here|+  contract C {+    function a(uint8 x) public returns (uint8 b) {+      b = x*2+1;+    }+  }+|]++assumeCheatCodeExample :: Text+assumeCheatCodeExample = [here|+  interface Hevm {function assume(bool) external;}+  contract C {+    function a(uint8 x) public returns (uint8 b) {+      Hevm hevm = Hevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);+      hevm.assume(x > 10);+      b = x;+    }+  }+|]++consoleLogExtcodesizeExample :: Text+consoleLogExtcodesizeExample = [here|+  contract C {+    function a() public view returns (uint256) {+      address console = 0x000000000000000000636F6e736F6c652e6c6f67;+      uint256 sz;+      assembly { sz := extcodesize(console) }+      return sz;+    }+  }+|]++consoleLogMultipleExample :: Text+consoleLogMultipleExample = [here|+  contract C {+    function a(uint256 x) public view returns (uint256) {+      address console = 0x000000000000000000636F6e736F6c652e6c6f67;+      bytes memory p1 = abi.encodeWithSignature("log(string)", "before");+      (bool s1,) = console.staticcall(p1);+      require(s1);+      bytes memory p2 = abi.encodeWithSignature("log(uint256)", x);+      (bool s2,) = console.staticcall(p2);+      require(s2);+      return x;+    }+  }+|]++consoleLogExample :: Text+consoleLogExample = [here|+  contract C {+    function a(uint256 x) public view returns (uint256) {+      address console = 0x000000000000000000636F6e736F6c652e6c6f67;+      bytes memory payload = abi.encodeWithSignature("log(string,uint256)", "value is", x);+      (bool s,) = console.staticcall(payload);+      require(s);+      return x + 1;+    }+  }+|]
+ test/EVM/Equivalence/EquivalenceTests.hs view
@@ -0,0 +1,566 @@+{-# LANGUAGE QuasiQuotes #-}+{-# OPTIONS_GHC -Wno-x-partial #-}++module EVM.Equivalence.EquivalenceTests (tests) where++import Control.Monad (when, unless, forM_)+import Data.ByteString (ByteString)+import Data.Either.Extra (fromRight)+import Data.Functor ((<&>))+import Data.Maybe (fromMaybe, mapMaybe, fromJust)+import Data.List qualified as List (isInfixOf)+import Data.String.Here+import Data.Text (Text)+import Data.Text qualified as T+import Data.Time (diffUTCTime, getCurrentTime)+import System.Environment (lookupEnv)+import System.FilePath (makeRelative, normalise)+import System.FilePath.Find (find, always, filePath, fileType, (&&?), FileType(RegularFile))+import Test.Tasty+import Test.Tasty.ExpectedFailure (ignoreTest)+import Test.Tasty.HUnit+import Text.RE.TDFA.String hiding (matches)+import Text.RE.Replace (replaceAll, captureText, CaptureOrdinal(..), CaptureID(..))+++import EVM.Effects qualified as Effects+import EVM.Expr qualified as Expr+import EVM.Format (hexByteString)+import EVM.Types+import EVM.Solidity (solcRuntime, solidity, yul)+import EVM.Solidity qualified as Solidity (CodeType(..))+import EVM.Solvers (withSolvers, defMemLimit, Solver(..))+import EVM.SymExec++tests :: TestTree+tests = testGroup "Bytecode equivalence tests"+  [ runtimeSolidityEquivalenceTests+  , deploymentSolidityEquivalenceTests+  , bytecodeEquivalenceTests+  , yulEquivalenceTests+  ]+++type SourceCode = Text++infix 2 `with`+infix 1 `on`++type Substitution = (Text, Text)+with :: Text -> Text -> Substitution+with = (,)++type SubstitutionJob = (Substitution, Text)+on :: Substitution -> Text -> SubstitutionJob+on = (,)++replace :: SubstitutionJob -> Text+replace ((old, new), txt) =+  case break matches (T.lines txt) of+    (xs, _:ys) -> T.unlines (xs ++ new : ys)+    _          -> internalError "replace: line not found"+  where+    matches line = T.strip line == T.strip old++compile :: SourceCode -> Solidity.CodeType -> IO ByteString+compile sourceCode Solidity.Runtime = solcRuntime "C" sourceCode <&> fromMaybe (internalError "Contract C not present in the given source code")+compile sourceCode Solidity.Creation = solidity "C" sourceCode <&> fromMaybe (internalError "Contract C not present in the given source code")++run :: SourceCode -> SourceCode -> Solidity.CodeType -> IO EqIssues+run s s' codeType = do+  a <- compile s codeType+  b <- compile s' codeType+  Effects.runApp $ withSolvers Bitwuzla 3 Nothing defMemLimit $ \sg -> do+    (calldata, _) <- mkCalldata Nothing []+    equivalenceCheck sg Nothing a b defaultVeriOpts calldata (codeType == Solidity.Creation)++runtimeSolidityEquivalenceTests :: TestTree+runtimeSolidityEquivalenceTests = testGroup "Solidity runtime equivalence"+  [+    testCase "eq-simple-diff" $ do+      let a = [here|+                contract C {+                  function stuff() public returns (uint256) {+                    return 4;+                  }+                }+             |]+      let b = replace $ "return 4;" `with` "return 5;" `on` a+      expectDifference 1 a b+  -- diverging gas overapproximations are caught+  -- previously, they had the same name (gas_...), so they compared equal+  , testCase "eq-divergent-overapprox-gas" $ do+    let a = [here|+              contract C {+                uint x;+                function stuff(uint a) public returns (uint256) {+                  unchecked { x = a * 2; }+                  return gasleft();+                }+              }+            |]+    let b = replace $ "unchecked { x = a * 2; }" `with` "unchecked { x = a + a; }" `on` a+    expectDifference 1 a b+  -- diverging gas overapproximations are caught+  -- previously, CALL fresh variables were the same so they compared equal+  , testCase "eq-divergent-overapprox-call" $ do+    let a = [i|+              contract C {+                function checkval(address inputAddr, uint256 x, uint256 y) public returns (bool) {+                    bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);+                    (bool success, bytes memory returnData) = inputAddr.staticcall(data);+                    return success;+                }+              }+            |]+    let b = replace $ [here|bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);|]+              `with`  [here|bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, x);|]+              `on` a+    expectDifference 1 a b+  , testCase "eq-unknown-addr" $ do+      let a = [i|+                contract C {+                  address addr;+                  function a(address a, address b) public {+                    addr = a;+                  }+                }+              |]+      let b = replace $ "addr = a;" `with` "addr = b;" `on` a+      expectDifference 1 a b+  , testCase "eq-sol-exp-cex" $ do+      let a = [i|+                contract C {+                  function a(uint8 x) public returns (uint8 b) {+                    unchecked {+                      b = x*2+1;+                    }+                  }+                }+              |]+      let b = replace $ "b = x*2+1;" `with` "b = x<<1;" `on` a+      expectDifference 1 a b+  , testCase "eq-sol-exp-qed" $ do+      let a = [i|+                contract C {+                  function a(uint8 x) public returns (uint8 b) {+                    unchecked {+                      b = x*2;+                    }+                  }+                }+              |]+      let b = replace $ "b = x*2;" `with` "b = x<<1;" `on` a+      expectEquivalent a b+  , testCase "eq-balance-differs" $ do+      let a = [i|+                contract Send {+                  constructor(address payable dst) payable {+                    selfdestruct(dst);+                  }+                }+                contract C {+                  function f() public {+                    new Send{value:2}(payable(address(0x0)));+                  }+                }+              |]+      let b = replace $ "new Send{value:2}(payable(address(0x0)));" `with` "new Send{value:1}(payable(address(0x0)));" `on` a+      expectDifferent a b+  , testCase "eq-storage-write-to-static-array-uint128" $ do+      let a =+            [i|+              contract C {+                uint128[5] arr;+                function set(uint i, uint128 v) external returns (uint) {+                  arr[i] = v;+                  arr[i] = v;+                  return 0;+                }+              }+            |]+      let b = replace $ "arr[i] = v;" `with` "" `on` a -- NOTE: This effectively removes the first occurrence+      expectEquivalent a b+  , testCase "eq-storage-write-to-static-array-uint8" $ do+      let a =+            [i|+              contract C {+                uint8[10] arr;+                function set(uint i, uint8 v) external returns (uint) {+                  arr[i] = v;+                  arr[i] = v;+                  return 0;+                }+              }+            |]+      let b = replace $ "arr[i] = v;" `with` "" `on` a -- NOTE: This effectively removes the first occurrence+      expectEquivalent a b+  , testCase "eq-storage-write-to-static-array-uint32" $ do+      let a =+            [i|+              contract C {+                uint32[5] arr;+                function set(uint i, uint32 v) external returns (uint) {+                  arr[i] = 1;+                  arr[i] = v;+                  return 0;+                }+              }+            |]+      let b = replace $ "arr[i] = 1;" `with` "" `on` a+      expectEquivalent a b+  , ignoreTest $ testCase "eq-handles-contract-deployment" $ do -- TODO: this fails because we don't check equivalence of deployed contracts+      let a = [i|+                contract Send {+                  constructor(address payable dst) payable {+                    selfdestruct(dst);+                  }+                }++                contract A {+                  address parent;+                  constructor(address p) {+                    parent = p;+                  }+                  function evil() public {+                    parent.call(abi.encode(C.drain.selector));+                  }+                }++                contract C {+                  address child;+                  function a() public {+                    child = address(new A(address(this)));+                  }+                  function drain() public {+                    require(msg.sender == child);+                    new Send{value: address(this).balance}(payable(address(0x0)));+                  }+                }+              |]+      let b = [i|+                contract Send {+                  constructor(address payable dst) payable {+                    selfdestruct(dst);+                  }+                }++                contract A {+                  address parent;+                  constructor(address p) {+                      parent = p;+                  }+                }++                contract C {+                  address child;+                  function a() public {+                    child = address(new A(address(this)));+                  }+                  function drain() public {+                    require(msg.sender == child);+                    new Send{value: address(this).balance}(payable(address(0x0)));+                  }+                }+              |]+      expectDifferent a b+  ]+  where+    expectDifference :: Int -> SourceCode -> SourceCode -> IO ()+    expectDifference diffCount s s' = do+      eq <- run s s' Solidity.Runtime+      assertBool "Must have a difference" (any (isCex . fst) eq.res)+      let cexs = mapMaybe (getCex . fst) eq.res+      assertEqual "Must have exactly one cex" diffCount (length cexs)++    expectDifferent :: SourceCode -> SourceCode -> IO ()+    expectDifferent s s' = do+      eq <- run s s' Solidity.Runtime+      assertBool "Must have a difference" (any (isCex . fst) eq.res)++    expectEquivalent :: SourceCode -> SourceCode -> IO ()+    expectEquivalent s s' = do+      eq <- run s s' Solidity.Runtime+      assertEqual "Must have no difference" [] (map fst eq.res)++deploymentSolidityEquivalenceTests :: TestTree+deploymentSolidityEquivalenceTests = testGroup "Solidity deployment equivalence"+  [ testCase "constructor-same-deployed-diff" $ do+      let a = [i|+                contract C {+                  uint public immutable NUMBER;+                  constructor(uint a) {+                    NUMBER = 4;+                  }+                  function stuff(uint b) public returns (uint256) {+                    unchecked{return 2*b+NUMBER;}+                  }+                }+              |]+      let b = replace $ "unchecked{return 2*b+NUMBER;}" `with` "unchecked {return 4*b+NUMBER;}" `on` a+      expectDifferent a b+  , testCase "constructor-same-deployed-diff2" $ do+      let aBody = [i|+                    uint public immutable NUMBER;+                    constructor(uint a) {+                      NUMBER = 4;+                    }+                    function stuff(uint b) public returns (uint256) {+                      unchecked{return 4*b+NUMBER;}+                    }+                  |]+      let bBody = aBody+            <> [i|+                function stuff_other(uint b) public returns (uint256) {+                  unchecked {return 2*b+NUMBER;}+                }+              |]+      let a = wrap aBody+      let b = wrap bBody+      expectDifferent a b+  , testCase "constructor-same-deployed-diff3" $ do+      let aBody = [i|+                    uint public immutable NUMBER;+                    constructor(uint a) {+                      NUMBER = 4;+                    }+                  |]+      let bBody = aBody+            <> [i|+                function stuff(uint b) public returns (uint256) {+                    unchecked{return 4*b+NUMBER;}+                }+                |]+      let a = wrap aBody+      let b = wrap bBody+      expectDifferent a b++  -- We set x to be 0 on deployment. Default value is also 0. So they are equivalent+  -- We cannot deal with symbolic code. However, the below will generate symbolic code,+  -- because of the parameter in the constructor that is set to NUMBER in the deployed code.+  -- Hence, this test is ignored.+  , ignoreTest $ testCase "constructor-diff-deploy" $ do+    let a = [i|+              contract C {+                uint public immutable NUMBER;+                constructor(uint a) {+                  NUMBER = a+4;+                }+                function stuff(uint b) public returns (uint256) {+                  return NUMBER;+                }+              }+            |]+    let b = replace $ "NUMBER = a+4;" `with` "NUMBER = a*2;" `on` a+    expectDifferent a b+  -- We set x to be 0 on deployment. Default value is also 0. So they are equivalent+  , testCase "constructor-implicit" $ do+    let aBody = [i|+                  uint immutable x;+                  function stuff(uint a) public returns (uint256) {+                    unchecked {return 8+a;}+                  }+                |]+    let bBody = aBody <> "constructor() {x = 0;}"+    let a = wrap aBody+    let b = wrap bBody+    expectEquivalent a b+  -- We set x to be 3 vs 0 (default) on deployment.+  , testCase "constructor-differing" $ do+    let aBody = [i|+                  uint x;+                  function stuff(uint a) public returns (uint256) {+                    unchecked {return a+x;}+                  }+                |]+    let bBody = aBody <> "constructor() {x = 3;}"+    let a = wrap aBody+    let b = wrap bBody+    expectDifferent a b+  ]+  where+    expectDifferent :: SourceCode -> SourceCode -> IO ()+    expectDifferent s s' = do+      eq <- run s s' Solidity.Creation+      assertBool "Must have a difference" (any (isCex . fst) eq.res)++    expectEquivalent :: SourceCode -> SourceCode -> IO ()+    expectEquivalent s s' = do+      eq <- run s s' Solidity.Creation+      assertEqual "Must have no difference" [] (map fst eq.res)++    wrap body = "contract C {\n" <> body <> "}"++bytecodeEquivalenceTests :: TestTree+bytecodeEquivalenceTests = testGroup "Bytecode equivalence"+  [ testCase "eq-issue-with-length-cex-bug679" $ do -- check bug https://github.com/argotorg/hevm/issues/679+    let a = fromJust (hexByteString "5f610100526020610100f3")+        b = fromJust (hexByteString "5f356101f40115610100526020610100f3")+    eq <- Effects.runApp $ withSolvers Z3 1 Nothing defMemLimit $ \s -> do+      (calldata, _) <- mkCalldata Nothing []+      equivalenceCheck s Nothing a b defaultVeriOpts calldata False+    assertBool "Must have a difference" (any (isCex . fst) eq.res)+    let cexs :: [SMTCex] = mapMaybe (getCex . fst) eq.res+    cex <- case cexs of+      [cex] -> pure cex+      _     -> assertFailure "Must have exactly one cex"+    let def = fromRight (internalError "cannot be") $ defaultSymbolicValues $ subModel cex (AbstractBuf "txdata")+    let buf = prettyBuf $ Expr.concKeccakSimpExpr def+    assertBool "Must start with specific string" (T.isPrefixOf "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0c" buf)+  ]++yulEquivalenceTests :: TestTree+yulEquivalenceTests = testGroup "Yul equivalence"+  [ testCase "eq-yul-simple-cex" $ do+    let a = [i|+              {+                calldatacopy(0, 0, 32)+                switch mload(0)+                case 0 { }+                case 1 { }+                default { invalid() }+              }+            |]+    let b = replace $ "case 1 { }" `with` "case 2 { }" `on` a+    Right aPrgm <- yul "" a+    Right bPrgm <- yul "" b+    eq <- Effects.runApp $ withSolvers Bitwuzla 1 Nothing defMemLimit $ \s -> do+      (calldata, _) <- mkCalldata Nothing []+      equivalenceCheck s Nothing aPrgm bPrgm defaultVeriOpts calldata False+    assertBool "Must have a difference" (any (isCex . fst) eq.res)+  , yulOptimizationsSolcTests+  ]++yulOptimizationsSolcTests :: TestTree+yulOptimizationsSolcTests = testCase "eq-all-yul-optimization-tests" $ do+        let opts = (defaultVeriOpts :: VeriOpts) { iterConf = defaultIterConf {maxIter = Just 5, askSmtIters = 20, loopHeuristic = Naive }}+            ignoredTests = fmap normalise+                    -- unbounded loop --+                    [ "commonSubexpressionEliminator/branches_for.yul"+                    , "conditionalSimplifier/no_opt_if_break_is_not_last.yul"+                    , "conditionalUnsimplifier/no_opt_if_break_is_not_last.yul"+                    , "expressionSimplifier/inside_for.yul"+                    , "forLoopConditionIntoBody/cond_types.yul"+                    , "forLoopConditionIntoBody/simple.yul"+                    , "fullSimplify/inside_for.yul"+                    , "fullSuite/no_move_loop_orig.yul"+                    , "loopInvariantCodeMotion/multi.yul"+                    , "redundantAssignEliminator/for_deep_simple.yul"+                    , "unusedAssignEliminator/for_deep_noremove.yul"+                    , "unusedAssignEliminator/for_deep_simple.yul"+                    , "ssaTransform/for_def_in_init.yul"+                    , "loopInvariantCodeMotion/simple_state.yul"+                    , "loopInvariantCodeMotion/simple.yul"+                    , "loopInvariantCodeMotion/recursive.yul"+                    , "loopInvariantCodeMotion/no_move_staticall_returndatasize.yul"+                    , "loopInvariantCodeMotion/no_move_state_loop.yul"+                    , "loopInvariantCodeMotion/no_move_state.yul" -- not infinite, but rollaround on a large int+                    , "loopInvariantCodeMotion/no_move_loop.yul"++                    -- infinite recursion+                    , "unusedStoreEliminator/function_side_effects_2.yul"+                    , "unusedStoreEliminator/write_before_recursion.yul"+                    , "fullInliner/multi_fun_callback.yul"+                    , "conditionalUnsimplifier/side_effects_of_functions.yul"+                    , "expressionInliner/double_recursive_calls.yul"+                    , "conditionalSimplifier/side_effects_of_functions.yul"++                    -- Takes too long, would timeout on most test setups.+                    -- We could probably fix these by "bunching together" queries+                    , "loadResolver/multi_sload_loop.yul"+                    , "fullSuite/abi_example1.yul"+                    , "loadResolver/merge_known_write_with_distance.yul"+                    , "loadResolver/second_mstore_with_delta.yul"+                    , "stackLimitEvader" -- all that are in this subdirectory++                    -- stack too deep --+                    , "fullSuite/aztec.yul"+                    , "stackCompressor/unusedPrunerWithMSize.yul"++                    -- New: symbolic index on MSTORE/MLOAD/CopySlice/CallDataCopy/ExtCodeCopy/Revert,+                    --      or exponent is symbolic (requires symbolic gas)+                    --      or SHA3 offset symbolic+                    , "expressionSimplifier/exp_simplifications.yul"+                    , "loadResolver/merge_mload_with_known_distance.yul"+                    , "loopInvariantCodeMotion/complex_move.yul"+                    , "loopInvariantCodeMotion/no_move_memory.yul"+                    , "loopInvariantCodeMotion/no_move_storage.yul"+                    -- EOF+                    , "loadResolver/extstaticcall.yul"+                    , "loadResolver/memory_with_extcall_invalidation.yul"+                    , "loadResolver/zero_length_reads_eof.yul"+                    -- Yul subobjects+                    , "fullSuite/sub_objects.yul"+                    ]++        solcRepo <- fromMaybe (internalError "cannot find solidity repo") <$> (lookupEnv "HEVM_SOLIDITY_REPO")+        let testDir = normalise $ solcRepo <> "/test/libyul/yulOptimizerTests"+            shouldIgnore fp = any (`List.isInfixOf` (makeRelative testDir fp)) ignoredTests+            isRegularFile = (== RegularFile) <$> fileType+        filesFiltered <- find always (isRegularFile &&? (not . shouldIgnore <$> filePath)) testDir++        -- Takes one file which follows the Solidity Yul optimizer unit tests format,+        -- extracts both the nonoptimized and the optimized versions, and checks equivalence.+        forM_ filesFiltered (\f-> do+          origcont <- readFile f+          let+            onlyAfter pattern (a:ax) = if a =~ pattern then ax else onlyAfter pattern ax+            onlyAfter _ [] = []+            replaceOnce pat repl inp = go inp [] where+              go (a:ax) b = if a =~ pat then let a2 = replaceAll repl $ a *=~ pat in b ++ a2:ax+                                        else go ax (b ++ [a])+              go [] b = b++            -- takes a yul program and ensures memory is symbolic by prepending+            -- `calldatacopy(0,0,1024)`. (calldata is symbolic, but memory starts empty).+            -- This forces the exploration of more branches, and makes the test vectors a+            -- little more thorough.+            symbolicMem program@(a:_) = if a =~ [re|^ *object|] then+                                      let replacementTemplate = "$0\n    calldatacopy(0,0,1024)"+                                          replaced = replaceAll replacementTemplate $ (unlines program) *=~ [re|code {|]+                                      in lines replaced+                                    else replaceOnce [re|^ *{|] "{\ncalldatacopy(0,0,1024)" program+            symbolicMem _ = internalError "Program too short"++            unfiltered = lines origcont+            filteredASym = symbolicMem [ x | x <- unfiltered, (not $ x =~ [re|^//|]) && (not $ x =~ [re|^$|]) ]+            filteredBSym = symbolicMem [ cleaned | x <- onlyAfter [re|^// step:|] unfiltered+                                        , let cleaned = replaceAll "" $ x *=~[re|^//|]+                                        , not $ null cleaned+                                       ]+            contractName = let+               match = head filteredASym ?=~ [re|^ *object +"([^"]+)"|]+               success = captureText (IsCaptureOrdinal $ CaptureOrdinal 1) match+             in T.pack $ case matched match of+                  False -> ""+                  True-> success+          start <- getCurrentTime+          let verbosity :: Int = 0+          when (verbosity > 0) $ putStrLn $ "Checking file: " <> f+          when (verbosity > 1) $ do+            putStrLn "-------------Original Below-----------------"+            mapM_ putStrLn unfiltered+            putStrLn "------------- Filtered A + Symb below-----------------"+            mapM_ putStrLn filteredASym+            putStrLn "------------- Filtered B + Symb below-----------------"+            mapM_ putStrLn filteredBSym+            putStrLn "------------- END -----------------"+          Right aPrgm <- yul contractName $ T.pack $ unlines filteredASym+          Right bPrgm <- yul contractName $ T.pack $ unlines filteredBSym+          eq <- Effects.runApp $ withSolvers Bitwuzla 1 (Just 100) defMemLimit $ \s -> do+            (calldata, _) <- mkCalldata Nothing []+            equivalenceCheck s Nothing aPrgm bPrgm opts calldata False+          let res = map fst eq.res+          end <- getCurrentTime+          case any isCex res of+            False -> do+              when (verbosity > 0) $ print $ "OK. Took " <> (show $ diffUTCTime end start) <> " seconds"+              let timeouts = filter isUnknown res+              let errors = filter isError res+              unless (null timeouts && null errors) $ do+                putStrLn $ "But " <> (show $ length timeouts) <> " timeout(s) and " <>  (show $ length errors) <> " error(s) occurred"+                assertFailure "Encountered timeout(s) and/or error(s)"+            True -> do+              putStrLn $ "Not OK: " <> show f <> " Got: " <> show res+              assertFailure "Was NOT equivalent"+           )
+ test/EVM/Expr/ExprTests.hs view
@@ -0,0 +1,1621 @@+{-# LANGUAGE TypeAbstractions #-}+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} -- It's OK to crash in tests if the pattern does not match++module EVM.Expr.ExprTests (tests) where++import Prelude hiding (LT, GT)+import Test.Tasty+import Test.Tasty.ExpectedFailure (ignoreTest)+import Test.Tasty.HUnit+import Test.Tasty.QuickCheck hiding (Failure, Success)++import Data.Bits (shiftL)+import Data.ByteString qualified as BS (pack)+import Data.Containers.ListUtils (nubOrd)+import Data.List qualified as List (nub)+import Data.Map.Strict qualified as Map+import Data.Maybe (fromMaybe)+import Data.Typeable++import EVM.Effects+import EVM.Expr qualified as Expr+import EVM.Expr.Generator+import EVM.Format (hexText)+import EVM.Solvers hiding (checkSat)+import EVM.SymExec (subModel)+import EVM.Traversals (mapExprM)+import EVM.Types hiding (Env)+++tests :: TestTree+tests = testGroup "Expr"+  [unitTests, fuzzTests]++unitTests :: TestTree+unitTests = testGroup "Unit tests"+  [ simplificationTests+  , constantPropagationTests+  , boundPropagationTests+  , comparisonTests+  , signExtendTests+  , concretizationTests+  ]++simplificationTests :: TestTree+simplificationTests = testGroup "Expr-rewriting"+  [ storageTests+  , copySliceTests+  , memoryTests+  , basicSimplificationTests+  , propSimplificationTests+  , wrappingTests+  ]++storageTests :: TestTree+storageTests = testGroup "Storage tests"+  [+    testCase "read-from-sstore" $ assertEqual errorMsg+        (Lit 0xab)+        (Expr.readStorage' (Lit 0x0) (SStore (Lit 0x0) (Lit 0xab) (AbstractStore (LitAddr 0x0) Nothing)))+    , testCase "read-from-concrete" $ assertEqual errorMsg+        (Lit 0xab)+        (Expr.readStorage' (Lit 0x0) (ConcreteStore $ Map.fromList [(0x0, 0xab)]))+    , testCase "read-past-write" $ assertEqual errorMsg+        (Lit 0xab)+        (Expr.readStorage' (Lit 0x0) (SStore (Lit 0x1) (Var "b") (ConcreteStore $ Map.fromList [(0x0, 0xab)])))+    , testCase "simplify-storage-wordToAddr" $ do+       let a = "0x000000000000000000000000d95322745865822719164b1fc167930754c248de000000000000000000000000000000000000000000000000000000000000004a"+           store = ConcreteStore (Map.fromList[(W256 0xebd33f63ba5dda53a45af725baed5628cdad261db5319da5f5d921521fe1161d,W256 0x5842cf)])+           expr = SLoad (Keccak (ConcreteBuf (hexText a))) store+           simpExpr = Expr.wordToAddr expr+           simpExpr2 = Expr.concKeccakSimpExpr expr+       assertEqual "Expression should simplify to value." simpExpr (Just $ LitAddr 0x5842cf)+       assertEqual "Expression should simplify to value." simpExpr2 (Lit 0x5842cf)+    , testCase "simplify-storage-wordToAddr-complex" $ do+       let a = "0x000000000000000000000000d95322745865822719164b1fc167930754c248de000000000000000000000000000000000000000000000000000000000000004a"+           store = ConcreteStore (Map.fromList[(W256 0xebd33f63ba5dda53a45af725baed5628cdad261db5319da5f5d921521fe1161d,W256 0x5842cf)])+           expr = SLoad (Keccak (ConcreteBuf (hexText a))) store+           writeWChain = WriteWord (Lit 0x32) (Lit 0x72) (WriteWord (Lit 0x0) expr (ConcreteBuf ""))+           kecc = Keccak (CopySlice (Lit 0x0) (Lit 0x0) (Lit 0x20) (WriteWord (Lit 0x0) expr (writeWChain)) (ConcreteBuf ""))+           keccAnd =  (And (Lit 1461501637330902918203684832716283019655932542975) kecc)+           outer = And (Lit 1461501637330902918203684832716283019655932542975) (SLoad (keccAnd) (ConcreteStore (Map.fromList[(W256 1184450375068808042203882151692185743185288360635, W256 0xacab)])))+           simp = Expr.concKeccakSimpExpr outer+       assertEqual "Expression should simplify to value." simp (Lit 0xacab)+  ]+  where errorMsg = "Storage read expression not simplified correctly"++copySliceTests :: TestTree+copySliceTests = testGroup "CopySlice tests"+  [ testCase "copyslice-simps" $ do+        let e a b =  CopySlice (Lit 0) (Lit 0) (BufLength (AbstractBuf "buff")) (CopySlice (Lit 0) (Lit 0) (BufLength (AbstractBuf "buff")) (AbstractBuf "buff") (ConcreteBuf a)) (ConcreteBuf b)+            expr1 = e "" ""+            expr2 = e "" "aasdfasdf"+            expr3 = e "9832478932" ""+            expr4 = e "9832478932" "aasdfasdf"+        assertEqual "Not full simp" (Expr.simplify expr1) (AbstractBuf "buff")+        assertEqual "Not full simp" (Expr.simplify expr2) $ CopySlice (Lit 0x0) (Lit 0x0) (BufLength (AbstractBuf "buff")) (AbstractBuf "buff") (ConcreteBuf "aasdfasdf")+        assertEqual "Not full simp" (Expr.simplify expr3) (AbstractBuf "buff")+        assertEqual "Not full simp" (Expr.simplify expr4) $ CopySlice (Lit 0x0) (Lit 0x0) (BufLength (AbstractBuf "buff")) (AbstractBuf "buff") (ConcreteBuf "aasdfasdf")+  , testCase "copyslice-difficult-concretization" $ do+        let commonOffset = (Var "x")+        let concreteVal = (Lit 0xff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00)+        let src = WriteWord commonOffset concreteVal (AbstractBuf "buff")+        let dst = ConcreteBuf ""+        let e = CopySlice commonOffset (Lit 1) (Lit 3) src dst+        let simpl = Expr.simplify e+        let expected = ConcreteBuf $ BS.pack [0x00, 0xff, 0x00, 0xff]+        assertEqual "" expected simpl+  , testCase "copyslice-single-byte-concretizes" $ do+        -- Regression for issue #1066. A size-1 CopySlice whose read range is+        -- fully covered by a concrete WriteWord must concretize, even when an+        -- unrelated (out-of-range) symbolic write sits underneath it.+        let w = Lit 0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789+            src = WriteWord (Lit 0x20) w (WriteWord (Lit 0x80) (Var "x") (ConcreteBuf ""))+            e = CopySlice (Lit 0x20) (Lit 0x0) (Lit 0x1) src (ConcreteBuf "")+        assertEqual "size-1 copy must concretize to the 1st byte, dropping the out-of-range symbolic write"+          (ConcreteBuf (BS.pack [0xab])) (Expr.simplify e)+  , testCase "copyslice-single-byte-value" $ do+        -- Same as above, but no symbolic write this time+        let w = Lit 0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789+            src = WriteWord (Lit 0x20) w (ConcreteBuf "")+            e = CopySlice (Lit 0x3f) (Lit 0x0) (Lit 0x1) src (ConcreteBuf "")+        assertEqual "size-1 copy must read the correct (last) byte"+          (ConcreteBuf (BS.pack [0x89])) (Expr.simplify e)+  ]++memoryTests :: TestTree+memoryTests = testGroup "Memory tests"+  [ testCase "read-write-same-byte" $ assertEqual ""+      (LitByte 0x12)+      (Expr.readByte (Lit 0x20) (WriteByte (Lit 0x20) (LitByte 0x12) mempty))+  , testCase "read-write-same-word" $ assertEqual ""+      (Lit 0x12)+      (Expr.readWord (Lit 0x20) (WriteWord (Lit 0x20) (Lit 0x12) mempty))+  , testCase "read-byte-write-word" $ assertEqual ""+      -- reading at byte 31 a word that's been written should return LSB+      (LitByte 0x12)+      (Expr.readByte (Lit 0x1f) (WriteWord (Lit 0x0) (Lit 0x12) mempty))+  , testCase "read-byte-write-word2" $ assertEqual ""+      -- Same as above, but offset not 0+      (LitByte 0x12)+      (Expr.readByte (Lit 0x20) (WriteWord (Lit 0x1) (Lit 0x12) mempty))+  , testCase "read-write-with-offset" $ assertEqual ""+      -- 0x3F = 63 decimal, 0x20 = 32. 0x12 = 18+      --    We write 128bits (32 Bytes), representing 18 at offset 32.+      --    Hence, when reading out the 63rd byte, we should read out the LSB 8 bits+      --           which is 0x12+      (LitByte 0x12)+      (Expr.readByte (Lit 0x3F) (WriteWord (Lit 0x20) (Lit 0x12) mempty))+  , testCase "read-write-with-offset2" $ assertEqual ""+      --  0x20 = 32, 0x3D = 61+      --  we write 128 bits (32 Bytes) representing 0x10012, at offset 32.+      --  we then read out a byte at offset 61.+      --  So, at 63 we'd read 0x12, at 62 we'd read 0x00, at 61 we should read 0x1+      (LitByte 0x1)+      (Expr.readByte (Lit 0x3D) (WriteWord (Lit 0x20) (Lit 0x10012) mempty))+  , testCase "read-write-with-extension-to-zero" $ assertEqual ""+      -- write word and read it at the same place (i.e. 0 offset)+      (Lit 0x12)+      (Expr.readWord (Lit 0x0) (WriteWord (Lit 0x0) (Lit 0x12) mempty))+  , testCase "read-write-with-extension-to-zero-with-offset" $ assertEqual ""+      -- write word and read it at the same offset of 4+      (Lit 0x12)+      (Expr.readWord (Lit 0x4) (WriteWord (Lit 0x4) (Lit 0x12) mempty))+  , testCase "read-write-with-extension-to-zero-with-offset2" $ assertEqual ""+      -- write word and read it at the same offset of 16+      (Lit 0x12)+      (Expr.readWord (Lit 0x20) (WriteWord (Lit 0x20) (Lit 0x12) mempty))+  , testCase "read-word-over-write-byte" $ assertEqual ""+      (ReadWord (Lit 0x4) (AbstractBuf "abs"))+      (Expr.readWord (Lit 0x4) (WriteByte (Lit 0x1) (LitByte 0x12) (AbstractBuf "abs")))+  , testCase "read-word-copySlice-overlap" $ assertEqual ""+      -- we should not recurse into a copySlice if the read index + 32 overlaps the sliced region+      (ReadWord (Lit 40) (CopySlice (Lit 0) (Lit 30) (Lit 12) (WriteWord (Lit 10) (Lit 0x64) (AbstractBuf "hi")) (AbstractBuf "hi")))+      (Expr.readWord (Lit 40) (CopySlice (Lit 0) (Lit 30) (Lit 12) (WriteWord (Lit 10) (Lit 0x64) (AbstractBuf "hi")) (AbstractBuf "hi")))+  , testCase "read-word-copySlice-after-slice" $ assertEqual "Read word simplification missing!"+      (ReadWord (Lit 100) (AbstractBuf "dst"))+      (Expr.readWord (Lit 100) (CopySlice (Var "srcOff") (Lit 12) (Lit 60) (AbstractBuf "src") (AbstractBuf "dst")))+  , testCase "indexword-MSB" $ assertEqual ""+      -- 31st is the LSB byte (of 32)+      (LitByte 0x78)+      (Expr.indexWord (Lit 31) (Lit 0x12345678))+  , testCase "indexword-LSB" $ assertEqual ""+      -- 0th is the MSB byte (of 32), Lit 0xff22bb... is exactly 32 Bytes.+      (LitByte 0xff)+      (Expr.indexWord (Lit 0) (Lit 0xff22bb4455667788990011223344556677889900112233445566778899001122))+  , testCase "indexword-LSB2" $ assertEqual ""+      -- same as above, but with offset 2+      (LitByte 0xbb)+      (Expr.indexWord (Lit 2) (Lit 0xff22bb4455667788990011223344556677889900112233445566778899001122))+  , testCase "indexword-oob-sym" $ assertEqual ""+      -- indexWord should return 0 for oob access+      (LitByte 0x0)+      (Expr.indexWord (Lit 100) (JoinBytes+        (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0)+        (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0)+        (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0)+        (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0)))+  , testCase "stripbytes-concrete-bug" $ assertEqual ""+      (Expr.simplifyReads (ReadByte (Lit 0) (ConcreteBuf "5")))+      (LitByte 53)++  -- Interval-bounds disjointness for readWord over WriteWord (Add (Lit c) X).++  , testCase "readWord-skip-write-above-and-bounded" $ assertEqual+      "write at Add (Lit 100) (And (Lit 0xff) X) must be skipped when reading at 0"+      (Lit 0)+      (Expr.readWord (Lit 0)+        (WriteWord (Add (Lit 100) (And (Lit 0xff) (Var "x")))+                   (Lit 0x42)+                   mempty))++  , testCase "readWord-skip-write-above-mul-and-bounded" $ assertEqual+      "write at Add (Lit 100) (Mul 32 (And 0xff X)) must be skipped when reading at 0"+      (Lit 0)+      (Expr.readWord (Lit 0)+        (WriteWord (Add (Lit 100) (Mul (Lit 32) (And (Lit 0xff) (Var "x"))))+                   (Lit 0x42)+                   mempty))++  , testCase "readWord-no-skip-write-above-unbounded" $ do+      -- Unbounded X: rule must NOT fire.+      let r = Expr.readWord (Lit 0)+                (WriteWord (Add (Lit 100) (Var "x")) (Lit 0x42) mempty)+      assertBool ("rule fired on unbounded X (unsound): " <> show r)+                 (r /= Lit 0)++  , testCase "readWord-no-skip-write-wrap-risk" $ do+      -- c near maxBound: write region can wrap, so rule must NOT fire.+      let bigC = (maxBound :: W256) - 50+          r = Expr.readWord (Lit 0)+                (WriteWord (Add (Lit bigC) (And (Lit 0xff) (Var "x")))+                           (Lit 0x42)+                           mempty)+      assertBool ("rule fired on wrap-risk write (unsound): " <> show r)+                 (r /= Lit 0)++  , testCase "readWord-no-skip-write-possible-overlap" $ do+      -- Read [80,111] vs write [100,355] overlap; rule must NOT fire.+      let r = Expr.readWord (Lit 80)+                (WriteWord (Add (Lit 100) (And (Lit 0xff) (Var "x")))+                           (Lit 0x42)+                           mempty)+      assertBool ("rule fired on possibly-overlapping write (unsound): " <> show r)+                 (r /= Lit 0)+  ]++basicSimplificationTests :: TestTree+basicSimplificationTests = testGroup "Basic simplification tests"+  [ testCase "simp-readByte1" $ do+      let srcOffset = (ReadWord (Lit 0x1) (AbstractBuf "stuff1"))+          size = (ReadWord (Lit 0x1) (AbstractBuf "stuff2"))+          src = (AbstractBuf "stuff2")+          e = ReadByte (Lit 0x0) (CopySlice srcOffset (Lit 0x10) size src (AbstractBuf "dst"))+          simp = Expr.simplify e+      assertEqual "" simp (ReadByte (Lit 0x0) (AbstractBuf "dst"))+  , testCase "simp-readByte2" $ do+      let srcOffset = (ReadWord (Lit 0x1) (AbstractBuf "stuff1"))+          size = (Lit 0x1)+          src = (AbstractBuf "stuff2")+          e = ReadByte (Lit 0x0) (CopySlice srcOffset (Lit 0x10) size src (AbstractBuf "dst"))+          simp = Expr.simplify e+      assertEqual "" (ReadByte (Lit 0x0) (AbstractBuf "dst")) simp+  , testCase "simp-readWord1" $ do+      let srcOffset = (ReadWord (Lit 0x1) (AbstractBuf "stuff1"))+          size = (ReadWord (Lit 0x1) (AbstractBuf "stuff2"))+          src = (AbstractBuf "stuff2")+          e = ReadWord (Lit 0x1) (CopySlice srcOffset (Lit 0x40) size src (AbstractBuf "dst"))+          simp = Expr.simplify e+      assertEqual "" simp (ReadWord (Lit 0x1) (AbstractBuf "dst"))+  , testCase "simp-readWord2" $ do+    let srcOffset = (ReadWord (Lit 0x12) (AbstractBuf "stuff1"))+        size = (Lit 0x1)+        src = (AbstractBuf "stuff2")+        e = ReadWord (Lit 0x12) (CopySlice srcOffset (Lit 0x50) size src (AbstractBuf "dst"))+        simp = Expr.simplify e+    assertEqual "readWord simplification" (ReadWord (Lit 0x12) (AbstractBuf "dst")) simp+  , testCase "simp-max-buflength" $ do+      let simp = Expr.simplify $ Max (Lit 0) (BufLength (AbstractBuf "txdata"))+      assertEqual "max-buflength rules" simp $ BufLength (AbstractBuf "txdata")+  , testCase "simp-PLT-max" $ do+      let simp = Expr.simplifyProp $ PLT (Max (Lit 5) (BufLength (AbstractBuf "txdata"))) (Lit 99)+      assertEqual "max-buflength rules" simp $ PLT (BufLength (AbstractBuf "txdata")) (Lit 99)+  , testCase "simp-assoc-add1" $ do+      let simp = Expr.simplify $ Add (Add (Var "c") (Var "a")) (Var "b")+      assertEqual "assoc rules" simp $ Add (Var "a") (Add (Var "b") (Var "c"))+  , testCase "simp-assoc-add2" $ do+      let simp = Expr.simplify $ Add (Add (Lit 1) (Var "c")) (Var "b")+      assertEqual "assoc rules" simp $ Add (Lit 1) (Add (Var "b") (Var "c"))+  , testCase "simp-assoc-add3" $ do+      let simp = Expr.simplify $ Add (Lit 1) (Add (Lit 2) (Var "c"))+      assertEqual "assoc rules" simp $ Add (Lit 3) (Var "c")+  , testCase "simp-assoc-add4" $ do+      let simp = Expr.simplify $ Add (Lit 1) (Add (Var "b") (Lit 2))+      assertEqual "assoc rules" simp $ Add (Lit 3) (Var "b")+  , testCase "simp-assoc-add5" $ do+      let simp = Expr.simplify $ Add (Var "a") (Add (Lit 1) (Lit 2))+      assertEqual "assoc rules" simp $ Add (Lit 3) (Var "a")+  , testCase "simp-assoc-add6" $ do+      let simp = Expr.simplify $ Add (Lit 7) (Add (Lit 1) (Lit 2))+      assertEqual "assoc rules" simp $ Lit 10+  , testCase "simp-assoc-add-7" $ do+      let simp = Expr.simplify $ Add (Var "a") (Add (Var "b") (Lit 2))+      assertEqual "assoc rules" simp $ Add (Lit 2) (Add (Var "a") (Var "b"))+  , testCase "simp-assoc-add8" $ do+      let simp = Expr.simplify $ Add (Add (Var "a") (Add (Lit 0x2) (Var "b"))) (Add (Var "c") (Add (Lit 0x2) (Var "d")))+      assertEqual "assoc rules" simp $ Add (Lit 4) (Add (Var "a") (Add (Var "b") (Add (Var "c") (Var "d"))))+  , testCase "simp-assoc-mul1" $ do+      let simp = Expr.simplify $ Mul (Mul (Var "b") (Var "a")) (Var "c")+      assertEqual "assoc rules" simp $ Mul (Var "a") (Mul (Var "b") (Var "c"))+  , testCase "simp-assoc-mul2" $ do+      let simp = Expr.simplify $ Mul (Lit 2) (Mul (Var "a") (Lit 3))+      assertEqual "assoc rules" simp $ Mul (Lit 6) (Var "a")+  , testCase "simp-assoc-xor1" $ do+      let simp = Expr.simplify $ Xor (Lit 2) (Xor (Var "a") (Lit 3))+      assertEqual "assoc rules" simp $ Xor (Lit 1) (Var "a")+  , testCase "simp-assoc-xor2" $ do+      let simp = Expr.simplify $ Xor (Lit 2) (Xor (Var "b") (Xor (Var "a") (Lit 3)))+      assertEqual "assoc rules" simp $ Xor (Lit 1) (Xor (Var "a") (Var "b"))+  , testCase "sign-extend-conc-1" $ do+      let p = Expr.sex (Lit 0) (Lit 0xff)+      assertEqual "" p (Lit Expr.maxLit)+      let p2 = Expr.sex (Lit 30) (Lit 0xff)+      assertEqual "" p2 (Lit 0xff)+      let p3 = Expr.sex (Lit 1) (Lit 0xff)+      assertEqual "" p3 (Lit 0xff)+      let p4 = Expr.sex (Lit 0) (Lit 0x1)+      assertEqual "" p4 (Lit 0x1)+      let p5 = Expr.sex (Lit 0) (Lit 0x0)+      assertEqual "" p5 (Lit 0x0)+  , testCase "writeWord-overflow" $ do+      let simp = Expr.simplify $ ReadByte (Lit 0x0) (WriteWord (Lit 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd) (Lit 0x0) (ConcreteBuf "\255\255\255\255"))+      assertEqual "writeWord overflow incorrect" (LitByte 0) simp+  , testCase "buffer-length-copy-slice-beyond-source1" $ do+      let simp = Expr.simplify $ BufLength (CopySlice (Lit 0x2) (Lit 0x2) (Lit 0x1) (ConcreteBuf "a") (ConcreteBuf ""))+      assertEqual "" (Lit 3) simp+  , testCase "copy-slice of size 0 is noop" $ do+        let simp = Expr.simplify $ BufLength $ CopySlice (Lit 0) (Lit 0x10) (Lit 0) (AbstractBuf "buffer") (ConcreteBuf "bimm")+        assertEqual "" (Lit 0x4) simp+  , testCase "length of concrete buffer is concrete" $ do+      let simp = Expr.simplify $ BufLength (ConcreteBuf "ab")+      assertEqual "" (Lit 2) simp+  , testCase "simplify read over write byte" $ do+      let simp = Expr.simplify $ ReadByte (Lit 0xf0000000000000000000000000000000000000000000000000000000000000) (WriteByte (And (Keccak (ConcreteBuf "")) (Lit 0x1)) (LitByte 0) (ConcreteBuf ""))+      assertEqual "" (LitByte 0) simp+  , testCase "storage-slot-single" $ do+      -- this tests that "" and "0"x32 is not equivalent in Keccak+      let x = SLoad (Add (Keccak (ConcreteBuf "")) (Lit 1)) (SStore (Keccak (ConcreteBuf "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL")) (Lit 0) (AbstractStore (SymAddr "stuff") Nothing))+      let simplified = Expr.simplify x+      let expected = SLoad (Add (Lit 1) (Keccak (ConcreteBuf ""))) (AbstractStore (SymAddr "stuff") Nothing)+      assertEqual "" expected simplified+  , testCase "word-eq-bug" $ do+      -- This test is actually OK because the simplified takes into account that it's impossible to find a+      -- near-collision in the keccak hash+      let x = (SLoad (Keccak (AbstractBuf "es")) (SStore (Add (Keccak (ConcreteBuf "")) (Lit 0x1)) (Lit 0xacab) (ConcreteStore (Map.empty))))+      let simplified = Expr.simplify x+      let expected = (SLoad (Keccak (AbstractBuf "es")) (ConcreteStore (Map.empty))) -- TODO: This should be simplified to (Lit 0)+      assertEqual "Must be equal, given keccak distance axiom" expected simplified+  , testCase "expr-simp-and-commut-assoc" $ do+      let+        a = And (Lit 4) (And (Lit 5) (Var "a"))+        b = Expr.simplify a+      assertEqual "Must reorder and perform And" (And (Lit 4) (Var "a")) b+  , testCase "expr-simp-order-eqbyte" $ do+      let+        a = EqByte (ReadByte (Lit 1) (AbstractBuf "b")) (ReadByte (Lit 1) (AbstractBuf "a"))+        b = Expr.simplify a+      assertEqual "Must order eqbyte params" b (EqByte (ReadByte (Lit 1) (AbstractBuf "a")) (ReadByte (Lit 1) (AbstractBuf "b")))+  , testCase "prop-simp-expr" $ do+      let+        successPath props = Success props mempty (ConcreteBuf "") mempty+        a = successPath [PEq (Add (Lit 1) (Lit 2)) (Sub (Lit 4) (Lit 1))]+        b = Expr.simplify a+      assertEqual "Must simplify down" (successPath []) b+  , testCase "simp-mul-neg1" $ do+      let simp = Expr.simplify $ Mul (Lit Expr.maxLit) (Var "x")+      assertEqual "mul by -1 is negation" (Sub (Lit 0) (Var "x")) simp+  , testCase "simp-not-plus1" $ do+      let simp = Expr.simplify $ Add (Lit 1) (Not (Var "x"))+      assertEqual "~x + 1 is negation" (Sub (Lit 0) (Var "x")) simp+  , testCase "simp-xor-maxlit-plus1" $ do+      let simp = Expr.simplify $ Add (Lit 1) (Xor (Lit Expr.maxLit) (Var "x"))+      assertEqual "xor(maxLit, x) + 1 is negation" (Sub (Lit 0) (Var "x")) simp+  , testCase "simp-double-not" $ do+      let simp = Expr.simplify $ Not (Not (Var "a"))+      assertEqual "Not should be concretized" (Var "a") simp+  ]++propSimplificationTests :: TestTree+propSimplificationTests = testGroup "prop-simplifications"+  [ testCase "simpProp-concrete-trues" $ do+      let+        t = [PBool True, PBool True]+        simplified = Expr.simplifyProps t+      assertEqual "Must be equal" [] simplified+  , testCase "simpProp-concrete-false1" $ do+      let+        t = [PBool True, PBool False]+        simplified = Expr.simplifyProps t+      assertEqual "Must be equal" [PBool False] simplified+  , testCase "simpProp-concrete-false2" $ do+      let+        t = [PBool False, PBool False]+        simplified = Expr.simplifyProps t+      assertEqual "Must be equal" [PBool False] simplified+  , testCase "simpProp-concrete-or-1" $ do+      let+        -- a = 5 && (a=4 || a=3)  -> False+        t = [PEq (Lit 5) (Var "a"), POr (PEq (Var "a") (Lit 4)) (PEq (Var "a") (Lit 3))]+        simplified = Expr.simplifyProps t+      assertEqual "Must be equal" [PBool False] simplified+  , ignoreTest $ testCase "simpProp-concrete-or-2" $ do+      let+        -- Currently does not work, because we don't do simplification inside+        --   POr/PAnd using canBeSat+        -- a = 5 && (a=4 || a=5)  -> a=5+        t = [PEq (Lit 5) (Var "a"), POr (PEq (Var "a") (Lit 4)) (PEq (Var "a") (Lit 5))]+        simplified = Expr.simplifyProps t+      assertEqual "Must be equal" [] simplified+  , testCase "simpProp-concrete-and-1" $ do+      let+        -- a = 5 && (a=4 && a=3)  -> False+        t = [PEq (Lit 5) (Var "a"), PAnd (PEq (Var "a") (Lit 4)) (PEq (Var "a") (Lit 3))]+        simplified = Expr.simplifyProps t+      assertEqual "Must be equal" [PBool False] simplified+  , testCase "simpProp-concrete-or-of-or" $ do+      let+        -- a = 5 && ((a=4 || a=6) || a=3)  -> False+        t = [PEq (Lit 5) (Var "a"), POr (POr (PEq (Var "a") (Lit 4)) (PEq (Var "a") (Lit 6))) (PEq (Var "a") (Lit 3))]+        simplified = Expr.simplifyProps t+      assertEqual "Must be equal" [PBool False] simplified+  , testCase "simpProp-inner-expr-simp" $ do+      let+        -- 5+1 = 6+        t = [PEq (Add (Lit 5) (Lit 1)) (Var "a")]+        simplified = Expr.simplifyProps t+      assertEqual "Must be equal" [PEq (Lit 6) (Var "a")] simplified+  , testCase "simpProp-inner-expr-simp-with-canBeSat" $ do+      let+        -- 5+1 = 6, 6 != 7+        t = [PAnd (PEq (Add (Lit 5) (Lit 1)) (Var "a")) (PEq (Var "a") (Lit 7))]+        simplified = Expr.simplifyProps t+      assertEqual "Must be equal" [PBool False] simplified+  , testCase "simpProp-inner-expr-bitwise-and" $ do+      let+        -- 1 & 2 != 2+        t = [PEq (And (Lit 1) (Lit 2)) (Lit 2)]+        simplified = Expr.simplifyProps t+      assertEqual "Must be equal" [PBool False] simplified+  , testCase "simpProp-inner-expr-bitwise-or" $ do+      let+        -- 2 | 4 == 6+        t = [PEq (Or (Lit 2) (Lit 4)) (Lit 6)]+        simplified = Expr.simplifyProps t+      assertEqual "Must be equal" [] simplified+  , testCase "simpProp-constpropagate-1" $ do+      let+        -- 5+1 = 6+        t = [PEq (Add (Lit 5) (Lit 1)) (Var "a"), PEq (Var "b") (Var "a")]+        simplified = Expr.simplifyProps t+      assertEqual "Must be equal" [PEq (Lit 6) (Var "a"), PEq (Lit 6) (Var "b")] simplified+  , testCase "simpProp-constpropagate-2" $ do+      let+        -- 5+1 = 6+        t = [PEq (Add (Lit 5) (Lit 1)) (Var "a"), PEq (Var "b") (Var "a"), PEq (Var "c") (Sub (Var "b") (Lit 1))]+        simplified = Expr.simplifyProps t+      assertEqual "Must be equal" [PEq (Lit 6) (Var "a"), PEq (Lit 6) (Var "b"), PEq (Lit 5) (Var "c")] simplified+  , testCase "simpProp-constpropagate-3" $ do+      let+        t = [ PEq (Add (Lit 5) (Lit 1)) (Var "a") -- a = 6+            , PEq (Var "b") (Var "a")             -- b = 6+            , PEq (Var "c") (Sub (Var "b") (Lit 1)) -- c = 5+            , PEq (Var "d") (Sub (Var "b") (Var "c"))] -- d = 1+        simplified = Expr.simplifyProps t+      assertEqual "Must  know d == 1" ((PEq (Lit 1) (Var "d")) `elem` simplified) True+  , testCase "PEq-and-PNot-PEq-1" $ do+      let a = [PEq (Lit 0x539) (Var "arg1"),PNeg (PEq (Lit 0x539) (Var "arg1"))]+      assertEqual "Must simplify to PBool False" (Expr.simplifyProps a) ([PBool False])+  , testCase "PEq-and-PNot-PEq-2" $ do+      let a = [PEq (Var "arg1") (Lit 0x539),PNeg (PEq (Lit 0x539) (Var "arg1"))]+      assertEqual "Must simplify to PBool False" (Expr.simplifyProps a) ([PBool False])+  , testCase "PEq-and-PNot-PEq-3" $ do+      let a = [PEq (Var "arg1") (Lit 0x539),PNeg (PEq (Var "arg1") (Lit 0x539))]+      assertEqual "Must simplify to PBool False" (Expr.simplifyProps a) ([PBool False])+  , testCase "propSimp-no-duplicate1" $ do+      let a = [PAnd (PGEq (Max (Lit 0x44) (BufLength (AbstractBuf "txdata"))) (Lit 0x44)) (PLT (Max (Lit 0x44) (BufLength (AbstractBuf "txdata"))) (Lit 0x10000000000000000)), PAnd (PGEq (Var "arg1") (Lit 0x0)) (PLEq (Var "arg1") (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)),PEq (Lit 0x63) (Var "arg2"),PEq (Lit 0x539) (Var "arg1"),PEq TxValue (Lit 0x0),PEq (IsZero (Eq (Lit 0x63) (Var "arg2"))) (Lit 0x0)]+      let simp = Expr.simplifyProps a+      assertEqual "must not duplicate" simp (nubOrd simp)+  , testCase "propSimp-no-duplicate2" $ do+      let a = [PNeg (PBool False),PAnd (PGEq (Max (Lit 0x44) (BufLength (AbstractBuf "txdata"))) (Lit 0x44)) (PLT (Max (Lit 0x44) (BufLength (AbstractBuf "txdata"))) (Lit 0x10000000000000000)),PAnd (PGEq (Var "arg2") (Lit 0x0)) (PLEq (Var "arg2") (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)),PAnd (PGEq (Var "arg1") (Lit 0x0)) (PLEq (Var "arg1") (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)),PEq (Lit 0x539) (Var "arg1"),PNeg (PEq (Lit 0x539) (Var "arg1")),PEq TxValue (Lit 0x0),PLT (BufLength (AbstractBuf "txdata")) (Lit 0x10000000000000000),PEq (IsZero (Eq (Lit 0x539) (Var "arg1"))) (Lit 0x0),PNeg (PEq (IsZero (Eq (Lit 0x539) (Var "arg1"))) (Lit 0x0)),PNeg (PEq (IsZero TxValue) (Lit 0x0))]+      let simp = Expr.simplifyProps a+      assertEqual "must not duplicate" simp (nubOrd simp)+  , testCase "full-order-prop1" $ do+      let a =[PNeg (PBool False),PAnd (PGEq (Max (Lit 0x44) (BufLength (AbstractBuf "txdata"))) (Lit 0x44)) (PLT (Max (Lit 0x44) (BufLength (AbstractBuf "txdata"))) (Lit 0x10000000000000000)),PAnd (PGEq (Var "arg2") (Lit 0x0)) (PLEq (Var "arg2") (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)),PAnd (PGEq (Var "arg1") (Lit 0x0)) (PLEq (Var "arg1") (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)),PEq (Lit 0x63) (Var "arg2"),PEq (Lit 0x539) (Var "arg1"),PEq TxValue (Lit 0x0),PLT (BufLength (AbstractBuf "txdata")) (Lit 0x10000000000000000),PEq (IsZero (Eq (Lit 0x63) (Var "arg2"))) (Lit 0x0),PEq (IsZero (Eq (Lit 0x539) (Var "arg1"))) (Lit 0x0),PNeg (PEq (IsZero TxValue) (Lit 0x0))]+      let simp = Expr.simplifyProps a+      assertEqual "must not duplicate" simp (nubOrd simp)+  , testCase "dont simplify abstract buffer of size 0" $ do+      let+        p = Expr.peq (BufLength (AbstractBuf "b")) (Lit 0x0)+        simp = Expr.simplifyProp p+      assertEqual "peq does not normalize arguments" (PEq (Lit (0x0)) (BufLength (AbstractBuf "b"))) p+      assertBool "" (p == simp)+  , testCase "simplify comparison GEq" $ do+      let simp = Expr.simplifyProp $ PEq (Lit 0x1) (GEq (Var "v") (Lit 0x2))+      assertEqual "" (PLEq (Lit 0x2) (Var "v")) simp+  , testCase "prop-simp-bool1" $ do+      let+        a = [PAnd (PBool True) (PBool False)]+        b = Expr.simplifyProps a+      assertEqual "Must simplify down" [PBool False] b+  , testCase "prop-simp-bool2" $ do+      let+        a = [POr (PBool True) (PBool False)]+        b = Expr.simplifyProps a+      assertEqual "Must simplify down" [] b+  , testCase "prop-simp-LT" $ do+      let+        a = [PLT (Lit 1) (Lit 2)]+        b = Expr.simplifyProps a+      assertEqual "Must simplify down" [] b+  , testCase "prop-simp-GEq" $ do+      let+        a = [PGEq (Lit 1) (Lit 2)]+        b = Expr.simplifyProps a+      assertEqual "Must simplify down" [PBool False] b+  , testCase "prop-simp-liteq-false" $ do+      let+        a = [PEq (LitByte 10) (LitByte 12)]+        b = Expr.simplifyProps a+      assertEqual "Must simplify down" [PBool False] b+  , testCase "prop-simp-concbuf-false" $ do+      let+        a = [PEq (ConcreteBuf "a") (ConcreteBuf "ab")]+        b = Expr.simplifyProps a+      assertEqual "Must simplify down" [PBool False] b+  , testCase "prop-simp-sort-eq-arguments" $ do+      let+        a = [PEq (Var "a") (Var "b"), PEq (Var "b") (Var "a")]+        b = Expr.simplifyProps a+      assertEqual "Must reorder and nubOrd" (length b) 1+  , testCase "prop-simp-demorgan-pneg-pand" $ do+      let+        a = [PNeg (PAnd (PEq (Lit 4) (Var "a")) (PEq (Lit 5) (Var "b")))]+        b = Expr.simplifyProps a+      assertEqual "Must apply demorgan" b [POr (PNeg (PEq (Lit 4) (Var "a"))) (PNeg (PEq (Lit 5) (Var "b")))]+  , testCase "prop-simp-demorgan-pneg-por" $ do+    let+      a = [PNeg (POr (PEq (Lit 4) (Var "a")) (PEq (Lit 5) (Var "b")))]+      b = Expr.simplifyProps a+    assertEqual "Must apply demorgan" [PNeg (PEq (Lit 4) (Var "a")), PNeg (PEq (Lit 5) (Var "b"))] b+  , testCase "prop-simp-lit0-or-both-0" $ do+    let+      a = [PEq (Lit 0) (Or (Var "a") (Var "b"))]+      b = Expr.simplifyProps a+    assertEqual "Must apply demorgan" [PEq (Lit 0) (Var "a"), PEq (Lit 0) (Var "b")] b+  , testCase "prop-simp-impl" $ do+      let+        a = [PImpl (PBool False) (PEq (Var "abc") (Var "bcd"))]+        b = Expr.simplifyProps a+      assertEqual "Must simplify down" [] b+  ]++constantPropagationTests :: TestTree+constantPropagationTests = testGroup "isUnsat-concrete-tests"+  [+    testCase "disjunction-left-false" $+    let+      t = [PEq (Var "x") (Lit 1), POr (PEq (Var "x") (Lit 0)) (PEq (Var "y") (Lit 1)), PEq (Var "y") (Lit 2)]+      propagated = Expr.constPropagate t+    in+    mustContainFalse propagated+  , testCase "disjunction-right-false" $+      let+        t = [PEq (Var "x") (Lit 1), POr (PEq (Var "y") (Lit 1)) (PEq (Var "x") (Lit 0)), PEq (Var "y") (Lit 2)]+        propagated = Expr.constPropagate t+      in+      mustContainFalse propagated+  , testCase "disjunction-both-false" $+      let+        t = [PEq (Var "x") (Lit 1), POr (PEq (Var "x") (Lit 2)) (PEq (Var "x") (Lit 0)), PEq (Var "y") (Lit 2)]+        propagated = Expr.constPropagate t+      in+      mustContainFalse propagated+  , ignoreTest $ testCase "disequality-and-equality" $+      let+        t = [PNeg (PEq (Lit 1) (Var "arg1")), PEq (Lit 1) (Var "arg1")]+        propagated = Expr.constPropagate t+      in+      mustContainFalse propagated+  , testCase "equality-and-disequality" $+      let+        t = [PEq (Lit 1) (Var "arg1"), PNeg (PEq (Lit 1) (Var "arg1"))]+        propagated = Expr.constPropagate t+      in+      mustContainFalse propagated+  ]+  where+    mustContainFalse props = assertBool "Expression must simplify to False" $ (PBool False) `elem` props++boundPropagationTests :: TestTree+boundPropagationTests =  testGroup "inequality-propagation-tests" [+  testCase "PLT-detects-impossible-constraint" $+  let+    -- x < 0 is impossible for unsigned integers+    t = [PLT (Var "x") (Lit 0)]+    propagated = Expr.constPropagate t+  in+    mustContainFalse propagated+  , testCase "PLT-overflow-check" $+      let+        -- maxLit < y is impossible+        t = [PLT (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (Var "y")]+        propagated = Expr.constPropagate t+      in+        mustContainFalse propagated+  , testCase "PGT-detects-impossible-constraint" $+      let+        -- x > maxLit is impossible+        t = [PGT (Var "x") (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)]+        propagated = Expr.constPropagate t+      in+        mustContainFalse propagated+  , testCase "PGT-overflow-check" $+      let+        -- 0 > y is impossible+        t = [PGT (Lit 0) (Var "y")]+        propagated = Expr.constPropagate t+      in+        mustContainFalse propagated+  , testCase "inequality-conflict-detection-narrow" $+      let+        -- x < 2 && x > 5 is impossible+        t = [PLT (Var "x") (Lit 2), PGT (Var "x") (Lit 5)]+        propagated = Expr.constPropagate t+      in+        mustContainFalse propagated+  , testCase "inequality-conflict-detection-wide" $+      let+        -- x < 5 && x > 10 is impossible+        t = [PLT (Var "x") (Lit 5), PGT (Var "x") (Lit 10)]+        propagated = Expr.constPropagate t+      in+        mustContainFalse propagated+  , testCase "inequality-tight-bounds-satisfied" $+      let+        -- x >= 5 && x <= 5 and x == 5 should be consistent+        t = [PGEq (Var "x") (Lit 5), PLEq (Var "x") (Lit 5), PEq (Var "x") (Lit 5)]+        propagated = Expr.constPropagate t+      in+        mustNotContainFalse propagated+  , testCase "inequality-tight-bounds-violated" $+      let+        -- x >= 5 && x <= 5 and x != 5 should be inconsistent+        t = [PGEq (Var "x") (Lit 5), PLEq (Var "x") (Lit 5), PNeg (PEq (Var "x") (Lit 5))]+        propagated = Expr.constPropagate t+      in+        mustContainFalse propagated+  , testCase "inequality-with-existing-equality-consistent" $+      let+        -- x == 5 && x < 10 is consistent+        t = [PEq (Var "x") (Lit 5), PLT (Var "x") (Lit 10)]+        propagated = Expr.constPropagate t+      in+        mustNotContainFalse propagated+  , testCase "inequality-with-existing-equality-inconsistent" $+      let+        -- x == 5 && x < 5 is inconsistent+        t = [PEq (Var "x") (Lit 5), PLT (Var "x") (Lit 5)]+        propagated = Expr.constPropagate t+      in+        mustContainFalse propagated+    ]+  where+    mustContainFalse props = assertBool "Expression must simplify to False" $ (PBool False) `elem` props+    mustNotContainFalse props = assertBool "Expression must NOT simplify to False" $ (PBool False) `notElem` props++comparisonTests :: TestTree+comparisonTests = testGroup "Prop comparison tests"+  [+    testCase "nubOrd-Prop-PLT" $ do+        let a = [ PLT (Lit 0x0) (ReadWord (ReadWord (Lit 0x0) (AbstractBuf "txdata")) (AbstractBuf "txdata"))+                , PLT (Lit 0x1) (ReadWord (ReadWord (Lit 0x0) (AbstractBuf "txdata")) (AbstractBuf "txdata"))+                , PLT (Lit 0x2) (ReadWord (ReadWord (Lit 0x0) (AbstractBuf "txdata")) (AbstractBuf "txdata"))+                , PLT (Lit 0x0) (ReadWord (ReadWord (Lit 0x0) (AbstractBuf "txdata")) (AbstractBuf "txdata"))]+        let simp = nubOrd a+            simp2 = List.nub a+        assertEqual "Must be 3-length" 3 (length simp)+        assertEqual "Must be 3-length" 3 (length simp2)+    , testCase "nubOrd-Prop-PEq" $ do+        let a = [ PEq (Lit 0x0) (ReadWord (Lit 0x0) (AbstractBuf "txdata"))+                , PEq (Lit 0x0) (ReadWord (Lit 0x1) (AbstractBuf "txdata"))+                , PEq (Lit 0x0) (ReadWord (Lit 0x2) (AbstractBuf "txdata"))+                , PEq (Lit 0x0) (ReadWord (Lit 0x0) (AbstractBuf "txdata"))]+        let simp = nubOrd a+            simp2 = List.nub a+        assertEqual "Must be 3-length" 3 (length simp)+        assertEqual "Must be 3-length" 3 (length simp2)+  ]++signExtendTests :: TestTree+signExtendTests = testGroup "SEx tests"+  [+    -- these should run without simplification to test the SMT solver's ability to handle sign extension+    testCase "sign-extend-1" $ do+        let p = (PEq (Lit 1) (SLT (Lit 1774544) (SEx (Lit 2) (Lit 1774567))))+        let simp = Expr.simplifyProps [p]+        assertEqual "Must simplify to PBool True" simp []+        res <- checkSat p+        assertBool "Must be satisfiable!" $ isSat res+  , testCase "sign-extend-2" $ do+      let p = (PEq (Lit 1) (SLT (SEx (Lit 2) (Var "arg1")) (Lit 0)))+      res <- checkSat p+      assertBool "Must be satisfiable!" $ isSat res+  , testCase "sign-extend-3" $ do+      let p = PAnd+              (PEq (Lit 1) (SLT (SEx (Lit 2) (Var "arg1")) (Lit 115792089237316195423570985008687907853269984665640564039457584007913128752664)))+              (PEq (Var "arg1") (SEx (Lit 2) (Var "arg1")))+      res <- checkSat p+      assertBool "Must be satisfiable!" $ isSat res+  , testProperty "sign-extend-vs-smt" $ withMaxSuccess 30 $ \(a :: W256, b :: W256) -> ioProperty $ do+      let p = (PEq (Var "arg1") (SEx (Lit (a `mod` 50)) (Lit b)))+      Cex cex <- checkSat p+      let Right (Lit v1) = subModel cex (Var "arg1")+      let [PEq (Lit v2) (Var "arg1")] = Expr.simplifyProps [p]+      pure $ v1 === v2+  ]++concretizationTests :: TestTree+concretizationTests = testGroup "Concretization tests"+  [ -- Arithmetic operations+    testCase "conc-add" $ do+      let simp = Expr.simplify $ Add (Lit 0x10) (Lit 0x20)+      assertEqual "Add should be concretized" (Lit 0x30) simp+  , testCase "conc-add-overflow" $ do+      let simp = Expr.simplify $ Add (Lit Expr.maxLit) (Lit 1)+      assertEqual "Add overflow should wrap" (Lit 0) simp+  , testCase "conc-sub" $ do+      let simp = Expr.simplify $ Sub (Lit 0x30) (Lit 0x10)+      assertEqual "Sub should be concretized" (Lit 0x20) simp+  , testCase "conc-sub-underflow" $ do+      let simp = Expr.simplify $ Sub (Lit 0) (Lit 1)+      assertEqual "Sub underflow should wrap" (Lit Expr.maxLit) simp+  , testCase "conc-mul" $ do+      let simp = Expr.simplify $ Mul (Lit 0x10) (Lit 0x20)+      assertEqual "Mul should be concretized" (Lit 0x200) simp+  , testCase "conc-mul-zero" $ do+      let simp = Expr.simplify $ Mul (Lit 0) (Lit 0x1234)+      assertEqual "Mul by zero" (Lit 0) simp+  , testCase "conc-div" $ do+      let simp = Expr.simplify $ Div (Lit 0x20) (Lit 0x10)+      assertEqual "Div should be concretized" (Lit 0x2) simp+  , testCase "conc-div-by-zero" $ do+      let simp = Expr.simplify $ Div (Lit 0x20) (Lit 0)+      assertEqual "Div by zero should be 0" (Lit 0) simp+  , testCase "conc-sdiv" $ do+      -- -6 / 3 = -2 in signed arithmetic+      -- -6 in W256 is maxLit - 5+      let neg6 = Expr.maxLit - 5+          neg2 = Expr.maxLit - 1+          simp = Expr.simplify $ SDiv (Lit neg6) (Lit 3)+      assertEqual "SDiv should handle signed division" (Lit neg2) simp+  , testCase "conc-sdiv-by-zero" $ do+      let simp = Expr.simplify $ SDiv (Lit 0x20) (Lit 0)+      assertEqual "SDiv by zero should be 0" (Lit 0) simp+  , testCase "conc-mod" $ do+      let simp = Expr.simplify $ Mod (Lit 0x25) (Lit 0x10)+      assertEqual "Mod should be concretized" (Lit 0x5) simp+  , testCase "conc-mod-by-zero" $ do+      let simp = Expr.simplify $ Mod (Lit 0x25) (Lit 0)+      assertEqual "Mod by zero should be 0" (Lit 0) simp+  , testCase "conc-smod" $ do+      -- -7 smod 3 = -1 in signed arithmetic+      let neg7 = Expr.maxLit - 6+          neg1 = Expr.maxLit+          simp = Expr.simplify $ SMod (Lit neg7) (Lit 3)+      assertEqual "SMod should handle signed mod" (Lit neg1) simp+  , testCase "conc-smod-by-zero" $ do+      let simp = Expr.simplify $ SMod (Lit 0x25) (Lit 0)+      assertEqual "SMod by zero should be 0" (Lit 0) simp+  , testCase "conc-addmod" $ do+      let simp = Expr.simplify $ AddMod (Lit 10) (Lit 10) (Lit 8)+      assertEqual "AddMod should be concretized" (Lit 4) simp+  , testCase "conc-addmod-by-zero" $ do+      let simp = Expr.simplify $ AddMod (Lit 10) (Lit 10) (Lit 0)+      assertEqual "AddMod by zero should be 0" (Lit 0) simp+  , testCase "conc-mulmod" $ do+      let simp = Expr.simplify $ MulMod (Lit 10) (Lit 10) (Lit 8)+      assertEqual "MulMod should be concretized" (Lit 4) simp+  , testCase "conc-mulmod-by-zero" $ do+      let simp = Expr.simplify $ MulMod (Lit 10) (Lit 10) (Lit 0)+      assertEqual "MulMod by zero should be 0" (Lit 0) simp+  , testCase "conc-exp" $ do+      let simp = Expr.simplify $ Exp (Lit 2) (Lit 10)+      assertEqual "Exp should be concretized" (Lit 1024) simp+  , testCase "conc-exp-zero" $ do+      let simp = Expr.simplify $ Exp (Lit 2) (Lit 0)+      assertEqual "x^0 should be 1" (Lit 1) simp+  , testCase "conc-sex" $ do+      -- sign-extend byte 0 of 0xff -> all 1s (since bit 7 is set)+      let simp = Expr.simplify $ SEx (Lit 0) (Lit 0xff)+      assertEqual "SEx should be concretized" (Lit Expr.maxLit) simp+  , testCase "conc-sex-no-extend" $ do+      -- sign-extend byte 30 of 0xff -> 0xff (bit 247 is not set)+      let simp = Expr.simplify $ SEx (Lit 30) (Lit 0xff)+      assertEqual "SEx should not extend" (Lit 0xff) simp++    -- Comparison operations+  , testCase "conc-lt-true" $ do+      let simp = Expr.simplify $ LT (Lit 1) (Lit 2)+      assertEqual "LT true" (Lit 1) simp+  , testCase "conc-lt-false" $ do+      let simp = Expr.simplify $ LT (Lit 2) (Lit 1)+      assertEqual "LT false" (Lit 0) simp+  , testCase "conc-gt-true" $ do+      let simp = Expr.simplify $ GT (Lit 2) (Lit 1)+      assertEqual "GT true" (Lit 1) simp+  , testCase "conc-gt-false" $ do+      let simp = Expr.simplify $ GT (Lit 1) (Lit 2)+      assertEqual "GT false" (Lit 0) simp+  , testCase "conc-leq-true" $ do+      let simp = Expr.simplify $ LEq (Lit 1) (Lit 1)+      assertEqual "LEq equal" (Lit 1) simp+  , testCase "conc-leq-false" $ do+      let simp = Expr.simplify $ LEq (Lit 2) (Lit 1)+      assertEqual "LEq false" (Lit 0) simp+  , testCase "conc-geq-true" $ do+      let simp = Expr.simplify $ GEq (Lit 2) (Lit 1)+      assertEqual "GEq true" (Lit 1) simp+  , testCase "conc-geq-false" $ do+      let simp = Expr.simplify $ GEq (Lit 1) (Lit 2)+      assertEqual "GEq false" (Lit 0) simp+  , testCase "conc-slt-true" $ do+      -- -1 < 0 in signed: maxLit < 0+      let simp = Expr.simplify $ SLT (Lit Expr.maxLit) (Lit 0)+      assertEqual "SLT true (negative < zero)" (Lit 1) simp+  , testCase "conc-slt-false" $ do+      let simp = Expr.simplify $ SLT (Lit 0) (Lit Expr.maxLit)+      assertEqual "SLT false (zero < negative)" (Lit 0) simp+  , testCase "conc-sgt-true" $ do+      let simp = Expr.simplify $ SGT (Lit 0) (Lit Expr.maxLit)+      assertEqual "SGT true (zero > negative)" (Lit 1) simp+  , testCase "conc-sgt-false" $ do+      let simp = Expr.simplify $ SGT (Lit Expr.maxLit) (Lit 0)+      assertEqual "SGT false (negative > zero)" (Lit 0) simp+  , testCase "conc-eq-true" $ do+      let simp = Expr.simplify $ Eq (Lit 42) (Lit 42)+      assertEqual "Eq true" (Lit 1) simp+  , testCase "conc-eq-false" $ do+      let simp = Expr.simplify $ Eq (Lit 42) (Lit 43)+      assertEqual "Eq false" (Lit 0) simp+  , testCase "conc-iszero-true" $ do+      let simp = Expr.simplify $ IsZero (Lit 0)+      assertEqual "IsZero of 0" (Lit 1) simp+  , testCase "conc-iszero-false" $ do+      let simp = Expr.simplify $ IsZero (Lit 42)+      assertEqual "IsZero of non-zero" (Lit 0) simp++    -- Bitwise operations+  , testCase "conc-and" $ do+      let simp = Expr.simplify $ And (Lit 0xff00) (Lit 0x0ff0)+      assertEqual "And should be concretized" (Lit 0x0f00) simp+  , testCase "conc-or" $ do+      let simp = Expr.simplify $ Or (Lit 0xff00) (Lit 0x00ff)+      assertEqual "Or should be concretized" (Lit 0xffff) simp+  , testCase "conc-xor" $ do+      let simp = Expr.simplify $ Xor (Lit 0xff00) (Lit 0x0ff0)+      assertEqual "Xor should be concretized" (Lit 0xf0f0) simp+  , testCase "conc-xor-same" $ do+      let simp = Expr.simplify $ Xor (Lit 0x1234) (Lit 0x1234)+      assertEqual "Xor of same values is 0" (Lit 0) simp+  , testCase "conc-xor-self" $ do+      let simp = Expr.simplify $ Xor (Var "x") (Var "x")+      assertEqual "x xor x = 0" (Lit 0) simp+  , testCase "conc-not" $ do+      let simp = Expr.simplify $ Not (Lit 0x55)+      assertEqual "Not should be concretized" (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa) simp+  , testCase "conc-not-zero" $ do+      let simp = Expr.simplify $ Not (Lit 0)+      assertEqual "Not of 0 is maxLit" (Lit Expr.maxLit) simp+  , testCase "conc-shl" $ do+      let simp = Expr.simplify $ SHL (Lit 4) (Lit 0xff)+      assertEqual "SHL should be concretized" (Lit 0xff0) simp+  , testCase "conc-shl-overflow" $ do+      let simp = Expr.simplify $ SHL (Lit 256) (Lit 0xff)+      assertEqual "SHL by 256 should be 0" (Lit 0) simp+  , testCase "conc-shr" $ do+      let simp = Expr.simplify $ SHR (Lit 4) (Lit 0xff)+      assertEqual "SHR should be concretized" (Lit 0xf) simp+  , testCase "conc-shr-overflow" $ do+      let simp = Expr.simplify $ SHR (Lit 257) (Lit 0xff)+      assertEqual "SHR by >256 should be 0" (Lit 0) simp+  , testCase "conc-sar-positive" $ do+      let simp = Expr.simplify $ SAR (Lit 4) (Lit 0xff)+      assertEqual "SAR positive should be like SHR" (Lit 0xf) simp+  , testCase "conc-sar-negative" $ do+      -- SAR of a negative number (MSB set) should fill with 1s+      -- maxLit has MSB set, shifting right by 4 should fill top 4 bits with 1s+      let simp = Expr.simplify $ SAR (Lit 4) (Lit Expr.maxLit)+      assertEqual "SAR negative should fill with 1s" (Lit Expr.maxLit) simp+  , testCase "conc-sar-large-shift-negative" $ do+      -- SAR by >= 255 of negative number -> maxBound (all 1s)+      let simp = Expr.simplify $ SAR (Lit 255) (Lit Expr.maxLit)+      assertEqual "SAR large shift negative" (Lit Expr.maxLit) simp+  , testCase "conc-sar-large-shift-positive" $ do+      -- SAR by >= 255 of positive number -> 0+      let simp = Expr.simplify $ SAR (Lit 255) (Lit 1)+      assertEqual "SAR large shift positive" (Lit 0) simp+  , testCase "conc-clz-zero" $ do+      let simp = Expr.simplify $ CLZ (Lit 0)+      assertEqual "CLZ of 0 should be 256" (Lit 256) simp+  , testCase "conc-clz-one" $ do+      let simp = Expr.simplify $ CLZ (Lit 1)+      assertEqual "CLZ of 1 should be 255" (Lit 255) simp+  , testCase "conc-clz-max" $ do+      let simp = Expr.simplify $ CLZ (Lit Expr.maxLit)+      assertEqual "CLZ of maxLit should be 0" (Lit 0) simp++    -- Min/Max operations+  , testCase "conc-min" $ do+      let simp = Expr.simplify $ Min (Lit 5) (Lit 10)+      assertEqual "Min should be concretized" (Lit 5) simp+  , testCase "conc-max" $ do+      let simp = Expr.simplify $ Max (Lit 5) (Lit 10)+      assertEqual "Max should be concretized" (Lit 10) simp++    -- Byte-level operations+  , testCase "conc-eqbyte-true" $ do+      let simp = Expr.eqByte (LitByte 0x42) (LitByte 0x42)+      assertEqual "EqByte equal" (Lit 1) simp+  , testCase "conc-eqbyte-false" $ do+      let simp = Expr.eqByte (LitByte 0x42) (LitByte 0x43)+      assertEqual "EqByte not equal" (Lit 0) simp+  , testCase "conc-joinbytes" $ do+      let simp = Expr.joinBytes (replicate 31 (LitByte 0) ++ [LitByte 0x42])+      assertEqual "JoinBytes all concrete" (Lit 0x42) simp+  , testCase "conc-joinbytes-msb" $ do+      let simp = Expr.joinBytes (LitByte 0xff : replicate 31 (LitByte 0))+      assertEqual "JoinBytes MSB set" (Lit 0xff00000000000000000000000000000000000000000000000000000000000000) simp++  -- Keccak+  , testCase "conc-keccak" $ do+      let simp = Expr.concKeccakSimpExpr $ Keccak (ConcreteBuf "")+      assertEqual "Keccak of empty should be concretized" (Lit (keccak' "")) simp+  , testCase "conc-keccak-nonempty" $ do+      let simp = Expr.concKeccakSimpExpr $ Keccak (ConcreteBuf "hello")+      assertEqual "Keccak of hello should be concretized" (Lit (keccak' "hello")) simp++  -- PEq simplifyProp over Props+  , testCase "conc-peq-lit-true" $ do+      let simp = Expr.simplifyProp $ PEq (Lit 42) (Lit 42)+      assertEqual "PEq Lit equal" (PBool True) simp+  , testCase "conc-peq-lit-false" $ do+      let simp = Expr.simplifyProp $ PEq (Lit 42) (Lit 43)+      assertEqual "PEq Lit not equal" (PBool False) simp+  , testCase "conc-peq-litaddr-true" $ do+      let simp = Expr.simplifyProp $ PEq (LitAddr 0x1234) (LitAddr 0x1234)+      assertEqual "PEq LitAddr equal" (PBool True) simp+  , testCase "conc-peq-litaddr-false" $ do+      let simp = Expr.simplifyProp $ PEq (LitAddr 0x1234) (LitAddr 0x5678)+      assertEqual "PEq LitAddr not equal" (PBool False) simp+  , testCase "conc-peq-litbyte-true" $ do+      let simp = Expr.simplifyProp $ PEq (LitByte 0xab) (LitByte 0xab)+      assertEqual "PEq LitByte equal" (PBool True) simp+  , testCase "conc-peq-litbyte-false" $ do+      let simp = Expr.simplifyProp $ PEq (LitByte 0xab) (LitByte 0xcd)+      assertEqual "PEq LitByte not equal" (PBool False) simp+  , testCase "conc-peq-concretebuf-true" $ do+      let simp = Expr.simplifyProp $ PEq (ConcreteBuf "hello") (ConcreteBuf "hello")+      assertEqual "PEq ConcreteBuf equal" (PBool True) simp+  , testCase "conc-peq-concretebuf-false" $ do+      let simp = Expr.simplifyProp $ PEq (ConcreteBuf "hello") (ConcreteBuf "world")+      assertEqual "PEq ConcreteBuf not equal" (PBool False) simp+  , testCase "conc-peq-concretestore-true" $ do+      let simp = Expr.simplifyProp $ PEq (ConcreteStore (Map.fromList [(1, 2)])) (ConcreteStore (Map.fromList [(1, 2)]))+      assertEqual "PEq ConcreteStore equal" (PBool True) simp+  , testCase "conc-peq-concretestore-false" $ do+      let simp = Expr.simplifyProp $ PEq (ConcreteStore (Map.fromList [(1, 2)])) (ConcreteStore (Map.fromList [(1, 3)]))+      assertEqual "PEq ConcreteStore not equal" (PBool False) simp+  , testCase "conc-peq-var-self" $ do+      let simp = Expr.simplifyProp $ PEq (Var "x") (Var "x")+      assertEqual "PEq Var self-equal" (PBool True) simp+  , testCase "conc-peq-symaddr-self" $ do+      let simp = Expr.simplifyProp $ PEq (SymAddr "a") (SymAddr "a")+      assertEqual "PEq SymAddr self-equal" (PBool True) simp+  , testCase "conc-peq-abstractbuf-self" $ do+      let simp = Expr.simplifyProp $ PEq (AbstractBuf "b") (AbstractBuf "b")+      assertEqual "PEq AbstractBuf self-equal" (PBool True) simp+  , testCase "conc-peq-abstractstore-self" $ do+      let simp = Expr.simplifyProp $ PEq (AbstractStore (LitAddr 0x1) Nothing) (AbstractStore (LitAddr 0x1) Nothing)+      assertEqual "PEq AbstractStore self-equal" (PBool True) simp+  -- Other simplifications over Props+  , testCase "conc-plt-true" $ do+      let simp = Expr.simplifyProp $ PLT (Lit 1) (Lit 2)+      assertEqual "PLT true" (PBool True) simp+  , testCase "conc-plt-false" $ do+      let simp = Expr.simplifyProp $ PLT (Lit 2) (Lit 1)+      assertEqual "PLT false" (PBool False) simp+  , testCase "conc-plt-equal" $ do+      let simp = Expr.simplifyProp $ PLT (Lit 5) (Lit 5)+      assertEqual "PLT equal is false" (PBool False) simp+  , testCase "conc-pgt-true" $ do+      let simp = Expr.simplifyProp $ PGT (Lit 2) (Lit 1)+      -- PGT a b is simplified to PLT b a+      assertEqual "PGT true" (PBool True) simp+  , testCase "conc-pgt-false" $ do+      let simp = Expr.simplifyProp $ PGT (Lit 1) (Lit 2)+      assertEqual "PGT false" (PBool False) simp+  , testCase "conc-pgeq-true" $ do+      let simp = Expr.simplifyProp $ PGEq (Lit 2) (Lit 1)+      -- PGEq a b is simplified to PLEq b a+      assertEqual "PGEq true" (PBool True) simp+  , testCase "conc-pgeq-false" $ do+      let simp = Expr.simplifyProp $ PGEq (Lit 1) (Lit 2)+      assertEqual "PGEq false" (PBool False) simp+  , testCase "conc-pgeq-equal" $ do+      let simp = Expr.simplifyProp $ PGEq (Lit 5) (Lit 5)+      assertEqual "PGEq equal is true" (PBool True) simp+  , testCase "conc-prop-pleq-true" $ do+      let simp = Expr.simplifyProp $ PLEq (Lit 1) (Lit 2)+      assertEqual "PLEq true" (PBool True) simp+  , testCase "conc-prop-pleq-false" $ do+      let simp = Expr.simplifyProp $ PLEq (Lit 2) (Lit 1)+      assertEqual "PLEq false" (PBool False) simp+  , testCase "conc-pneg-true" $ do+      let simp = Expr.simplifyProp $ PNeg (PBool True)+      assertEqual "PNeg True" (PBool False) simp+  , testCase "conc-pneg-false" $ do+      let simp = Expr.simplifyProp $ PNeg (PBool False)+      assertEqual "PNeg False" (PBool True) simp+  , testCase "conc-pand-true" $ do+      let simp = Expr.simplifyProp $ PAnd (PBool True) (PBool True)+      assertEqual "PAnd True True" (PBool True) simp+  , testCase "conc-pand-false" $ do+      let simp = Expr.simplifyProp $ PAnd (PBool True) (PBool False)+      assertEqual "PAnd True False" (PBool False) simp+  , testCase "conc-por-true" $ do+      let simp = Expr.simplifyProp $ POr (PBool False) (PBool True)+      assertEqual "POr False True" (PBool True) simp+  , testCase "conc-por-false" $ do+      let simp = Expr.simplifyProp $ POr (PBool False) (PBool False)+      assertEqual "POr False False" (PBool False) simp+  , testCase "conc-pimpl-true-true" $ do+      let simp = Expr.simplifyProp $ PImpl (PBool True) (PBool True)+      assertEqual "PImpl True True" (PBool True) simp+  , testCase "conc-pimpl-true-false" $ do+      let simp = Expr.simplifyProp $ PImpl (PBool True) (PBool False)+      assertEqual "PImpl True False" (PBool False) simp+  , testCase "conc-pimpl-false-any" $ do+      let simp = Expr.simplifyProp $ PImpl (PBool False) (PBool False)+      assertEqual "PImpl False _ is True" (PBool True) simp++    -- WAddr concretization+  , testCase "conc-waddr" $ do+      let simp = Expr.simplify $ WAddr (LitAddr 0x1234)+      assertEqual "WAddr LitAddr should be concretized" (Lit 0x1234) simp++    -- BufLength concretization+  , testCase "conc-buflength-empty" $ do+      let simp = Expr.simplify $ BufLength (ConcreteBuf "")+      assertEqual "BufLength of empty" (Lit 0) simp+  , testCase "conc-buflength-nonempty" $ do+      let simp = Expr.simplify $ BufLength (ConcreteBuf "hello")+      assertEqual "BufLength of hello" (Lit 5) simp++    -- ITE with concrete condition+  , testCase "conc-ite-true" $ do+      let simp = Expr.simplify $ ITE (Lit 1) (Lit 42) (Lit 99)+      assertEqual "ITE true takes true branch" (Lit 42) simp+  , testCase "conc-ite-false" $ do+      let simp = Expr.simplify $ ITE (Lit 0) (Lit 42) (Lit 99)+      assertEqual "ITE false takes false branch" (Lit 99) simp+  , testCase "conc-ite-same-branches" $ do+      let simp = Expr.simplify $ ITE (Var "c") (Lit 42) (Lit 42)+      assertEqual "ITE with same branches" (Lit 42) simp++    -- Eq in simplifier (structural equality)+  , testCase "conc-eq-self" $ do+      let simp = Expr.simplify $ Eq (Var "x") (Var "x")+      assertEqual "Eq self" (Lit 1) simp++    -- GT/LT/SLT/SGT special cases+  , testCase "conc-lt-zero" $ do+      let simp = Expr.simplify $ LT (Var "x") (Lit 0)+      assertEqual "Nothing is < 0 unsigned" (Lit 0) simp+  , testCase "conc-lt-maxlit" $ do+      let simp = Expr.simplify $ LT (Lit Expr.maxLit) (Var "x")+      assertEqual "maxLit is never < anything" (Lit 0) simp+  , testCase "conc-gt-maxlit" $ do+      -- GT _ maxLit becomes lt maxLit _ which is 0+      let simp = Expr.simplify $ GT (Var "x") (Lit Expr.maxLit)+      assertEqual "Nothing is > maxLit" (Lit 0) simp+  , testCase "conc-gt-zero" $ do+      let simp = Expr.simplify $ GT (Lit 0) (Var "x")+      assertEqual "0 is not > anything unsigned" (Lit 0) simp+  , testCase "conc-slt-min-signed" $ do+      let simp = Expr.simplify $ SLT (Var "x") (Lit Expr.minLitSigned)+      assertEqual "Nothing is SLT minLitSigned" (Lit 0) simp+  , testCase "conc-slt-max-signed" $ do+      let simp = Expr.simplify $ SLT (Lit Expr.maxLitSigned) (Var "x")+      assertEqual "maxLitSigned is not SLT anything" (Lit 0) simp+  , testCase "conc-sgt-min-signed" $ do+      let simp = Expr.simplify $ SGT (Lit Expr.minLitSigned) (Var "x")+      assertEqual "minLitSigned is not SGT anything" (Lit 0) simp+  , testCase "conc-sgt-max-signed" $ do+      let simp = Expr.simplify $ SGT (Var "x") (Lit Expr.maxLitSigned)+      assertEqual "Nothing is SGT maxLitSigned" (Lit 0) simp++    -- Sub self+  , testCase "conc-sub-self" $ do+      let simp = Expr.simplify $ Sub (Var "x") (Var "x")+      assertEqual "x - x = 0" (Lit 0) simp++    -- Div/SDiv special cases+  , testCase "conc-div-zero-num" $ do+      let simp = Expr.simplify $ Div (Lit 0) (Var "x")+      assertEqual "0 / x = 0" (Lit 0) simp+  , testCase "conc-div-zero-denom" $ do+      let simp = Expr.simplify $ Div (Var "x") (Lit 0)+      assertEqual "x / 0 = 0" (Lit 0) simp+  , testCase "conc-div-by-one" $ do+      let simp = Expr.simplify $ Div (Var "x") (Lit 1)+      assertEqual "x / 1 = x" (Var "x") simp+  , testCase "conc-sdiv-zero-num" $ do+      let simp = Expr.simplify $ SDiv (Lit 0) (Var "x")+      assertEqual "0 sdiv x = 0" (Lit 0) simp+  , testCase "conc-sdiv-zero-denom" $ do+      let simp = Expr.simplify $ SDiv (Var "x") (Lit 0)+      assertEqual "x sdiv 0 = 0" (Lit 0) simp+  , testCase "conc-sdiv-by-one" $ do+      let simp = Expr.simplify $ SDiv (Var "x") (Lit 1)+      assertEqual "x sdiv 1 = x" (Var "x") simp++    -- Mod/SMod special cases+  , testCase "conc-mod-zero-denom" $ do+      let simp = Expr.simplify $ Mod (Var "x") (Lit 0)+      assertEqual "x mod 0 = 0" (Lit 0) simp+  , testCase "conc-mod-zero-num" $ do+      let simp = Expr.simplify $ Mod (Lit 0) (Var "x")+      assertEqual "0 mod x = 0" (Lit 0) simp+  , testCase "conc-mod-self" $ do+      let simp = Expr.simplify $ Mod (Var "x") (Var "x")+      assertEqual "x mod x = 0" (Lit 0) simp+  , testCase "conc-smod-zero-denom" $ do+      let simp = Expr.simplify $ SMod (Var "x") (Lit 0)+      assertEqual "x smod 0 = 0" (Lit 0) simp+  , testCase "conc-smod-zero-num" $ do+      let simp = Expr.simplify $ SMod (Lit 0) (Var "x")+      assertEqual "0 smod x = 0" (Lit 0) simp+  , testCase "conc-smod-self" $ do+      let simp = Expr.simplify $ SMod (Var "x") (Var "x")+      assertEqual "x smod x = 0" (Lit 0) simp+  , testCase "conc-mod-one" $ do+      let simp = Expr.simplify $ Mod (Var "x") (Lit 1)+      assertEqual "x mod 1 = 0" (Lit 0) simp+  , testCase "conc-smod-one" $ do+      let simp = Expr.simplify $ SMod (Var "x") (Lit 1)+      assertEqual "x smod 1 = 0" (Lit 0) simp++    -- MulMod/AddMod zero cases+  , testCase "conc-mulmod-zero-first" $ do+      let simp = Expr.simplify $ MulMod (Lit 0) (Var "y") (Var "z")+      assertEqual "0 * y mod z = 0" (Lit 0) simp+  , testCase "conc-mulmod-zero-second" $ do+      let simp = Expr.simplify $ MulMod (Var "x") (Lit 0) (Var "z")+      assertEqual "x * 0 mod z = 0" (Lit 0) simp+  , testCase "conc-mulmod-zero-mod" $ do+      let simp = Expr.simplify $ MulMod (Var "x") (Var "y") (Lit 0)+      assertEqual "x * y mod 0 = 0" (Lit 0) simp+  , testCase "conc-addmod-zero-mod" $ do+      let simp = Expr.simplify $ AddMod (Var "x") (Var "y") (Lit 0)+      assertEqual "x + y mod 0 = 0" (Lit 0) simp++    -- Exp special cases+  , testCase "conc-exp-zero-exp" $ do+      let simp = Expr.simplify $ Exp (Var "x") (Lit 0)+      assertEqual "x^0 = 1" (Lit 1) simp+  , testCase "conc-exp-one-exp" $ do+      let simp = Expr.simplify $ Exp (Var "x") (Lit 1)+      assertEqual "x^1 = x" (Var "x") simp+  , testCase "conc-exp-one-base" $ do+      let simp = Expr.simplify $ Exp (Lit 1) (Var "x")+      assertEqual "1^x = 1" (Lit 1) simp++    -- Mul special cases+  , testCase "conc-mul-zero-left" $ do+      let simp = Expr.simplify $ Mul (Lit 0) (Var "x")+      assertEqual "0 * x = 0" (Lit 0) simp+  , testCase "conc-mul-one" $ do+      let simp = Expr.simplify $ Mul (Lit 1) (Var "x")+      assertEqual "1 * x = x" (Var "x") simp++    -- And/Or special cases+  , testCase "conc-and-zero" $ do+      let simp = Expr.simplify $ And (Lit 0) (Var "x")+      assertEqual "0 & x = 0" (Lit 0) simp+  , testCase "conc-and-maxlit" $ do+      let simp = Expr.simplify $ And (Lit Expr.maxLit) (Var "x")+      assertEqual "maxLit & x = x" (Var "x") simp+  , testCase "conc-and-self" $ do+      let simp = Expr.simplify $ And (Var "x") (Var "x")+      assertEqual "x & x = x" (Var "x") simp+  , testCase "conc-and-complement" $ do+      let simp = Expr.simplify $ And (Not (Var "x")) (Var "x")+      assertEqual "~x & x = 0" (Lit 0) simp+  , testCase "conc-or-zero" $ do+      let simp = Expr.simplify $ Or (Lit 0) (Var "x")+      assertEqual "0 | x = x" (Var "x") simp+  , testCase "conc-or-self" $ do+      let simp = Expr.simplify $ Or (Var "x") (Var "x")+      assertEqual "x | x = x" (Var "x") simp+  , testCase "conc-or-maxlit-left" $ do+      let simp = Expr.simplify $ Or (Lit Expr.maxLit) (Var "x")+      assertEqual "maxLit | x = maxLit" (Lit Expr.maxLit) simp+  , testCase "conc-or-maxlit-right" $ do+      let simp = Expr.simplify $ Or (Var "x") (Lit Expr.maxLit)+      assertEqual "x | maxLit = maxLit" (Lit Expr.maxLit) simp++    -- SHL/SHR with zero+  , testCase "conc-shl-zero-amount" $ do+      let simp = Expr.simplify $ SHL (Lit 0) (Var "x")+      assertEqual "x << 0 = x" (Var "x") simp+  , testCase "conc-shl-zero-value" $ do+      let simp = Expr.simplify $ SHL (Var "n") (Lit 0)+      assertEqual "0 << n = 0" (Lit 0) simp+  , testCase "conc-shl-large-shift" $ do+      let simp = Expr.simplify $ SHL (Lit 256) (Var "x")+      assertEqual "x << 256 = 0" (Lit 0) simp+  , testCase "conc-shl-very-large-shift" $ do+      let simp = Expr.simplify $ SHL (Lit 1000) (Var "x")+      assertEqual "x << 1000 = 0" (Lit 0) simp+  , testCase "conc-shr-zero-amount" $ do+      let simp = Expr.simplify $ SHR (Lit 0) (Var "x")+      assertEqual "x >> 0 = x" (Var "x") simp+  , testCase "conc-shr-zero-value" $ do+      let simp = Expr.simplify $ SHR (Var "n") (Lit 0)+      assertEqual "0 >> n = 0" (Lit 0) simp+  , testCase "conc-shr-large-shift" $ do+      let simp = Expr.simplify $ SHR (Lit 256) (Var "x")+      assertEqual "x >> 256 = 0" (Lit 0) simp+  , testCase "conc-shr-very-large-shift" $ do+      let simp = Expr.simplify $ SHR (Lit 1000) (Var "x")+      assertEqual "x >> 1000 = 0" (Lit 0) simp++    -- ReadWord+  , testCase "conc-readword" $ do+      let simp = Expr.simplify $ ReadWord (Lit 0) (WriteWord (Lit 0) (Lit 0x42) (AbstractBuf "abc"))+      assertEqual "ReadWord from WriteWord" (Lit 0x42) simp++    -- WriteByte+  , testCase "conc-writebyte" $ do+      let simp = Expr.simplify $ ReadByte (Lit 0) (WriteByte (Lit 0) (LitByte 0xff) (AbstractBuf "abc"))+      assertEqual "WriteByte then ReadByte" (LitByte 0xff) simp++    -- WriteWord+  , testCase "conc-writeword" $ do+      let simp = Expr.simplify $ ReadWord (Lit 0) (WriteWord (Lit 0) (Lit 0xab) (AbstractBuf "abc"))+      assertEqual "WriteWord then ReadWord" (Lit 0xab) simp++    -- CopySlice of zero size should be a no-op+  , testCase "conc-copyslice-zero-size" $ do+      let simp = Expr.simplify $ CopySlice (Lit 0) (Lit 0) (Lit 0) (AbstractBuf "src") (AbstractBuf "dst")+      assertEqual "CopySlice size 0 is noop" (AbstractBuf "dst") simp++    -- IndexWord+  , testCase "conc-indexword" $ do+      let simp = Expr.simplify $ IndexWord (Lit 31) (Lit 0x42)+      assertEqual "IndexWord LSB" (LitByte 0x42) simp+  , testCase "conc-indexword-msb" $ do+      let simp = Expr.simplify $ IndexWord (Lit 0) (Lit 0xff00000000000000000000000000000000000000000000000000000000000000)+      assertEqual "IndexWord MSB" (LitByte 0xff) simp++    -- SEx special case+  , testCase "conc-sex-zero" $ do+      let simp = Expr.simplify $ SEx (Var "b") (Lit 0)+      assertEqual "SEx of 0 is 0" (Lit 0) simp++    -- Min with zero+  , testCase "conc-min-zero" $ do+      let simp = Expr.simplify $ Min (Lit 0) (Var "x")+      assertEqual "min(0, x) = 0" (Lit 0) simp+  ]++-- | Regression tests for W256 unsigned wrapping bugs in the expression simplifier.+-- Each test constructs an expression that triggers a specific wrapping edge case where+-- the simplifier previously produced a result that was not semantically equivalent to+-- the original expression. See commit c4d33256 for the fix.+wrappingTests :: TestTree+wrappingTests = testGroup "W256 wrapping edge cases"+  [ -- A1: readByte, CopySlice before-region, symbolic size+    -- dstOffset near maxBound makes dstOffset+size potentially wrap to low addresses.+    -- Old code only checked dstOffset > x, missing the wrapping case.+    testCase "readByte-copySlice-before-symSize-wrap" $ do+      let dstOff = maxBound - 5 :: W256+          x = 3 :: W256+          buf = CopySlice (Lit 0) (Lit dstOff) (Var "sz") (AbstractBuf "src") (AbstractBuf "dst")+          full = ReadByte (Lit x) buf+          simplified = Expr.readByte (Lit x) buf+      result <- proveEquivExpr full simplified+      assertBool "readByte with near-maxBound dstOffset and symbolic size" result++  , -- A2: readByte, CopySlice before-region, concrete size, dstOffset+size wraps+    -- dstOffset + size wraps past zero, overwriting low addresses.+    testCase "readByte-copySlice-before-concSize-wrap" $ do+      let dstOff = maxBound - 30 :: W256+          size = 58 :: W256+          x = 10 :: W256+          buf = CopySlice (Lit 0) (Lit dstOff) (Lit size) (AbstractBuf "src") (AbstractBuf "dst")+          full = ReadByte (Lit x) buf+          simplified = Expr.readByte (Lit x) buf+      result <- proveEquivExpr full simplified+      assertBool "readByte with wrapping dstOffset+size" result++  , -- B1: readWord, CopySlice before-region, symbolic size+    -- Same wrapping issue as A1 but for 32-byte reads.+    testCase "readWord-copySlice-before-symSize-wrap" $ do+      let dstOff = maxBound - 5 :: W256+          x = 3 :: W256+          buf = CopySlice (Lit 0) (Lit dstOff) (Var "sz") (AbstractBuf "src") (AbstractBuf "dst")+          full = ReadWord (Lit x) buf+          simplified = Expr.readWord (Lit x) buf+      result <- proveEquivExpr full simplified+      assertBool "readWord with near-maxBound dstOffset and symbolic size" result++  , -- B2: readWord, CopySlice before-region, concrete size, wrapping+    -- Same as A2 but for word reads.+    testCase "readWord-copySlice-before-concSize-wrap" $ do+      let dstOff = maxBound - 30 :: W256+          size = 100 :: W256+          x = 3 :: W256+          buf = CopySlice (Lit 0) (Lit dstOff) (Lit size) (AbstractBuf "src") (AbstractBuf "dst")+          full = ReadWord (Lit x) buf+          simplified = Expr.readWord (Lit x) buf+      result <- proveEquivExpr full simplified+      assertBool "readWord with wrapping dstOffset+size" result++  , -- C1: readWord, CopySlice before-region, x+32 wraps past maxBound+    -- When x is near maxBound, x+32 wraps to a small value, making+    -- dstOffset >= x+32 true even though the 32-byte read wraps into the CopySlice region.+    testCase "readWord-copySlice-before-x32-wrap" $ do+      let dstOff = maxBound - 100 :: W256+          x = maxBound - 10 :: W256  -- x+32 = 21, wraps!+          buf = CopySlice (Lit 0) (Lit dstOff) (Var "sz") (AbstractBuf "src") (AbstractBuf "dst")+          full = ReadWord (Lit x) buf+          simplified = Expr.readWord (Lit x) buf+      result <- proveEquivExpr full simplified+      assertBool "readWord with x+32 wrapping" result++  , -- C2: readWord, CopySlice after-region, concrete size, dstOffset+size wraps+    -- x is inside the wrapped CopySlice region => the expression cannot be simplified+    testCase "readWord-copySlice-after-concSize-wrap" $ do+      let dstOff = maxBound - 100 :: W256+          size = 200 :: W256+          x = maxBound - 50 :: W256+          buf = CopySlice (Lit 0) (Lit dstOff) (Lit size) (AbstractBuf "src") (AbstractBuf "dst")+          full = ReadWord (Lit x) buf+          simplified = Expr.readWord (Lit x) buf+      result <- proveEquivExpr full simplified+      assertBool "readWord after-region with wrapping dstOffset+size" result++  , -- D1: readWord, ConcreteBuf, idx+31 wraps+    -- readWord previously did not handle this case correctly due to overflow of idx+31+    testCase "readWord-concreteBuf-wrap" $ do+      let idx = maxBound - 19 :: W256+          buf = ConcreteBuf (BS.pack [1..50])+          full = ReadWord (Lit idx) buf+          simplified = Expr.readWord (Lit idx) buf+      result <- proveEquivExpr full simplified+      assertBool "readWord with idx+31 wrapping on ConcreteBuf" result++  , -- D2: readWord, non-concrete buf, idx+31 wraps+    -- Same wrapping issue as D1 but with a non-concrete buffer (WriteByte over ConcreteBuf).+    -- WriteByte at maxBound-5 is within the read range and won't be incorrectly skipped.+    testCase "readWord-abstractBuf-wrap" $ do+      let idx = maxBound - 10 :: W256+          buf = WriteByte (Lit (maxBound - 5)) (LitByte 0xAB) (ConcreteBuf (BS.pack [1..50]))+          full = ReadWord (Lit idx) buf+          simplified = Expr.readWord (Lit idx) buf+      result <- proveEquivExpr full simplified+      assertBool "readWord with idx+31 wrapping on abstract buf" result++  , -- E1: copySlice fully concrete, srcOffset near maxBound wraps+    -- When srcOffset is near maxBound, srcOffset > BS.length triggers zero-fill,+    -- but the wrapped indices actually read real source data.+    testCase "copySlice-concrete-srcOffset-wrap" $ do+      let srcOff = maxBound - 9 :: W256+          size = 20 :: W256+          srcBuf = ConcreteBuf (BS.pack [1..50])+          dstBuf = ConcreteBuf (BS.pack (replicate 50 0))+          full = CopySlice (Lit srcOff) (Lit 0) (Lit size) srcBuf dstBuf+          simplified = Expr.copySlice (Lit srcOff) (Lit 0) (Lit size) srcBuf dstBuf+      result <- proveEquivExpr full simplified+      assertBool "copySlice with wrapping srcOffset" result+  ]++fuzzTests :: TestTree+fuzzTests = adjustOption (\(Test.Tasty.QuickCheck.QuickCheckTests n) -> Test.Tasty.QuickCheck.QuickCheckTests (div n 2)) $+  testGroup "ExprFuzzTests" [equivalenceSanityChecks, simplifierFuzzTests]++equivalenceSanityChecks :: TestTree+equivalenceSanityChecks = testGroup "Expr equivalence sanity checks"+  [ testCase "read-beyond-bound (negative-test)" $ do+      let+        e1 = CopySlice (Lit 1) (Lit 0) (Lit 2) (ConcreteBuf "a") (ConcreteBuf "")+        e2 = ConcreteBuf "Definitely not the same!"+      equal <- proveEquivExpr e1 e2+      assertBool "Should not be equivalent!" $ not equal+  , testProperty "expr equality is satisfiable" $ \(buf, idx) -> ioProperty $ do+        let simplified = Expr.readWord idx buf+            full = ReadWord idx buf+        res <- checkSat (Expr.peq full simplified)+        pure $ isSatOrUnknown res+  ]++-- These tests fuzz the simplifier by generating a random expression,+-- applying some simplification rules, and then using the smt encoding to+-- check that the simplified version is semantically equivalent to the+-- unsimplified one+simplifierFuzzTests :: TestTree+simplifierFuzzTests = testGroup "SimplifierPropertyTests"+    [ testProperty  "buffer-simplification" $ \(expr :: Expr Buf) -> ioProperty $ proveEquivExpr expr (Expr.simplify expr)+    , testProperty  "buffer-simplification-len" $ \(expr :: Expr Buf) -> ioProperty $ do+        let buflen = BufLength expr+        proveEquivExpr buflen (Expr.simplify buflen)+    , testProperty "store-simplification" $ \(expr :: Expr Storage) -> ioProperty $ proveEquivExpr expr (Expr.simplify expr)+    , testProperty "load-simplification" $ \(GenWriteStorageLoad expr) -> ioProperty $ proveEquivExpr expr (Expr.simplify expr)+    , ignoreTest $ testProperty "load-decompose" $ \(GenWriteStorageLoad expr) -> ioProperty $ do+        let simp = Expr.simplify expr+        let decomposed = fromMaybe simp $ mapExprM Expr.decomposeStorage simp+        proveEquivExpr expr decomposed+    , testProperty "byte-simplification" $ \(expr :: Expr Byte) -> ioProperty $ proveEquivExpr expr (Expr.simplify expr)+    , askOption $ \(QuickCheckTests n) -> testProperty "word-simplification" $ withMaxSuccess (min n 20) $ \(ZeroDepthWord expr) ->+        ioProperty $ proveEquivExpr expr (Expr.simplify expr)+    , testProperty "readStorage-equivalance" $ \(store, slot) -> ioProperty $ do+        let simplified = Expr.readStorage' slot store+            full = SLoad slot store+        proveEquivExpr full simplified+    , testProperty "writeStorage-equivalance" $ \(val, GenWriteStorageExpr (slot, store)) -> ioProperty $ do+        let simplified = Expr.writeStorage slot val store+            full = SStore slot val store+        proveEquivExpr full simplified+    , testProperty "readWord-equivalance" $ \(buf, idx) -> ioProperty $ do+        let simplified = Expr.readWord idx buf+            full = ReadWord idx buf+        proveEquivExpr full simplified+    , testProperty "writeWord-equivalance" $ \(idx, val, WriteWordBuf buf) -> ioProperty $ do+        let simplified = Expr.writeWord idx val buf+            full = WriteWord idx val buf+        proveEquivExpr full simplified+    , testProperty "arith-simplification" $ \(_ :: Int) -> ioProperty $ do+        expr <- generate . sized $ genWordArith 15+        let simplified = Expr.simplify expr+        proveEquivExpr expr simplified+    , testProperty "readByte-equivalance" $ \(buf, idx) -> ioProperty $ do+        let simplified = Expr.readByte idx buf+            full = ReadByte idx buf+        proveEquivExpr full simplified+    -- we currently only simplify concrete writes over concrete buffers so that's what we test here+    , testProperty "writeByte-equivalance" $ \(LitOnly val, LitOnly buf, GenWriteByteIdx idx) -> ioProperty $ do+        let simplified = Expr.writeByte idx val buf+            full = WriteByte idx val buf+        proveEquivExpr full simplified+    , testProperty "copySlice-equivalance" $ \(srcOff, GenCopySliceBuf src, GenCopySliceBuf dst, LitWord @300 size) -> ioProperty $ do+        -- we bias buffers to be concrete more often than not+        dstOff <- generate (maybeBoundedLit 100_000)+        let simplified = Expr.copySlice srcOff dstOff size src dst+            full = CopySlice srcOff dstOff size src dst+        proveEquivExpr full simplified+    , testProperty "indexWord-equivalence" $ \(src, LitWord @50 idx) -> ioProperty $ do+        let simplified = Expr.indexWord idx src+            full = IndexWord idx src+        proveEquivExpr full simplified+    , testProperty "pow-base2-simp" $ \(_ :: Int) -> ioProperty $ do+        expo <- generate . sized $ genWordArith 15+        let full = Exp (Lit 2) expo+            simplified = Expr.simplify full+        proveEquivExpr full simplified+    , testProperty "pow-low-exponent-simp" $ \(LitWord @100 expo) -> ioProperty $ do+        base <- generate . sized $ genWordArith 15+        let full = Exp base expo+            simplified = Expr.simplify full+        proveEquivExpr full simplified+    , testProperty "indexWord-mask-equivalence" $ \(src :: Expr EWord, LitWord @35 idx) -> ioProperty $ do+        mask <- generate $ do+          pow <- arbitrary :: Gen Int+          frequency+           [ (1, pure $ Lit $ (shiftL 1 (pow `mod` 256)) - 1)        -- potentially non byte aligned+           , (1, pure $ Lit $ (shiftL 1 ((pow * 8) `mod` 256)) - 1)  -- byte aligned+           ]+        let+          input = And mask src+          simplified = Expr.indexWord idx input+          full = IndexWord idx input+        proveEquivExpr full simplified+    , testProperty "toList-equivalance" $ \buf -> ioProperty $ do+        let+          -- transforms the input buffer to give it a known length+          fixLength :: Expr Buf -> Gen (Expr Buf)+          fixLength = mapExprM go+            where+              go :: Expr a -> Gen (Expr a)+              go = \case+                WriteWord _ val b -> WriteWord <$> idx <*> (pure val) <*> (pure b)+                WriteByte _ val b -> WriteByte <$> idx <*> (pure val) <*> (pure b)+                CopySlice so _ sz src dst -> CopySlice <$> (pure so) <*> idx <*> (pure sz) <*> (pure src) <*> (pure dst)+                AbstractBuf _ -> cbuf+                e -> pure e+              cbuf = do+                bs <- arbitrary+                pure $ ConcreteBuf bs+              idx = do+                w <- arbitrary+                -- we use 100_000 as an upper bound for indices to keep tests reasonably fast here+                pure $ Lit (w `mod` 100_000)++        input <- generate $ fixLength buf+        case Expr.toList input of+          Nothing -> do+            pure True -- ignore cases where the buf cannot be represented as a list+          Just asList -> do+            let asBuf = Expr.fromList asList+            proveEquivExpr asBuf input+    , testProperty "simplifyProp-equivalence-lit" $ \(LitProp p) -> ioProperty $ do+        let simplified = Expr.simplifyProps [p]+        case simplified of+          [] -> proveEquivProp (PBool True) p+          [val@(PBool _)] -> proveEquivProp val p+          _ -> assertFailure "must evaluate down to a literal bool"+    , testProperty "simplifyProp-equivalence-sym" $ \(p) -> ioProperty $ proveEquivProp p (Expr.simplifyProp p)+    , testProperty "simplify-joinbytes" $ \(SymbolicJoinBytes exprList) -> ioProperty $ do+        let x = joinBytesFromList exprList+        let simplified = Expr.simplify x+        proveEquivExpr x simplified+    , testProperty "simpProp-equivalence-sym-Prop" $ withMaxSuccess 20 $ \(ps :: [Prop]) -> ioProperty $ do+        let simplified = pand (Expr.simplifyProps ps)+        proveEquivProp (pand ps) simplified+    , testProperty "simpProp-equivalence-sym-LitProp" $ \(LitProp p) -> ioProperty $ do+        let simplified = pand (Expr.simplifyProps [p])+        proveEquivProp p simplified+    , testProperty "storage-slot-simp-property" $ \(StorageExp s) -> ioProperty $ do+        -- we have to run `Expr.litToKeccak` on the unsimplified system, or+        -- we'd need some form of minimal simplifier for things to work out. As long as+        -- we trust the litToKeccak, this is fine, as that function is stand-alone,+        -- and quite minimal+        let s2 = Expr.litToKeccak s+        let simplified = Expr.simplify s2+        proveEquivExpr s2 simplified+    , testProperty "expr-ordering" $ \(p) -> ioProperty $ do+      let simp = Expr.simplifyProp p+      pure $ Expr.checkLHSConstProp simp+    ]+    {- NOTE: These tests were designed to test behaviour on reading from a buffer such that the indices overflow 2^256.+           However, such scenarios are impossible in the real world (the operation would run out of gas). The problem+           is that the behaviour of bytecode interpreters does not match the semantics of SMT. Intrepreters just+           return all zeroes for any read beyond buffer size, while in SMT reading multiple bytes may lead to overflow+           on indices and subsequently to reading from the beginning of the buffer (wrap-around semantics).+  , testGroup "concrete-buffer-simplification-large-index" [+      test "copy-slice-large-index-nooverflow" $ do+        let+          e = CopySlice (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (Lit 0x0) (Lit 0x1) (ConcreteBuf "a") (ConcreteBuf "")+          s = Expr.simplify e+        equal <- checkEquiv e s+        assertEqualM "Must be equal" True equal+    , test "copy-slice-overflow-back-into-source" $ do+        let+          e = CopySlice (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (Lit 0x0) (Lit 0x2) (ConcreteBuf "a") (ConcreteBuf "")+          s = Expr.simplify e+        equal <- checkEquiv e s+        assertEqualM "Must be equal" True equal+    , test "copy-slice-overflow-beyond-source" $ do+        let+          e = CopySlice (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (Lit 0x0) (Lit 0x3) (ConcreteBuf "a") (ConcreteBuf "")+          s = Expr.simplify e+        equal <- checkEquiv e s+        assertEqualM "Must be equal" True equal+    , test "copy-slice-overflow-beyond-source-into-nonempty" $ do+        let+          e = CopySlice (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (Lit 0x0) (Lit 0x3) (ConcreteBuf "a") (ConcreteBuf "b")+          s = Expr.simplify e+        equal <- checkEquiv e s+        assertEqualM "Must be equal" True equal+    , test "read-word-overflow-back-into-source" $ do+        let+          e = ReadWord (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (ConcreteBuf "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk")+          s = Expr.simplify e+        equal <- checkEquiv e s+        assertEqualM "Must be equal" True equal+  ]+  -}+++proveEquivProp :: Prop -> Prop -> IO Bool+proveEquivProp a b+  | a == b = pure True+  | otherwise = do+      res <- checkSat $ PNeg ((PImpl a b) .&& (PImpl b a))+      pure $ isUnsatOrUnknown res++proveEquivExpr :: Typeable a => Expr a -> Expr a -> IO Bool+proveEquivExpr a b+  | a == b = pure True+  | otherwise = do+      res <- (checkSat $ a ./= b)+      pure $ isUnsatOrUnknown res++checkSat :: Prop -> IO SMTResult+checkSat p = runEnv (Env {config = equivConfig}) $+    withOneBitwuzla $ \solvers -> checkSatWithProps solvers [p]++equivConfig :: Config+equivConfig = defaultConfig {simp = False, dumpQueries = False}++withOneBitwuzla :: App m => (SolverGroup -> m a) -> m a+withOneBitwuzla = withSolvers Bitwuzla 1 (Just 1) defMemLimit++isSat :: SMTResult -> Bool+isSat = isCex++isSatOrUnknown :: SMTResult -> Bool+isSatOrUnknown res = isSat res || isUnknown res++isUnsatOrUnknown :: SMTResult -> Bool+isUnsatOrUnknown res = isQed res || isUnknown res
+ test/EVM/Expr/Generator.hs view
@@ -0,0 +1,573 @@+module EVM.Expr.Generator where++import Prelude hiding (LT, GT)++import Control.Monad (replicateM)+import Data.ByteString (ByteString)+import Data.ByteString qualified as BS+import Data.DoubleWord (Word128, Word256, Word160, fromHiAndLo)+import Data.Proxy+import Data.Text (Text)+import Data.Text qualified as T (pack)+import Data.Vector qualified as V+import Data.Word (Word8, Word64)+import GHC.TypeLits+import Witch (into)++import Test.QuickCheck.Arbitrary+import Test.QuickCheck.Gen+import Test.QuickCheck.Instances.ByteString()++import EVM.Types (Expr(..), EType(..), W256(..), W64(..), internalError, Addr(..), Prop(..), ContractCode(..), RuntimeCode(..), EvmError(..))+import EVM.Expr qualified as Expr++-- GenWriteStorageLoad+newtype GenWriteStorageLoad = GenWriteStorageLoad (Expr EWord)+  deriving (Show, Eq)++instance Arbitrary GenWriteStorageLoad where+  arbitrary = do+    load <- genStorageLoad 10+    pure $ GenWriteStorageLoad load++    where+      genStorageLoad :: Int -> Gen (Expr EWord)+      genStorageLoad sz = SLoad <$> genStorageKey <*> genStorage (sz `div` 10)++genStorage :: Int -> Gen (Expr Storage)+genStorage 0 = oneof+  [ AbstractStore <$> arbitrary <*> (pure Nothing)+  , ConcreteStore <$> resize 5 arbitrary+  ]+genStorage sz = SStore <$> genStorageKey <*> val <*> subStore+  where+    subStore = genStorage (sz `div` 10)+    val = defaultWord (sz `div` 5)++genStorageKey :: Gen (Expr EWord)+genStorageKey = frequency+     -- array slot+    [ (4, Expr.ArraySlotWithOffs <$> (genByteStringKey 32) <*> (genLit 5))+    , (4, Expr.ArraySlotZero <$> (genByteStringKey 32))+     -- mapping slot+    , (8, Expr.MappingSlot <$> (genByteStringKey 64) <*> (genLit 5))+     -- small slot+    , (4, genLit 20)+    -- unrecognized slot type+    , (1, genLit 5)+    ]++genByteStringKey :: W256 -> Gen (ByteString)+genByteStringKey len = do+  b :: Word8 <- arbitrary+  pure $ BS.pack ([ 0 | _ <- [0..(len-2)]] ++ [b `mod` 5])++genLit :: W256 -> Gen (Expr EWord)+genLit bound = do+  w <- arbitrary+  pure $ Lit (w `mod` bound)++defaultWord :: Int -> Gen (Expr EWord)+defaultWord = genWord 10++genWord :: Int -> Int -> Gen (Expr EWord)+genWord litFreq 0 = frequency+  [ (litFreq, do+      val <- frequency+       [ (10, fmap (`mod` 100) arbitrary)+       , (1, pure 0)+       , (1, pure Expr.maxLit)+       , (1, arbitrary)+       ]+      pure $ Lit val+    )+  , (1, oneof+      [ pure Origin+      , pure Coinbase+      , pure Timestamp+      , pure BlockNumber+      , pure PrevRandao+      , pure GasLimit+      , pure ChainId+      , pure BaseFee+      --, liftM2 SelfBalance arbitrary arbitrary+      --, liftM2 Gas arbitrary arbitrary+      , fmap Lit arbitrary+      , fmap joinBytesFromList $ replicateM 32 arbitrary+      , fmap Var (genName "word")+      ]+    )+  ]+genWord litFreq sz = frequency+  [ (litFreq, do+      val <- frequency+       [ (10, fmap (`mod` 100) arbitrary)+       , (1, arbitrary)+       ]+      pure $ Lit val+    )+  , (1, oneof+    [ Add <$> subWord <*> subWord+    , Sub <$> subWord <*> subWord+    , Mul <$> subWord <*> subWord+    , Div <$> subWord <*> subWord+    , SDiv <$> subWord <*> subWord+    , Mod <$> subWord <*> subWord+    , SMod <$> subWord <*> subWord+    -- We skip AddMod, MulMod and Exp intentionally+    , SEx <$> subWord <*> subWord+    , Min <$> subWord <*> subWord+    , LT <$> subWord <*> subWord+    , GT <$> subWord <*> subWord+    , LEq <$> subWord <*> subWord+    , GEq <$> subWord <*> subWord+    , SLT <$> subWord <*> subWord+    , SGT <$> subWord <*> subWord+    , Eq <$> subWord <*> subWord+    , IsZero <$> subWord+    , And <$> subWord <*> subWord+    , Or <$> subWord <*> subWord+    , Xor <$> subWord <*> subWord+    , Not <$> subWord+    , SHL <$> subWord <*> subWord+    , SHR <$> subWord <*> subWord+    , SAR <$> subWord <*> subWord+    , BlockHash <$> subWord+    --, liftM3 Balance arbitrary arbitrary subWord+    --, fmap CodeSize subWord+    --, fmap ExtCodeHash subWord+    , Keccak <$> subBuf+    , SLoad <$> subWord <*> subStore+    , ReadWord <$> genReadIndex <*> subBuf+    , BufLength <$> subBuf+    , do+      one <- subByte+      two <- subByte+      three <- subByte+      four <- subByte+      five <- subByte+      six <- subByte+      seven <- subByte+      eight <- subByte+      nine <- subByte+      ten <- subByte+      eleven <- subByte+      twelve <- subByte+      thirteen <- subByte+      fourteen <- subByte+      fifteen <- subByte+      sixteen <- subByte+      seventeen <- subByte+      eighteen <- subByte+      nineteen <- subByte+      twenty <- subByte+      twentyone <- subByte+      twentytwo <- subByte+      twentythree <- subByte+      twentyfour <- subByte+      twentyfive <- subByte+      twentysix <- subByte+      twentyseven <- subByte+      twentyeight <- subByte+      twentynine <- subByte+      thirty <- subByte+      thirtyone <- subByte+      thirtytwo <- subByte+      pure $ JoinBytes+        one two three four five six seven eight nine ten+        eleven twelve thirteen fourteen fifteen sixteen+        seventeen eighteen nineteen twenty twentyone+        twentytwo twentythree twentyfour twentyfive+        twentysix twentyseven twentyeight twentynine+        thirty thirtyone thirtytwo+    ])+  ]+ where+   subWord = genWord litFreq (sz `div` 5)+   subBuf = defaultBuf (sz `div` 10)+   subStore = genStorage (sz `div` 10)+   subByte = genByte (sz `div` 10)+   genReadIndex = do+    o :: (Expr EWord) <- subWord+    pure $ case o of+      Lit w -> Lit $ w `mod` into (maxBound :: Word64)+      _ -> o++genName :: String -> Gen Text+-- In order not to generate SMT reserved words, we prepend with "esc_"+genName ty = fmap (T.pack . (("esc_" <> ty <> "_") <> )) $ listOf1 (oneof . (fmap pure) $ ['a'..'z'] <> ['A'..'Z'])+++genByte :: Int -> Gen (Expr Byte)+genByte 0 = fmap LitByte arbitrary+genByte sz = oneof+  [ IndexWord <$> subWord <*> subWord+  , ReadByte <$> subWord <*> subBuf+  ]+  where+    subWord = defaultWord (sz `div` 10)+    subBuf = defaultBuf (sz `div` 10)++defaultBuf :: Int -> Gen (Expr Buf)+defaultBuf = genBuf (4_000_000)++genBuf :: W256 -> Int -> Gen (Expr Buf)+genBuf _ 0 = oneof+  [ fmap AbstractBuf (genName "buf")+  , fmap ConcreteBuf arbitrary+  ]+genBuf bound sz = oneof+  [ WriteWord <$> (maybeBoundedLit bound) <*> subWord <*> subBuf+  , WriteByte <$> (maybeBoundedLit bound) <*> subByte <*> subBuf+  -- we don't generate copyslice instances where:+  --   - size is abstract+  --   - size > 100 (due to unrolling in SMT.hs)+  --   - literal dstOffsets are > 4,000,000 (due to unrolling in SMT.hs)+  -- n.b. that 4,000,000 is the theoretical maximum memory size given a 30,000,000 block gas limit+  , CopySlice <$> genReadIndex <*> (maybeBoundedLit bound) <*> smolLitWord <*> subBuf <*> subBuf+  ]+  where+    -- copySlice gets unrolled in the generated SMT so we can't go too crazy here+    smolLitWord = do+      w <- arbitrary+      pure $ Lit (w `mod` 100)+    subWord = defaultWord (sz `div` 5)+    subByte = genByte (sz `div` 10)+    subBuf = genBuf bound (sz `div` 10)+    genReadIndex = do+      o :: (Expr EWord) <- subWord+      pure $ case o of+        Lit w -> Lit $ w `mod` into (maxBound :: Word64)+        _ -> o++maybeBoundedLit :: W256 -> Gen (Expr EWord)+maybeBoundedLit bound = do+    o <- (arbitrary :: Gen (Expr EWord))+    pure $ case o of+            Lit w -> Lit $ w `mod` bound+            _ -> o++joinBytesFromList :: [Expr Byte] -> Expr EWord+joinBytesFromList [a0, a1, a2, a3, a4, a5, a6, a7,+                   a8, a9, a10, a11, a12, a13, a14, a15,+                   a16, a17, a18, a19, a20, a21, a22, a23,+                   a24, a25, a26, a27, a28, a29, a30, a31] =+  JoinBytes a0 a1 a2 a3 a4 a5 a6 a7+            a8 a9 a10 a11 a12 a13 a14 a15+            a16 a17 a18 a19 a20 a21 a22 a23+            a24 a25 a26 a27 a28 a29 a30 a31+joinBytesFromList _ = internalError "List must contain exactly 32 elements"++instance Arbitrary W256 where+  arbitrary = fmap W256 arbitrary++instance Arbitrary Word128 where+  arbitrary = fromHiAndLo <$> arbitrary <*> arbitrary++instance Arbitrary Word160 where+  arbitrary = fromHiAndLo <$> arbitrary <*> arbitrary++instance Arbitrary Word256 where+  arbitrary = fromHiAndLo <$> arbitrary <*> arbitrary++instance Arbitrary W64 where+  arbitrary = fmap W64 arbitrary++instance Arbitrary Addr where+  arbitrary = fmap Addr arbitrary++instance Arbitrary (Expr EAddr) where+  arbitrary = oneof+    [ fmap LitAddr arbitrary+    , fmap SymAddr (genName "addr")+    ]++instance Arbitrary (Expr Storage) where+  arbitrary = sized genStorage++instance Arbitrary (Expr EWord) where+  arbitrary = sized defaultWord++instance Arbitrary (Expr Byte) where+  arbitrary = sized genByte++newtype SymbolicJoinBytes = SymbolicJoinBytes [Expr Byte]+  deriving (Eq, Show)++instance Arbitrary SymbolicJoinBytes where+  arbitrary = SymbolicJoinBytes <$> replicateM 32 arbitrary++instance Arbitrary (Expr Buf) where+  arbitrary = sized defaultBuf++instance Arbitrary (Expr EContract) where+  arbitrary = sized genEContract++genEContract :: Int -> Gen (Expr EContract)+genEContract sz = do+  c <- arbitrary+  b <- defaultWord sz+  n <- arbitrary+  s <- genStorage sz+  ts <- genStorage sz+  pure $ C {code=c, storage=s, tStorage=ts, balance=b, nonce=n}++instance Arbitrary (Expr End) where+  arbitrary = sized genEnd++instance Arbitrary (ContractCode) where+  arbitrary = oneof+    [ fmap UnknownCode arbitrary+    , InitCode <$> arbitrary <*> arbitrary+    , fmap RuntimeCode arbitrary+    ]++instance Arbitrary (RuntimeCode) where+  arbitrary = oneof+    [ fmap ConcreteRuntimeCode arbitrary+    , fmap SymbolicRuntimeCode arbitrary+    ]++instance Arbitrary (V.Vector (Expr Byte)) where+  arbitrary = fmap V.fromList (listOf1 arbitrary)++-- ZeroDepthWord+newtype ZeroDepthWord = ZeroDepthWord (Expr EWord)+  deriving (Show, Eq)++instance Arbitrary ZeroDepthWord where+  arbitrary = do+    fmap ZeroDepthWord . sized $ genWord 0+++-- GenWriteStorageExpr+newtype GenWriteStorageExpr = GenWriteStorageExpr (Expr EWord, Expr Storage)+  deriving (Show, Eq)++instance Arbitrary GenWriteStorageExpr where+  arbitrary = do+    slot <- arbitrary+    let mkStore = oneof+          [ pure $ ConcreteStore mempty+          , fmap ConcreteStore arbitrary+          , do+              -- generate some write chains where we know that at least one+              -- write matches either the input addr, or both the input+              -- addr and slot+              let addWrites :: Expr Storage -> Int -> Gen (Expr Storage)+                  addWrites b 0 = pure b+                  addWrites b n = SStore <$> arbitrary <*> arbitrary <*> (addWrites b (n - 1))+              s <- arbitrary+              addMatch <- fmap (SStore slot) arbitrary+              let withMatch = addMatch s+              newWrites <- oneof [ pure 0, pure 1, fmap (`mod` 5) arbitrary ]+              addWrites withMatch newWrites+          , arbitrary+          ]+    store <- mkStore+    pure $ GenWriteStorageExpr (slot, store)++-- WriteWordBuf+newtype WriteWordBuf = WriteWordBuf (Expr Buf)+  deriving (Show, Eq)++instance Arbitrary WriteWordBuf where+  arbitrary = do+    let mkBuf = oneof+          [ pure $ ConcreteBuf ""       -- empty+          , fmap ConcreteBuf arbitrary  -- concrete+          , sized (genBuf 100)          -- overlapping writes+          , arbitrary                   -- sparse writes+          ]+    fmap WriteWordBuf mkBuf++-- GenCopySliceBuf+newtype GenCopySliceBuf = GenCopySliceBuf (Expr Buf)+  deriving (Show, Eq)++instance Arbitrary GenCopySliceBuf where+  arbitrary = do+    let mkBuf = oneof+          [ pure $ ConcreteBuf ""+          , fmap ConcreteBuf arbitrary+          , arbitrary+          ]+    fmap GenCopySliceBuf mkBuf++-- GenWriteByteIdx+newtype GenWriteByteIdx = GenWriteByteIdx (Expr EWord)+  deriving (Show, Eq)++instance Arbitrary GenWriteByteIdx where+  arbitrary = do+    -- 1st: can never overflow an Int+    -- 2nd: can overflow an Int+    let mkIdx = frequency [ (10, genLit 1_000_000) , (1, fmap Lit arbitrary) ]+    fmap GenWriteByteIdx mkIdx++newtype LitOnly a = LitOnly a+  deriving (Show, Eq)++newtype LitWord (sz :: Nat) = LitWord (Expr EWord)+  deriving (Show)++instance (KnownNat sz) => Arbitrary (LitWord sz) where+  arbitrary = LitWord <$> genLit (fromInteger v)+    where+      v = natVal (Proxy @sz)++instance Arbitrary (LitOnly (Expr Byte)) where+  arbitrary = LitOnly . LitByte <$> arbitrary++instance Arbitrary (LitOnly (Expr EWord)) where+  arbitrary = LitOnly . Lit <$> arbitrary++instance Arbitrary (LitOnly (Expr Buf)) where+  arbitrary = LitOnly . ConcreteBuf <$> arbitrary++newtype LitProp = LitProp Prop+  deriving (Show, Eq)++instance Arbitrary LitProp where+  arbitrary = LitProp <$> sized (genProp True)++instance Arbitrary Prop where+  arbitrary = sized (genProp False)++genProps :: Bool -> Int -> Gen [Prop]+genProps onlyLits sz2 = listOf $ genProp onlyLits sz2++genProp :: Bool -> Int -> Gen (Prop)+genProp _ 0 = PBool <$> arbitrary+genProp onlyLits sz = oneof+  [ PEq <$> subWord <*> subWord+  , PLT <$> subWord <*> subWord+  , PGT <$> subWord <*> subWord+  , PLEq <$> subWord <*> subWord+  , PGEq <$> subWord <*> subWord+  , PNeg <$> subProp+  , PAnd <$> subProp <*> subProp+  , POr <$> subProp <*> subProp+  , PImpl <$> subProp <*> subProp+  ]+  where+    subWord = if onlyLits then frequency [(2, Lit <$> arbitrary)+                                         ,(1, pure $ Lit 0)+                                         ,(1, pure $ Lit Expr.maxLit)+                                         ]+                          else genWord 1 (sz `div` 2)+    subProp = genProp onlyLits (sz `div` 2)+++newtype StorageExp = StorageExp (Expr EWord)+  deriving (Show, Eq)++instance Arbitrary StorageExp where+  arbitrary = StorageExp <$> (genStorageExp)+++genStorageExp :: Gen (Expr EWord)+genStorageExp = do+  fromPos <- genSlot+  storage <- genStorageWrites+  pure $ SLoad fromPos storage++genSlot :: Gen (Expr EWord)+genSlot = frequency [ (1, do+                        buf <- genConcreteBufSlot 64+                        case buf of+                          (ConcreteBuf b) -> do+                            key <- genLit 10+                            pure $ Expr.MappingSlot b key+                          _ -> internalError "impossible"+                        )+                     -- map element+                     ,(2, do+                        l <- genLit 10+                        buf <- genConcreteBufSlot 64+                        pure $ Add (Keccak buf) l)+                    -- Array element+                     ,(2, do+                        l <- genLit 10+                        buf <- genConcreteBufSlot 32+                        pure $ Add (Keccak buf) l)+                     -- member of the Contract+                     ,(2, pure $ Lit 20)+                     -- array element+                     ,(2, do+                        arrayNum :: Int <- arbitrary+                        offs :: W256 <- arbitrary+                        pure $ Lit $ fst (Expr.preImages !! (arrayNum `mod` 3)) + (offs `mod` 3))+                     -- random stuff+                     ,(1, pure $ Lit (maxBound :: W256))+                     ]++-- Generates an N-long buffer, all with the same value, at most 8 different ones+genConcreteBufSlot :: Int -> Gen (Expr Buf)+genConcreteBufSlot len = do+  b :: Word8 <- arbitrary+  pure $ ConcreteBuf $ BS.pack ([ 0 | _ <- [0..(len-2)]] ++ [b])++genStorageWrites :: Gen (Expr Storage)+genStorageWrites = do+  toSlot <- genSlot+  val <- genLit (maxBound :: W256)+  store <- frequency [ (3, pure $ AbstractStore (SymAddr "") Nothing)+                     , (2, genStorageWrites)+                     ]+  pure $ SStore toSlot val store++genWordArith :: Int -> Int -> Gen (Expr EWord)+genWordArith litFreq 0 = frequency+  [ (litFreq, fmap Lit arbitrary)+  , (1, oneof [ fmap Lit arbitrary ])+  ]+genWordArith litFreq sz = frequency+  [ (litFreq, fmap Lit arbitrary)+  , (20, frequency+    [ (20, Add <$> subWord <*> subWord)+    , (20, Sub <$> subWord <*> subWord)+    , (20, Mul <$> subWord <*> subWord)+    , (20, SEx <$> subWord <*> subWord)+    , (20, Xor <$> subWord <*> subWord)+    -- these reduce variability+    , (3 , Min  <$> subWord <*> subWord)+    , (3 , Div  <$> subWord <*> subWord)+    , (3 , SDiv <$> subWord <*> subWord)+    , (3 , Mod  <$> subWord <*> subWord)+    , (3 , SMod <$> subWord <*> subWord)+    , (3 , SHL  <$> subWord <*> subWord)+    , (3 , SHR  <$> subWord <*> subWord)+    , (3 , SAR  <$> subWord <*> subWord)+    , (3 , Or   <$> subWord <*> subWord)+    -- comparisons, reducing variability greatly+    , (1 , LEq  <$> subWord <*> subWord)+    , (1 , GEq  <$> subWord <*> subWord)+    , (1 , SLT  <$> subWord <*> subWord)+    , (1 , SGT  <$> subWord <*> subWord)+    , (1 , Eq   <$> subWord <*> subWord)+    , (1 , And  <$> subWord <*> subWord)+    , (1 , IsZero <$> subWord)+    -- Expensive below+    --(1,  liftM3 AddMod subWord subWord subWord+    --(1,  liftM3 MulMod subWord subWord subWord+    --(1,  liftM2 Exp subWord litWord+    ])+  ]+ where+   subWord = genWordArith (litFreq `div` 2) (sz `div` 2)++genEnd :: Int -> Gen (Expr End)+genEnd 0 = oneof+  [ fmap (Failure mempty mempty . UnrecognizedOpcode) arbitrary+  , pure $ Failure mempty mempty IllegalOverflow+  , pure $ Failure mempty mempty SelfDestruction+  ]+genEnd sz = oneof+  [ Failure <$> subProp <*> (pure mempty) <*> (fmap Revert subBuf)+  , Success <$> subProp <*> (pure mempty) <*> subBuf <*> arbitrary+  -- TODO Partial+  ]+  where+    subBuf = defaultBuf (sz `div` 2)+    subProp = genProps False (sz `div` 2)
+ test/EVM/SymExec/SymExecTests.hs view
@@ -0,0 +1,1401 @@+{-# LANGUAGE QuasiQuotes #-}++module EVM.SymExec.SymExecTests (tests) where++import Test.Tasty+import Test.Tasty.HUnit+import Test.Tasty.ExpectedFailure++import Control.Monad.IO.Unlift (MonadUnliftIO)+import Control.Monad.Reader (ReaderT)+import Data.ByteString (ByteString)+import Data.List qualified as List (isInfixOf)+import Data.Maybe (isJust, mapMaybe)+import Data.Monoid (Any(..))+import Data.String.Here++import EVM.ABI+import EVM.Effects qualified as Effects+import EVM.Expr qualified as Expr+import EVM.Types+import EVM.SMT qualified as SMT (getVar)+import EVM.Solidity (solcRuntime)+import EVM.Solvers (withSolvers, defMemLimit, Solver(..), SolverGroup)+import EVM.SymExec+import EVM.Traversals+++tests :: TestTree+tests = testGroup "Symbolic execution"+  [solidityExplorationTests]++solidityExplorationTests :: TestTree+solidityExplorationTests = testGroup "Exploration of Solidity"+    [ basicTests+    , maxDepthTests+    , copySliceTests+    , storageTests+    , panicCodeTests+    , cheatCodeTests+    , arithmeticTests+    ]++basicTests :: TestTree+basicTests = testGroup "simple-checks"+  [ testCase "simple-stores" $ do+    Just c <- solcRuntime "MyContract"+      [i|+      contract MyContract {+        mapping(uint => uint) items;+        function func() public {+          assert(items[5] == 0);+        }+      }+      |]+    let sig = (Sig "func()" [])+    (_, [Cex (_, _ctr)]) <- executeWithBitwuzla $ \s -> checkAssert s defaultPanicCodes c (Just sig) [] defaultVeriOpts+    assertBool "" True+  , testCase "simple-fixed-value" $ do+    Just c <- solcRuntime "MyContract"+      [i|+      contract MyContract {+        mapping(uint => uint) items;+        function func(uint a) public {+          assert(a != 1337);+        }+      }+      |]+    let sig = (Sig "func(uint256)" [AbiUIntType 256])+    (_, [Cex (_, ctr)]) <- executeWithBitwuzla $ \s -> checkAssert s defaultPanicCodes c (Just sig) [] defaultVeriOpts+    assertEqual "Expected input not found" (1337 :: W256) (SMT.getVar ctr "arg1")+  , testCase "simple-fixed-value2" $ do+    Just c <- solcRuntime "MyContract"+      [i|+      contract MyContract {+        function func(uint a, uint b) public {+          assert(!((a == 1337) && (b == 99)));+        }+      }+      |]+    let sig = (Sig "func(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])+    (_, [Cex (_, ctr)]) <- executeWithBitwuzla $ \s -> checkAssert s defaultPanicCodes c (Just sig) [] defaultVeriOpts+    let a = SMT.getVar ctr "arg1"+    let b = SMT.getVar ctr "arg2"+    assertBool "Expected input not found" (a == 1337 && b == 99)+  , testCase "simple-fixed-value3" $ do+    Just c <- solcRuntime "MyContract"+      [i|+      contract MyContract {+        function func(uint a, uint b) public {+          assert(((a != 1337) && (b != 99)));+        }+      }+      |]+    let sig = (Sig "func(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])+    (_, cexs) <- executeWithBitwuzla $ \s -> checkAssert s defaultPanicCodes c (Just sig) [] defaultVeriOpts+    assertBool ("Expected at least 1 counterexample, got " ++ show (length cexs)) (not $ null cexs)+  , testCase "simple-fixed-value-store1" $ do+    Just c <- solcRuntime "MyContract"+      [i|+      contract MyContract {+        mapping(uint => uint) items;+        function func(uint a) public {+          uint f = items[2];+          assert(a != f);+        }+      }+      |]+    let sig = (Sig "func(uint256)" [AbiUIntType 256, AbiUIntType 256])+    (_, [Cex _]) <- executeWithBitwuzla $ \s -> checkAssert s defaultPanicCodes c (Just sig) [] defaultVeriOpts+    assertBool "" True+  , testCase "simple-fixed-value-store2" $ do+    Just c <- solcRuntime "MyContract"+      [i|+      contract MyContract {+        mapping(uint => uint) items;+        function func(uint a) public {+          items[0] = 1337;+          assert(a != items[0]);+        }+      }+      |]+    let sig = (Sig "func(uint256)" [AbiUIntType 256, AbiUIntType 256])+    (_, [Cex (_, _ctr)]) <- executeWithBitwuzla $ \s -> checkAssert s defaultPanicCodes c (Just sig) [] defaultVeriOpts+    assertBool "" True+  , testCase "symbolic-exp-0-to-n" $ do+    Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          function fun(uint256 a, uint256 b, uint256 k) external pure {+            uint x = 0 ** b;+            assert (x == 1);+          }+          }+        |]+    let sig = Just (Sig "fun(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])+    (_, [Cex (_, ctr)]) <- executeWithBitwuzla $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+    let b = SMT.getVar ctr "arg2"+    assertBool "b must be non-0" (b /= 0)+  , testCase "symbolic-exp-0-to-n2" $ do+    Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          function fun(uint256 a, uint256 b, uint256 k) external pure {+            uint x = 0 ** b;+            assert (x == 0);+          }+          }+        |]+    let sig = Just (Sig "fun(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])+    (_, [Cex (_, ctr)]) <- executeWithBitwuzla $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+    let b = SMT.getVar ctr "arg2"+    assertBool "b must be 0" (b == 0)+  ]++copySliceTests :: TestTree+copySliceTests = testGroup "Copyslice tests"+  [ testCase "symbolic-mcopy" $ do+      Just c <- solcRuntime "MyContract"+          [i|+          contract MyContract {+            function fun(uint256 a, uint256 s) external returns (uint) {+              require(a < 5);+              assembly {+                  mcopy(0x2, 0, s)+                  a:=mload(s)+              }+              assert(a < 5);+              return a;+            }+            }+          |]+      let sig = Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])+      (_, k) <- executeWithBitwuzla $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      let numErrs = sum $ map (fromEnum . isError) k+      assertEqual "number of errors (i.e. copySlice issues) is 1" 1 numErrs+      let errStrings = mapMaybe getResError k+      assertEqual "All errors are from copyslice" True $ all ("CopySlice" `List.isInfixOf`) errStrings+  , testCase "symbolic-copyslice" $ do+      Just c <- solcRuntime "MyContract"+          [i|+          contract MyContract {+            function fun(uint256 a, uint256 s) external returns (uint) {+              require(a < 10);+              if (a >= 8) {+                assembly {+                    calldatacopy(0x5, s, s)+                    a:=mload(s)+                }+              } else {+                assembly {+                    calldatacopy(0x2, 0x2, 5)+                    a:=mload(s)+                }+              }+              assert(a < 9);+              return a;+            }+            }+          |]+      let sig = Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])+      (_, k) <- executeWithBitwuzla $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      let numErrs = sum $ map (fromEnum . isError) k+      assertEqual "number of errors (i.e. copySlice issues) is 1" 1 numErrs+      let errStrings = mapMaybe getResError k+      assertEqual "All errors are from copyslice" True $ all ("CopySlice" `List.isInfixOf`) errStrings++  ]++storageTests :: TestTree+storageTests = testGroup "Storage handling" [storageDecompositionTests, storageSimplificationTests]++storageDecompositionTests :: TestTree+storageDecompositionTests = testGroup "Storage decomposition"+    [ testCase "decompose-1" $ do+      Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          mapping (address => uint) balances;+          function prove_mapping_access(address x, address y) public {+              require(x != y);+              balances[x] = 1;+              balances[y] = 2;+              assert(balances[x] != balances[y]);+          }+        }+        |]+      paths <- executeWithBitwuzla $ \s -> getExpr s c (Just (Sig "prove_mapping_access(address,address)" [AbiAddressType, AbiAddressType])) [] defaultVeriOpts+      let simpExpr = map (mapExprM Expr.decomposeStorage) paths+      assertBool "Decompose did not succeed." (all isJust simpExpr)+    , testCase "decompose-2" $ do+      Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          mapping (address => uint) balances;+          function prove_mixed_symoblic_concrete_writes(address x, uint v) public {+              balances[x] = v;+              balances[address(0)] = balances[x];+              assert(balances[address(0)] == v);+          }+        }+        |]+      paths <- executeWithBitwuzla $ \s -> getExpr s c (Just (Sig "prove_mixed_symoblic_concrete_writes(address,uint256)" [AbiAddressType, AbiUIntType 256])) [] defaultVeriOpts+      let pathsSimp = map (mapExprM (Expr.decomposeStorage . Expr.concKeccakSimpExpr . Expr.simplify)) paths+      assertBool "Decompose did not succeed." (all isJust pathsSimp)+    , testCase "decompose-3" $ do+      Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          uint[] a;+          function prove_array(uint x, uint v1, uint y, uint v2) public {+              require(v1 != v2);+              a[x] = v1;+              a[y] = v2;+              assert(a[x] == a[y]);+          }+        }+        |]+      paths <- executeWithBitwuzla $ \s -> getExpr s c (Just (Sig "prove_array(uint256,uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+      let simpExpr = map (mapExprM Expr.decomposeStorage) paths+      assertBool "Decompose did not succeed." (all isJust simpExpr)+    , testCase "decompose-4-mixed" $ do+      Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          uint[] a;+          mapping( uint => uint) balances;+          function prove_array(uint x, uint v1, uint y, uint v2) public {+              require(v1 != v2);+              balances[x] = v1+1;+              balances[y] = v1+2;+              a[x] = v1;+              assert(balances[x] != balances[y]);+          }+        }+        |]+      paths <- executeWithBitwuzla $ \s -> getExpr s c (Just (Sig "prove_array(uint256,uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+      let simpExpr = map (mapExprM Expr.decomposeStorage) paths+      -- putStrLnM $ T.unpack $ formatExpr (fromJust simpExpr)+      assertBool "Decompose did not succeed." (all isJust simpExpr)+    , testCase "decompose-5-mixed" $ do+      Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          mapping (address => uint) balances;+          mapping (uint => bool) auth;+          uint[] arr;+          uint a;+          uint b;+          function prove_mixed(address x, address y, uint val) public {+            b = val+1;+            require(x != y);+            balances[x] = val;+            a = val;+            arr[val] = 5;+            auth[val+1] = true;+            balances[y] = val+2;+            if (balances[y] == balances[y]) {+                assert(balances[y] == val);+            }+          }+        }+        |]+      paths <- executeWithBitwuzla $ \s -> getExpr s c (Just (Sig "prove_mixed(address,address,uint256)" [AbiAddressType, AbiAddressType, AbiUIntType 256])) [] defaultVeriOpts+      let simpExpr = map (mapExprM Expr.decomposeStorage) paths+      assertBool "Decompose did not succeed." (all isJust simpExpr)+    , testCase "decompose-6" $ do+      Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          uint[] arr;+          function prove_mixed(uint val) public {+            arr[val] = 5;+            arr[val+1] = val+5;+            assert(arr[val] == arr[val+1]);+          }+        }+        |]+      paths <- executeWithBitwuzla $ \s -> getExpr s c (Just (Sig "prove_mixed(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+      let simpExpr = map (mapExprM Expr.decomposeStorage) paths+      assertBool "Decompose did not succeed." (all isJust simpExpr)+    -- This test uses array.length, which is is concrete 0 only in case we start with an empty storage+    -- otherwise (i.e. with getExpr) it's symbolic, and the exploration loops forever+    , testCase "decompose-7-empty-storage" $ do+       Just c <- solcRuntime "MyContract" [i|+        contract MyContract {+          uint[] arr;+          function nested_append(uint v, uint w) public {+            arr.push(w);+            arr.push();+            arr.push();+            arr.push(arr[0]-1);++            arr[2] = v;+            arr[1] = arr[0]-arr[2];++            assert(arr.length == 4);+            assert(arr[0] == w);+            assert(arr[1] == w-v);+            assert(arr[2] == v);+            assert(arr[3] == w-1);+          }+       } |]+       let sig = Just $ Sig "nested_append(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256]+       paths <- executeWithBitwuzla $ \s -> getExprEmptyStore s c sig [] defaultVeriOpts+       assertEqual "Expression must be clean." (badStoresInExpr paths) False+    ]++storageSimplificationTests :: TestTree+storageSimplificationTests = testGroup "Storage simplification"+    [ testCase "simplify-storage-array-only-static" $ do+       Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          uint[] a;+          function transfer(uint acct, uint val1, uint val2) public {+            unchecked {+              a[0] = val1 + 1;+              a[1] = val2 + 2;+              assert(a[0]+a[1] == val1 + val2 + 3);+            }+          }+        }+        |]+       paths <- executeWithBitwuzla $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+       assertEqual "Expression is not clean." (badStoresInExpr paths) False+    , testCase "simplify-storage-map-only-static" $ do+       Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          mapping(uint => uint) items1;+          function transfer(uint acct, uint val1, uint val2) public {+            unchecked {+              items1[0] = val1+1;+              items1[1] = val2+2;+              assert(items1[0]+items1[1] == val1 + val2 + 3);+            }+          }+        }+        |]+       let sig = (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256]))+       paths <- executeWithBitwuzla $ \s -> getExpr s c sig [] defaultVeriOpts+       let pathsSimp = map (mapExpr (Expr.concKeccakSimpExpr . Expr.simplify)) paths+       assertEqual "Expression is not clean." (badStoresInExpr pathsSimp) False+    , testCase "simplify-storage-map-only-2" $ do+       Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          mapping(uint => uint) items1;+          function transfer(uint acct, uint val1, uint val2) public {+            unchecked {+              items1[acct] = val1+1;+              items1[acct+1] = val2+2;+              assert(items1[acct]+items1[acct+1] == val1 + val2 + 3);+            }+          }+        }+        |]+       paths <- executeWithBitwuzla $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+       assertEqual "Expression is not clean." (badStoresInExpr paths) False+    , testCase "simplify-storage-map-with-struct" $ do+       Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          struct MyStruct {+            uint a;+            uint b;+          }+          mapping(uint => MyStruct) items1;+          function transfer(uint acct, uint val1, uint val2) public {+            unchecked {+              items1[acct].a = val1+1;+              items1[acct].b = val2+2;+              assert(items1[acct].a+items1[acct].b == val1 + val2 + 3);+            }+          }+        }+        |]+       paths <- executeWithBitwuzla $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+       assertEqual "Expression is not clean." (badStoresInExpr paths) False+    , testCase "simplify-storage-map-and-array" $ do+       Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          uint[] a;+          mapping(uint => uint) items1;+          mapping(uint => uint) items2;+          function transfer(uint acct, uint val1, uint val2) public {+            uint beforeVal1 = items1[acct];+            uint beforeVal2 = items2[acct];+            unchecked {+              items1[acct] = val1+1;+              items2[acct] = val2+2;+              a[0] = val1 + val2 + 1;+              a[1] = val1 + val2 + 2;+              assert(items1[acct]+items2[acct]+a[0]+a[1] > beforeVal1 + beforeVal2);+            }+          }+        }+       |]+       paths <- executeWithBitwuzla $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+       assertEqual "Expression is not clean." (badStoresInExpr paths) False+    , testCase "simplify-storage-array-loop-struct" $ do+       Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          struct MyStruct {+            uint a;+            uint b;+          }+          MyStruct[] arr;+          function transfer(uint v1, uint v2) public {+            for (uint i = 0; i < arr.length; i++) {+              arr[i].a = v1+1;+              arr[i].b = v2+2;+              assert(arr[i].a + arr[i].b == v1 + v2 + 3);+            }+          }+        }+        |]+       let veriOpts = (defaultVeriOpts :: VeriOpts) { iterConf = defaultIterConf { maxIter = Just 5 }}+       paths <- executeWithBitwuzla $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] veriOpts+       assertEqual "Expression is not clean." (badStoresInExpr paths) False+    -- This case is somewhat artificial. We can't simplify this using only+    -- static rewrite rules, because `acct` is totally abstract and a[acct]+    -- could overflow the store and rewrite slot 1, where the array size is stored.+    -- The load/store simplifications would have to take other constraints into account.+    , ignoreTestBecause "We cannot simplify this with only static rewrite rules" $ testCase "simplify-storage-array-symbolic-index" $ do+       Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          uint b;+          uint[] a;+          function transfer(uint acct, uint val1) public {+            unchecked {+              a[acct] = val1;+              assert(a[acct] == val1);+            }+          }+        }+        |]+       paths <- executeWithBitwuzla $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+       -- T.writeFile "symbolic-index.expr" $ formatExpr paths+       assertEqual "Expression is not clean." (badStoresInExpr paths) False+    , ignoreTestBecause "We cannot simplify this with only static rewrite rules" $ testCase "simplify-storage-array-of-struct-symbolic-index" $ do+       Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          struct MyStruct {+            uint a;+            uint b;+          }+          MyStruct[] arr;+          function transfer(uint acct, uint val1, uint val2) public {+            unchecked {+              arr[acct].a = val1+1;+              arr[acct].b = val1+2;+              assert(arr[acct].a + arr[acct].b == val1+val2+3);+            }+          }+        }+        |]+       paths <- executeWithBitwuzla $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+       assertEqual "Expression is not clean." (badStoresInExpr paths) False+    , testCase "simplify-storage-array-loop-nonstruct" $ do+       Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          uint[] a;+          function transfer(uint v) public {+            for (uint i = 0; i < a.length; i++) {+              a[i] = v;+              assert(a[i] == v);+            }+          }+        }+        |]+       let veriOpts = (defaultVeriOpts :: VeriOpts) { iterConf = defaultIterConf { maxIter = Just 5 }}+       paths <- executeWithBitwuzla $ \s -> getExpr s c (Just (Sig "transfer(uint256)" [AbiUIntType 256])) [] veriOpts+       assertEqual "Expression is not clean." (badStoresInExpr paths) False+    , testCase "simplify-storage-map-newtest1" $ do+       Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          mapping (uint => uint) a;+          mapping (uint => uint) b;+          function fun(uint v, uint i) public {+            require(i < 1000);+            require(v < 1000);+            b[i+v] = v+1;+            a[i] = v;+            b[i+1] = v+1;+            assert(a[i] == v);+            assert(b[i+1] == v+1);+          }+        }+        |]+       paths <- executeWithBitwuzla $ \s -> getExpr s c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+       assertEqual "Expression is not clean." (badStoresInExpr paths) False+       (_, []) <- executeWithBitwuzla $ \s -> checkAssert s [0x11] c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+       assertBool "" True+    , testCase "simplify-storage-map-todo" $ do+       Just c <- solcRuntime "MyContract"+        [i|+        contract MyContract {+          mapping (uint => uint) a;+          mapping (uint => uint) b;+          function fun(uint v, uint i) public {+            require(i < 1000);+            require(v < 1000);+            a[i] = v;+            b[i+1] = v+1;+            b[i+v] = 55; // note: this can overwrite b[i+1], hence assert below can fail+            assert(a[i] == v);+            assert(b[i+1] == v+1);+          }+        }+        |]+       -- TODO: expression below contains (load idx1 (store idx1 (store idx1 (store idx0)))), and the idx0+       --       is not stripped. This is due to us not doing all we can in this case, see+       --       note above readStorage. Decompose remedies this (when it can be decomposed)+       -- paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+       -- putStrLnM $ T.unpack $ formatExpr paths+       (_, [Cex _]) <- executeWithBitwuzla $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+       assertBool "" True+    ,+    -- TODO: we can't deal with symbolic jump conditions+    expectFail $ testCase "call-zero-inited-var-thats-a-function" $ do+      Just c <- solcRuntime "MyContract"+          [i|+          contract MyContract {+            function (uint256) internal returns (uint) funvar;+            function fun2(uint256 a) internal returns (uint){+              return a;+            }+            function fun(uint256 a) external returns (uint) {+              if (a != 44) {+                funvar = fun2;+              }+              return funvar(a);+            }+            }+          |]+      (_, [Cex (_, cex)]) <- executeWithBitwuzla $ \s -> checkAssert s [0x51] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+      let a = SMT.getVar cex "arg1"+      assertEqual "unexpected cex value" 44 a+    ]+++panicCodeTests :: TestTree+panicCodeTests = testGroup "Panic code tests via symbolic execution"+  [ testCase "assert-fail" $ do+      Just c <- solcRuntime "MyContract"+          [i|+          contract MyContract {+            function fun(uint256 a) external pure {+              assert(a != 0);+            }+          }+          |]+      (_, [Cex (_, ctr)]) <- executeWithBitwuzla $ \s -> checkAssert s [0x01] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+      assertEqual "Must be 0" 0 $ SMT.getVar ctr "arg1"+  , testCase "safeAdd-fail" $ do+      Just c <- solcRuntime "MyContract"+          [i|+          contract MyContract {+            function fun(uint256 a, uint256 b) external pure returns (uint256 c) {+              c = a+b;+            }+            }+          |]+      (_, [Cex (_, ctr)]) <- executeWithBitwuzla $ \s -> checkAssert s [0x11] c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+      let x = SMT.getVar ctr "arg1"+      let y = SMT.getVar ctr "arg2"++      let maxUint = 2 ^ (256 :: Integer) :: Integer+      assertBool "Overflow must occur" (toInteger x + toInteger y >= maxUint)+  , testCase "div-by-zero-fail" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(uint256 a, uint256 b) external pure returns (uint256 c) {+               c = a/b;+              }+             }+            |]+        (_, [Cex (_, ctr)]) <- executeWithBitwuzla $ \s -> checkAssert s [0x12] c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+        assertEqual "Division by 0 needs b=0" (SMT.getVar ctr "arg2") 0+        -- putStrLnM "expected counterexample found"+  , testCase "unused-args-fail" $ do+         Just c <- solcRuntime "C"+             [i|+             contract C {+               function fun(uint256 a) public pure {+                 assert(false);+               }+             }+             |]+         (_, results) <- executeWithBitwuzla $ \s -> checkAssert s [0x1] c Nothing [] defaultVeriOpts+         expectOneCex results+  , testCase "gas-decrease-monotone" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(uint8 a) external {+                uint a = gasleft();+                uint b = gasleft();+                assert(a > b);+              }+             }+            |]+        let sig = (Just (Sig "fun(uint8)" [AbiUIntType 8]))+        (_, results) <- executeWithBitwuzla $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        expectNoCex results+  , testCase "enum-conversion-fail" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              enum MyEnum { ONE, TWO }+              function fun(uint256 a) external pure returns (MyEnum b) {+                b = MyEnum(a);+              }+             }+            |]+        (_, [Cex (_, ctr)]) <- executeWithBitwuzla $ \s -> checkAssert s [0x21] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+        assertBool "Enum is only defined for 0 and 1" $ (SMT.getVar ctr "arg1") > 1+  ,+    -- TODO 0x22 is missing: "0x22: If you access a storage byte array that is incorrectly encoded."+     -- TODO below should NOT fail+     -- TODO this has a loop that depends on a symbolic value and currently causes interpret to loop +    ignoreTest $ testCase "pop-empty-array" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              uint[] private arr;+              function fun(uint8 a) external {+                arr.push(1);+                arr.push(2);+                for (uint i = 0; i < a; i++) {+                  arr.pop();+                }+              }+             }+            |]+        a <- executeWithBitwuzla $ \s -> checkAssert s [0x31] c (Just (Sig "fun(uint8)" [AbiUIntType 8])) [] defaultVeriOpts+        print $ length a+        print $ show a+        putStrLn "expected counterexample found"+  , testCase "access-out-of-bounds-array" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              uint[] private arr;+              function fun(uint8 a) external returns (uint x){+                arr.push(1);+                arr.push(2);+                x = arr[a];+              }+             }+            |]+        (_, [Cex (_, ctr)]) <- executeWithBitwuzla $ \s -> checkAssert s [0x32] c (Just (Sig "fun(uint8)" [AbiUIntType 8])) [] defaultVeriOpts+        assertBool "Access must be beyond index 1" $ (SMT.getVar ctr "arg1") > 1+  , testCase "alloc-too-much" $ do -- Note: we catch the assertion here, even though we are only able to explore partially+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(uint256 a) external {+                uint[] memory arr = new uint[](a);+              }+             }+            |]+        (_, results) <- executeWithBitwuzla $ \s -> checkAssert s [0x41] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+        expectOneCex results+  ]++cheatCodeTests :: TestTree+cheatCodeTests = testGroup "Cheatcode tests via symbolic execution"+  [ testCase "vm.deal unknown address" $ do+      Just c <- solcRuntime "C"+        [i|+          interface Vm {+            function deal(address,uint256) external;+          }+          contract C {+            // this is not supported yet due to restrictions around symbolic address aliasing...+            function f(address e, uint val) external {+                Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);+                vm.deal(e, val);+                assert(e.balance == val);+            }+          }+        |]+      result <- verifyUserAsserts c (Just $ Sig "f(address,uint256)" [AbiAddressType, AbiUIntType 256])+      expectPartial result -- FIXME: Ideally, we would be able to explore fully and prove the assertion+  , testCase "vm.prank-create" $ do+      Just c <- solcRuntime "C"+          [i|+            interface Vm {+              function prank(address) external;+            }+            contract Owned {+              address public owner;+              constructor() {+                owner = msg.sender;+              }+            }+            contract C {+              function f() external {+                Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);++                Owned target = new Owned();+                assert(target.owner() == address(this));++                address usr = address(1312);+                vm.prank(usr);+                target = new Owned();+                assert(target.owner() == usr);+                target = new Owned();+                assert(target.owner() == address(this));+              }+            }+          |]+      result <- verifyUserAsserts c (Just $ Sig "f()" [])+      expectNoCexNoPartial result+  , testCase "vm.prank-create2" $ do+      Just c <- solcRuntime "C"+          [i|+            interface Vm {+              function prank(address) external;+            }+            contract Owned {+              address public owner;+              constructor() {+                owner = msg.sender;+              }+            }+            contract C {+              function f() external {+                Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);++                bytes32 salt = bytes32(uint256(1));+                address usr = address(1312);+                vm.prank(usr);+                Owned target = new Owned{salt: salt}();+                address expected = address(uint160(uint256(keccak256(abi.encodePacked(+                  bytes1(0xff),+                  usr,+                  salt,+                  keccak256(type(Owned).creationCode)+                )))));++                assert(target.owner() == usr);+                assert(address(target) == expected);+              }+            }+          |]+      result <- verifyUserAsserts c (Just $ Sig "f()" [])+      expectNoCexNoPartial result+  , testCase "vm.prank underflow" $ do+      Just c <- solcRuntime "C"+          [i|+            interface Vm {+              function prank(address) external;+            }+            contract Payable {+                function hi() public payable {}+            }+            contract C {+              function f() external {+                Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);++                uint amt = 10;+                address from = address(0xacab);+                require(from.balance < amt);++                Payable target = new Payable();+                vm.prank(from);+                target.hi{value : amt}();+              }+            }+          |]+      expectAllBranchesFail c Nothing+  , testCase "cheatcode-nonexistent" $ do+      Just c <- solcRuntime "C"+          [i|+            interface Vm {+              function nonexistent_cheatcode(uint) external;+            }+          contract C {+            function fun(uint a) public {+                // Cheatcode address+                Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);+                vm.nonexistent_cheatcode(a);+                assert(1 == 1);+            }+          }+          |]+      let sig = Just (Sig "fun(uint256)" [AbiUIntType 256])+      result <- executeWithBitwuzla $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      expectPartial result+  , testCase "cheatcode-with-selector" $ do+      Just c <- solcRuntime "C"+          [i|+          contract C {+          function prove_warp_symbolic(uint128 jump) public {+                  uint pre = block.timestamp;+                  address hevm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;+                  (bool success, ) = hevm.call(abi.encodeWithSelector(bytes4(keccak256("warp(uint256)")), block.timestamp+jump));+                  require(success, "Call to hevm.warp failed");+                  assert(block.timestamp == pre + jump);+              }+          }+          |]+      result <- verifyUserAsserts c Nothing+      expectNoCexNoPartial result+  , testCase "call ffi when disabled" $ do+      Just c <- solcRuntime "C"+          [i|+            interface Vm {+              function ffi(string[] calldata) external;+            }+            contract C {+              function f() external {+                Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);++                string[] memory inputs = new string[](2);+                inputs[0] = "echo";+                inputs[1] = "acab";++                // should fail to explore this branch+                vm.ffi(inputs);+              }+            }+          |]+      expectAllBranchesFail c Nothing+  ]++maxDepthTests :: TestTree+maxDepthTests = testGroup "Tests for branching depth"+  [+      -- below we hit the limit of the depth of the symbolic execution tree+      testCase "limit-num-explore-hit-limit" $ do+        let conf = symExecTestsConfig {Effects.maxDepth = Just 3}+        Just c <- solcRuntime "C"+          [i|+          contract C {+              function checkval(uint256 a, uint256 b, uint256 c) public {+                if (a == b) {+                  if (b == c) {+                    assert(false);+                  }+                }+              }+          }+          |]+        let sig = Just (Sig "checkval(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])+        res@(_, ret) <- Effects.runEnv (Effects.Env conf) $ withBitwuzla $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        expectPartial res+        expectNoCex ret+        expectNoError ret+      -- below we don't hit the limit of the depth of the symbolic execution tree+      , testCase "limit-num-explore-no-hit-limit" $ do+        let conf = symExecTestsConfig {Effects.maxDepth = Just 7}+        Just c <- solcRuntime "C"+          [i|+          contract C {+              function checkval(uint256 a, uint256 b, uint256 c) public {+                if (a == b) {+                  if (b == c) {+                    assert(false);+                  }+                }+              }+          }+          |]+        let sig = Just (Sig "checkval(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])+        (paths, ret) <- Effects.runEnv (Effects.Env conf) $ withBitwuzla $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        expectNoError ret+        expectOneCex ret+        assertBool "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)+  ]++arithmeticTests :: TestTree+arithmeticTests = testGroup "Arithmetic tests"+  [ testCase "math-avg" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_Avg(uint a, uint b) external pure {+            require(a + b >= a);+            unchecked {+              uint r1 = (a & b) + (a ^ b) / 2;+              uint r2 = (a + b) / 2;+              assert(r1 == r2);+            }+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , testCase "unsigned-div-by-zero" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_unsigned_div_by_zero(uint256 a) external pure {+            uint256 result;+            assembly { result := div(a, 0) }+            assert(result == 0);+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , testCase "arith-div-pass" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_Div_pass(uint x, uint y) external pure {+            require(x > y);+            require(y > 0);+            uint q;+            assembly { q := div(x, y) }+            assert(q != 0);+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , testCase "arith-div-fail" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_Div_fail(uint x, uint y) external pure {+            require(x > y);+            uint q;+            assembly { q := div(x, y) }+            assert(q != 0);+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectOneCex res+  , testCase "arith-mod-fail" $ do+        Just c <- solcRuntime "C" [i|+          contract C {+            function prove_Div_fail(uint x, uint y) external pure {+              require(x > y);+              uint q;+              assembly { q := mod(x, y) }+              assert(q != 0);+            }+          } |]+        (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+        expectOneCex res+  ,testCase "arith-mod" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function unchecked_smod(int x, int y) internal pure returns (int ret) {+            assembly { ret := smod(x, y) }+          }+          function prove_Mod(int x, int y) external pure {+            unchecked {+              assert(unchecked_smod(x, 0) == 0);+              assert(x % 1 == 0);+              assert(x % 2 < 2 && x % 2 > -2);+              assert(x % 4 < 4 && x % 4 > -4);+              int x_smod_y = unchecked_smod(x, y);+              assert(x_smod_y <= y || y < 0);}+          }+        } |]+      (_, res) <- executeWithBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , ignoreTestBecause "Currently takes too long" $ testCase "math-mint-fail" $ do+        Just c <- solcRuntime "C" [i|+          contract C {+            function prove_mint(uint s, uint A1, uint S1) external pure {+              uint a = (s * A1) / S1;+              uint A2 = A1 + a;+              uint S2 = S1 + s;+              assert(A1 * S2 <= A2 * S1);+            }+          } |]+        (_, res) <- executeWithBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+        expectOneCex res+  , signedDivModTests+  , abdkMathTests+  ]++signedDivModTests :: TestTree+signedDivModTests = testGroup "Tests for signed division and modulo"+  [ testCase "sdiv-by-one" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_sdiv_by_one(int256 a) external pure {+            int256 result;+            assembly { result := sdiv(a, 1) }+            assert(result == a);+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , testCase "sdiv-by-neg-one" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_sdiv_by_neg_one(int256 a) external pure {+            int256 result;+            assembly { result := sdiv(a, sub(0, 1)) }+            if (a == -170141183460469231731687303715884105728 * 2**128) { // type(int256).min+                assert(result == a);+            } else {+                assert(result == -a);+            }+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , testCase "sdiv-intmin-by-two" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_sdiv_intmin_by_two() external pure {+            int256 result;+            assembly {+              let intmin := 0x8000000000000000000000000000000000000000000000000000000000000000+              result := sdiv(intmin, 2)+            }+            // -2**254 is 0xc000...0000+            assert(result == -0x4000000000000000000000000000000000000000000000000000000000000000);+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , testCase "smod-by-zero" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_smod_by_zero(int256 a) external pure {+            int256 result;+            assembly { result := smod(a, 0) }+            assert(result == 0);+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , testCase "smod-intmin-by-three" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_smod_intmin_by_three() external pure {+            int256 result;+            assembly { result := smod(0x8000000000000000000000000000000000000000000000000000000000000000, 3) }+            assert(result == -2);+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , testCase "sdiv-by-zero" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_sdiv_by_zero(int256 a) external pure {+            int256 result;+            assembly { result := sdiv(a, 0) }+            assert(result == 0);+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , testCase "sdiv-zero-dividend" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_sdiv_zero_dividend(int256 b) external pure {+            int256 result;+            assembly { result := sdiv(0, b) }+            assert(result == 0);+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , testCase "sdiv-truncation" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_sdiv_truncation() external pure {+            int256 result;+            assembly { result := sdiv(sub(0, 7), 2) }+            assert(result == -3);+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , ignoreTestBecause "Currently takes too long" $ testCase "sdiv-sign-symmetry" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_sdiv_sign_symmetry(int256 a, int256 b) external pure {+            if (a == -57896044618658097711785492504343953926634992332820282019728792003956564819968) return;+            if (b == -57896044618658097711785492504343953926634992332820282019728792003956564819968) return;+            if (b == 0) return;+            int256 r1;+            int256 r2;+            assembly {+              r1 := sdiv(a, b)+              r2 := sdiv(sub(0, a), sub(0, b))+            }+            assert(r1 == r2);+          }+        } |]+      (_, res) <- executeWithBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , ignoreTestBecause "Currently takes too long" $ testCase "sdiv-sign-antisymmetry" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_sdiv_sign_antisymmetry(int256 a, int256 b) external pure {+            if (a == -57896044618658097711785492504343953926634992332820282019728792003956564819968) return;+            if (b == 0) return;+            int256 r1;+            int256 r2;+            assembly {+              r1 := sdiv(a, b)+              r2 := sdiv(sub(0, a), b)+            }+            assert(r1 == -r2);+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , testCase "smod-by-one" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_smod_by_one(int256 a) external pure {+            int256 r1;+            int256 r2;+            assembly {+              r1 := smod(a, 1)+              r2 := smod(a, sub(0, 1))+            }+            assert(r1 == 0);+            assert(r2 == 0);+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , testCase "smod-zero-dividend" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_smod_zero_dividend(int256 b) external pure {+            int256 result;+            assembly { result := smod(0, b) }+            assert(result == 0);+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , testCase "smod-sign-matches-dividend" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_smod_sign_matches_dividend(int256 a, int256 b) external pure {+            if (b == 0 || a == 0) return;+            int256 result;+            assembly { result := smod(a, b) }+            if (result != 0) {+              assert((a > 0 && result > 0) || (a < 0 && result < 0));+            }+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , testCase "smod-intmin" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_smod_intmin() external pure {+            int256 result;+            assembly { result := smod(0x8000000000000000000000000000000000000000000000000000000000000000, 2) }+            assert(result == 0);+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , testCase "sdiv-intmin-by-neg-one" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_sdiv_intmin_by_neg_one() external pure {+            int256 result;+            assembly {+              let intmin := 0x8000000000000000000000000000000000000000000000000000000000000000+              result := sdiv(intmin, sub(0, 1))+            }+            // EVM defines sdiv(MIN_INT, -1) = MIN_INT (overflow)+            assert(result == -57896044618658097711785492504343953926634992332820282019728792003956564819968);+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , testCase "smod-intmin-by-neg-one" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_smod_intmin_by_neg_one() external pure {+            int256 result;+            assembly {+              let intmin := 0x8000000000000000000000000000000000000000000000000000000000000000+              result := smod(intmin, sub(0, 1))+            }+            // smod(MIN_INT, -1) = 0 since MIN_INT is divisible by -1+            assert(result == 0);+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  , testCase "sdiv-intmin-by-intmin" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+          function prove_sdiv_intmin_by_intmin() external pure {+            int256 result;+            assembly {+              let intmin := 0x8000000000000000000000000000000000000000000000000000000000000000+              result := sdiv(intmin, intmin)+            }+            assert(result == 1);+          }+        } |]+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      expectProved res+  ]++abdkMathTests :: TestTree+abdkMathTests = testGroup "ABDK math tests"+  [ -- "make verify-hevm T=prove_div_negative_divisor" in https://github.com/gustavo-grieco/abdk-math-64.64-verification+    ignoreTestBecause "Currently takes too long" $ testCase "prove_div_values-abdk" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+            bool public IS_TEST = true;++            int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;+            int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;++            // ABDKMath64x64.fromInt(0) == 0+            int128 private constant ZERO_FP = 0;+            // ABDKMath64x64.fromInt(1) == 1 << 64+            int128 private constant ONE_FP = 0x10000000000000000;++            // ABDKMath64x64.div+            function div(int128 x, int128 y) internal pure returns (int128) {+                unchecked {+                    require(y != 0);+                    int256 result = (int256(x) << 64) / y;+                    require(result >= MIN_64x64 && result <= MAX_64x64);+                    return int128(result);+                }+            }++            // ABDKMath64x64.abs+            function abs(int128 x) internal pure returns (int128) {+                unchecked {+                    require(x != MIN_64x64);+                    return x < 0 ? -x : x;+                }+            }++            // Property: |x / y| <= |x| when |y| >= 1, and |x / y| >= |x| when |y| < 1+            function prove_div_values(int128 x, int128 y) public pure {+                require(y != ZERO_FP);++                int128 x_y = abs(div(x, y));++                if (abs(y) >= ONE_FP) {+                    assert(x_y <= abs(x));+                } else {+                    assert(x_y >= abs(x));+                }+            }+        } |]+      let sig = (Just $ Sig "prove_div_values(int128,int128)" [AbiIntType 128, AbiIntType 128])+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      expectProved res+  -- "make verify-hevm T=prove_div_negative_divisor" in https://github.com/gustavo-grieco/abdk-math-64.64-verification+  , ignoreTestBecause "Currently takes too long" $ testCase "prove_div_negative_divisor" $ do+      Just c <- solcRuntime "C" [i|+        contract C {+            bool public IS_TEST = true;++            int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;+            int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;++            // ABDKMath64x64.fromInt(0) == 0+            int128 private constant ZERO_FP = 0;+            +            // ABDKMath64x64.div+            function div(int128 x, int128 y) internal pure returns (int128) {+                unchecked {+                    require(y != 0);+                    int256 result = (int256(x) << 64) / y;+                    require(result >= MIN_64x64 && result <= MAX_64x64);+                    return int128(result);+                }+            }+            +            // ABDKMath64x64.neg+            function neg(int128 x) internal pure returns (int128) {+                unchecked {+                    require(x != MIN_64x64);+                    return -x;+                }+            }++            // Property: x / (-y) == -(x / y)+            function prove_div_negative_divisor(int128 x, int128 y) public pure {+                require(y < ZERO_FP);++                int128 x_y = div(x, y);+                int128 x_minus_y = div(x, neg(y));++                assert(x_y == neg(x_minus_y));+            }+        } |]+      let sig = (Just $ Sig "prove_div_negative_divisor(int128,int128)" [AbiIntType 128, AbiIntType 128])+      (_, res) <- executeWithShortBitwuzla $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      expectProved res+  ]++expectProved :: [VerifyResult] -> Assertion+expectProved results = assertBool "Must be proved" (null results)++expectNoCex :: [VerifyResult] -> Assertion+expectNoCex results = assertBool "There should be no cex" (not $ any isCex results)++expectNoError :: [VerifyResult] -> Assertion+expectNoError results = assertBool "There should be no error" (not $ any isError results)++expectOneCex :: [VerifyResult] -> Assertion+expectOneCex [Cex _] = assertBool "" True+expectOneCex _ = assertFailure "There should exactly one cex"++expectPartial :: ([Expr End], [VerifyResult]) -> Assertion+expectPartial (paths, _) = assertBool "There should be a partial path" (any isPartial paths)++expectNoCexNoPartial :: ([Expr End], [VerifyResult]) -> Assertion+expectNoCexNoPartial (paths, results) = do+  assertBool "There should be no partial paths" (not $ any isPartial paths)+  expectNoCex results++-- Finds SLoad -> SStore. This should not occur in most scenarios+-- as we can simplify them away+badStoresInExpr :: [Expr a] -> Bool+badStoresInExpr exprs = any (getAny . foldExpr match mempty) exprs+  where+      match (SLoad _ (SStore _ _ _)) = Any True+      match _ = Any False++symExecTestsConfig :: Effects.Config+symExecTestsConfig = Effects.defaultConfig++symExecTestsEnvironment :: Effects.Env+symExecTestsEnvironment = Effects.Env symExecTestsConfig++executeWithBitwuzla :: MonadUnliftIO m => (SolverGroup -> ReaderT Effects.Env m a) -> m a+executeWithBitwuzla action = Effects.runEnv symExecTestsEnvironment $ withBitwuzla action++executeWithShortBitwuzla :: MonadUnliftIO m => (SolverGroup -> ReaderT Effects.Env m a) -> m a+executeWithShortBitwuzla action = Effects.runEnv symExecTestsEnvironment $ withSolvers Bitwuzla 1 (Just 5) defMemLimit action+++withBitwuzla :: Effects.App m => (SolverGroup -> m a) -> m a+withBitwuzla = withSolvers Bitwuzla 1 Nothing defMemLimit++verifyUserAsserts :: ByteString -> Maybe Sig -> IO ([Expr End], [VerifyResult])+verifyUserAsserts c sig = executeWithBitwuzla $ \s -> checkAssert s [0x01] c sig [] defaultVeriOpts++expectAllBranchesFail :: ByteString -> Maybe Sig -> Assertion+expectAllBranchesFail c sig = do+  result <- executeWithBitwuzla $ \s -> verifyContract s c sig [] defaultVeriOpts Nothing post+  expectNoCexNoPartial result+  where+    post _ = \case+      Success _ _ _ _ -> PBool False+      _ -> PBool True
test/EVM/Test/BlockchainTests.hs view
@@ -1,12 +1,12 @@-module EVM.Test.BlockchainTests (prepareTests, problematicTests, Case, vmForCase, checkExpectation, allTestCases) where+module EVM.Test.BlockchainTests (prepareTests, problematicTests, findIgnoreReason, Case, vmForCase, checkExpectation, allTestCases) where -import EVM (initialContract, makeVm, setEIP4788Storage)+import EVM (initialContract, makeVm, setEIP4788Storage, setEIP2935Storage) import EVM.Concrete qualified as EVM import EVM.Effects import EVM.Expr (maybeLitAddrSimp) import EVM.FeeSchedule (feeSchedule) import EVM.Fetch qualified-import EVM.Solvers (withSolvers, Solver(..))+import EVM.Solvers (withSolvers, defMemLimit, Solver(..)) import EVM.Stepper qualified import EVM.Transaction import EVM.Types hiding (Block, Case, Env)@@ -23,14 +23,15 @@ import Data.Aeson.Types qualified as JSON import Data.ByteString qualified as BS import Data.ByteString.Lazy qualified as Lazy+import Data.List (isPrefixOf) import Data.Map (Map) import Data.Map qualified as Map-import Data.Maybe (fromJust, fromMaybe, isNothing, isJust)+import Data.Maybe (fromJust, fromMaybe, isNothing) import Data.Word (Word64) import GHC.Generics (Generic) import System.Environment (getEnv) import System.FilePath.Find qualified as Find-import System.FilePath.Posix (makeRelative, (</>))+import System.FilePath.Posix (makeRelative) import Witch (into, unsafeInto) import Witherable (Filterable, catMaybes) @@ -40,6 +41,12 @@  data Which = Pre | Post +-- EIP-4895: Withdrawal from beacon chain+data Withdrawal = Withdrawal+  { wAddress :: Addr+  , wAmount  :: W256  -- amount in Gwei+  } deriving (Show, Eq)+ data Block = Block   { coinbase    :: Addr   , difficulty  :: W256@@ -50,6 +57,8 @@   , timestamp   :: W256   , txs         :: [Transaction]   , beaconRoot  :: W256+  , parentHash  :: W256+  , withdrawals :: [Withdrawal]   } deriving Show  data BlockchainContract = BlockchainContract@@ -82,9 +91,10 @@ type BlockchainContracts = Map Addr BlockchainContract  data Case = Case-  { vmOpts      :: VMOpts Concrete+  { vmOpts          :: VMOpts Concrete   , checkContracts  :: BlockchainContracts   , testExpectation :: BlockchainContracts+  , caseWithdrawals :: [Withdrawal]   } deriving Show  data BlockchainCase = BlockchainCase@@ -99,6 +109,9 @@   rootDir <- liftIO rootDirectory   liftIO $ putStrLn $ "Loading and parsing json files from ethereum-tests from " <> show rootDir   cases <- liftIO allTestCases+  let testCount = sum . fmap Map.size $ cases+  let expectedTestCount = 16989+  when (testCount < expectedTestCount) $ internalError $ "Lower than expected number of tests!\nExpected: " <> (show expectedTestCount) <> "\nGot: " <> (show testCount)   groups <- forM (Map.toList cases) (\(f, subtests) -> testGroup (makeRelative rootDir f) <$> (process subtests))   liftIO $ putStrLn "Loaded."   pure $ testGroup "ethereum-tests" groups@@ -108,20 +121,22 @@      runTest :: App m => (String, Case) -> m TestTree     runTest (name, x) = do-      let fetcher q = withSolvers Z3 0 1 (Just 0) $ \s -> EVM.Fetch.noRpcFetcher s q+      let fetcher q = withSolvers Z3 0 (Just 0) defMemLimit $ \s -> EVM.Fetch.noRpcFetcher s q       exec <- toIO $ runVMTest fetcher x       pure $ testCase' name exec     testCase' :: String -> Assertion -> TestTree     testCase' name assertion =-      case Map.lookup name problematicTests of-        Just f -> f (testCase name assertion)+      case findIgnoreReason name of+        Just reason -> ignoreTestBecause reason (testCase name assertion)         Nothing -> testCase name assertion +-- | Find if a test name matches any problematic test prefix+findIgnoreReason :: String -> Maybe String+findIgnoreReason name = lookup True [(prefix `isPrefixOf` name, reason) | (prefix, reason) <- problematicTests]+ rootDirectory :: IO FilePath-rootDirectory = do-  repo <- getEnv "HEVM_ETHEREUM_TESTS_REPO"-  let testsDir = "BlockchainTests/GeneralStateTests"-  pure $ repo </> testsDir+rootDirectory = getEnv "HEVM_ETHEREUM_TESTS_REPO"+  -- Env var now points directly to the blockchain_tests directory  collectJsonFiles :: FilePath -> IO [FilePath] collectJsonFiles rootDir = Find.find Find.always (Find.extension Find.==? ".json") rootDir@@ -132,50 +147,85 @@   jsons <- collectJsonFiles root   cases <- forM jsons (\fname -> do       fContents <- BS.readFile fname-      let parsed = case (parseBCSuite (Lazy.fromStrict fContents)) of-                    Left "No cases to check." -> mempty-                    Left _err -> mempty -- TODO: This should be an error-                    Right allTests -> allTests+      parsed <- case (parseBCSuite (Lazy.fromStrict fContents)) of+                    Left "No cases to check." -> pure mempty+                    Left err -> do+                      putStrLn $ "Warning: Failed to parse " ++ fname ++ ": " ++ err+                      pure mempty+                    Right allTests -> pure allTests       pure (fname, parsed)     )   pure $ Map.fromList cases -problematicTests :: Map String (TestTree -> TestTree)-problematicTests = Map.fromList-  [ ("loopMul_d0g0v0_Cancun", ignoreTestBecause "hevm is too slow")-  , ("loopMul_d1g0v0_Cancun", ignoreTestBecause "hevm is too slow")-  , ("loopMul_d2g0v0_Cancun", ignoreTestBecause "hevm is too slow")-  , ("CALLBlake2f_MaxRounds_d0g0v0_Cancun", ignoreTestBecause "very slow, bypasses timeout due time spent in FFI")--  , ("15_tstoreCannotBeDosd_d0g0v0_Cancun", ignoreTestBecause "slow test")-  , ("21_tstoreCannotBeDosdOOO_d0g0v0_Cancun", ignoreTestBecause "slow test")--  -- TODO: implement point evaluation, 0xa precompile, EIP-4844, Cancun-  , ("idPrecomps_d9g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d117g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d12g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d135g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d153g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d171g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d189g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d207g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d225g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d243g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d261g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d279g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d27g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d297g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d315g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d333g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d351g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d369g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d387g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d45g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d63g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d81g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("precompsEIP2929Cancun_d99g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("makeMoney_d0g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")-  , ("failed_tx_xcf416c53_d0g0v0_Cancun", ignoreTestBecause "EIP-4844 not implemented")+-- | Tests that are known to fail or are too slow to run in CI.+-- Uses prefix matching: any test name starting with a prefix will be ignored.+-- Test names are from the execution-spec-tests fixtures format.+problematicTests :: [(String, String)]+problematicTests =+  [ -- EIP-4844 point evaluation precompile (0x0A) not implemented+    ("tests/cancun/eip4844_blobs/test_point_evaluation_precompile.py::", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/cancun/eip4844_blobs/test_point_evaluation_precompile_gas.py::", "EIP-4844 point evaluation precompile (0x0A) not implemented")+    -- EIP-2537 precompiles+  , ("tests/prague/eip2537_bls_12_381_precompiles/test_bls12_g1add.py::", "EIP-2537 precompiles not implemented")+  , ("tests/prague/eip2537_bls_12_381_precompiles/test_bls12_g1msm.py::", "EIP-2537 precompiles not implemented")+  , ("tests/prague/eip2537_bls_12_381_precompiles/test_bls12_g1mul.py::", "EIP-2537 precompiles not implemented")+  , ("tests/prague/eip2537_bls_12_381_precompiles/test_bls12_g2add.py::", "EIP-2537 precompiles not implemented")+  , ("tests/prague/eip2537_bls_12_381_precompiles/test_bls12_g2msm.py::", "EIP-2537 precompiles not implemented")+  , ("tests/prague/eip2537_bls_12_381_precompiles/test_bls12_g2mul.py::", "EIP-2537 precompiles not implemented")+  , ("tests/prague/eip2537_bls_12_381_precompiles/test_bls12_map_fp_to_g1.py::", "EIP-2537 precompiles not implemented")+  , ("tests/prague/eip2537_bls_12_381_precompiles/test_bls12_map_fp2_to_g2.py::", "EIP-2537 precompiles not implemented")+  , ("tests/prague/eip2537_bls_12_381_precompiles/test_bls12_pairing.py::", "EIP-2537 precompiles not implemented")+  , ("tests/prague/eip2537_bls_12_381_precompiles/test_bls12_variable_length_input_contracts.py::", "EIP-2537 precompiles not implemented")+    -- EIP-7951+  , ("tests/osaka/eip7951_p256verify_precompiles/test_eip_mainnet.py::", "EIP-7951 precompiles not implemented")+  , ("tests/osaka/eip7951_p256verify_precompiles/test_p256verify.py::", "EIP-7951 precompiles not implemented")+    -- Other tests that invoke the 0x0A precompile+  , ("tests/frontier/precompiles/test_precompiles.py::test_precompiles[fork_Osaka-address_0x000000000000000000000000000000000000000a", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stSpecialTest/failed_tx_xcf416c53_ParisFiller.json::", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-11]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-13]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-24]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-28]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-39]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-42]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-53]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-64]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-75]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-86]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-97]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-108]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-119]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-130]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-141]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-152]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-163]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-174]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-185]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-196]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-207]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929OsakaFiller.yml::precompsEIP2929Osaka[fork_Osaka-blockchain_test_from_state_test-yes-218]", "EIP-4844 point evaluation precompile (0x0A) not implemented")+    -- Other precompile tests+  , ("tests/frontier/precompiles/test_precompiles.py::test_precompiles[fork_Osaka-address_0x000000000000000000000000000000000000000b", "0xB precompile not implemented")+  , ("tests/frontier/precompiles/test_precompiles.py::test_precompiles[fork_Osaka-address_0x000000000000000000000000000000000000000c", "0xC precompile not implemented")+  , ("tests/frontier/precompiles/test_precompiles.py::test_precompiles[fork_Osaka-address_0x000000000000000000000000000000000000000d", "0xD precompile not implemented")+  , ("tests/frontier/precompiles/test_precompiles.py::test_precompiles[fork_Osaka-address_0x000000000000000000000000000000000000000e", "0xE precompile not implemented")+  , ("tests/frontier/precompiles/test_precompiles.py::test_precompiles[fork_Osaka-address_0x000000000000000000000000000000000000000f", "0xF precompile not implemented")+  , ("tests/frontier/precompiles/test_precompiles.py::test_precompiles[fork_Osaka-address_0x0000000000000000000000000000000000000010", "0x10 precompile not implemented")+  , ("tests/frontier/precompiles/test_precompiles.py::test_precompiles[fork_Osaka-address_0x0000000000000000000000000000000000000011", "0x11 precompile not implemented")+  , ("tests/frontier/precompiles/test_precompiles.py::test_precompiles[fork_Osaka-address_0x0000000000000000000000000000000000000100", "0x100 precompile not implemented")+  , ("tests/static/state_tests/stPreCompiledContracts/precompsEIP2929CancunFiller.yml::", "TODO")+    -- Needs EIP-7702 otherContractsFromPreState+  , ("tests/static/state_tests/stCreate2/create2collisionSelfdestructed2Filler.json::", "needs EIP-7702")+  , ("tests/static/state_tests/stCreate2/create2collisionSelfdestructedFiller.json::", "needs EIP-7702")+  , ("tests/static/state_tests/stCreate2/create2collisionSelfdestructedRevertFiller.json::", "needs EIP-7702")+    -- EIP-7685: General Purpose EL Requests - requires multi-block context+  , ("tests/prague/eip7685_general_purpose_el_requests/test_multi_type_requests.py::", "TODO")+    -- EIP-7702: Set Code TX - not yet implemented+  , ("tests/prague/eip7702_set_code_tx/test_calls.py::", "TODO")+  , ("tests/prague/eip7702_set_code_tx/test_gas.py::", "TODO")+  , ("tests/prague/eip7702_set_code_tx/test_set_code_txs_2.py::", "TODO")+    -- EIP-2935: Historical block hashes - requires multi-block context+  , ("tests/prague/eip2935_historical_block_hashes_from_state/test_block_hashes.py::", "TODO")   ]  @@ -185,9 +235,33 @@   vm0 <- liftIO $ vmForCase x   result <- EVM.Stepper.interpret fetcher vm0 EVM.Stepper.runFully   writeTrace result-  let maybeReason = checkExpectation x result+  -- Apply EIP-4895 withdrawals after transaction execution+  let resultWithWithdrawals = applyWithdrawals x.caseWithdrawals result+  let maybeReason = checkExpectation x resultWithWithdrawals   liftIO $ forM_ maybeReason (liftIO >=> assertFailure) +-- | Apply EIP-4895 withdrawals to VM state+-- Withdrawals credit balance to addresses (amount is in Gwei, multiply by 10^9 for Wei)+applyWithdrawals :: [Withdrawal] -> VM Concrete -> VM Concrete+applyWithdrawals ws vm = foldl applyWithdrawal vm ws+  where+    applyWithdrawal :: VM Concrete -> Withdrawal -> VM Concrete+    applyWithdrawal vm' (Withdrawal addr amount) =+      let weiAmount = Lit (amount * 1000000000)  -- Gwei to Wei+          addrExpr = LitAddr addr+          contracts' = Map.alter (creditBalance weiAmount) addrExpr vm'.env.contracts+      in vm' { env = vm'.env { contracts = contracts' } }++    creditBalance :: Expr EWord -> Maybe Contract -> Maybe Contract+    creditBalance weiAmount Nothing =+      -- Create new account with just the withdrawal balance+      Just $ (EVM.initialContract (RuntimeCode (ConcreteRuntimeCode "")))+        { balance = weiAmount }+    creditBalance (Lit weiAmount) (Just c) =+      -- Add to existing balance+      Just $ c { balance = Lit (forceLit c.balance + weiAmount) }+    creditBalance _ (Just c) = Just c  -- shouldn't happen in concrete execution+ checkExpectation :: Case -> VM Concrete -> Maybe (IO String) checkExpectation x vm = let (okState, okBal, okNonce, okStor, okCode) = checkExpectedContracts vm x.testExpectation in   if okState then Nothing else Just $ checkStateFail x (okBal, okNonce, okStor, okCode)@@ -280,6 +354,14 @@   parseJSON invalid =     JSON.typeMismatch "GeneralState test case" invalid +instance FromJSON Withdrawal where+  parseJSON (JSON.Object v) = do+    addr   <- addrField v "address"+    amount <- wordField v "amount"+    pure $ Withdrawal addr amount+  parseJSON invalid =+    JSON.typeMismatch "Withdrawal" invalid+ instance FromJSON Block where   parseJSON (JSON.Object v) = do     v'         <- v .: "blockHeader"@@ -292,9 +374,12 @@     timestamp  <- wordField v' "timestamp"     mixHash    <- wordField v' "mixHash"     beaconRoot <- fmap read <$> v' .:? "parentBeaconBlockRoot"+    parentHash <- wordField v' "parentHash"+    ws         <- v .:? "withdrawals"     pure $ Block { coinbase, difficulty, mixHash, gasLimit                  , baseFee = fromMaybe 0 baseFee, number, timestamp                  , txs, beaconRoot = fromMaybe 0 beaconRoot+                 , parentHash, withdrawals = fromMaybe [] ws                  }   parseJSON invalid =     JSON.typeMismatch "Block" invalid@@ -305,7 +390,7 @@           Pre  -> "pre"           Post -> "postState" -parseBCSuite :: Lazy.ByteString-> Either String (Map String Case)+parseBCSuite :: Lazy.ByteString -> Either String (Map String Case) parseBCSuite x = case (JSON.eitherDecode' x) :: Either String (Map String BlockchainCase) of   Left e        -> Left e   Right bcCases -> let allCases = fromBlockchainCase <$> bcCases@@ -328,11 +413,11 @@   | InvalidTx   | OldNetwork   | FailedCreate+  | UnsupportedTxType   deriving Show  errorFatal :: BlockchainError -> Bool-errorFatal TooManyBlocks = True-errorFatal TooManyTxs = True+errorFatal FailedCreate = True errorFatal SignatureUnverified = True errorFatal InvalidTx = True errorFatal _ = False@@ -340,7 +425,8 @@ fromBlockchainCase :: BlockchainCase -> Either BlockchainError Case fromBlockchainCase (BlockchainCase blocks preState postState network) =   case (blocks, network) of-    ([block], "Cancun") -> case block.txs of+    ([block], "Osaka") -> case block.txs of+      [tx] | tx.txtype == EIP4844Transaction || tx.txtype == EIP7702Transaction -> Left UnsupportedTxType -- TODO EIP4844 / EIP7702       [tx] -> fromBlockchainCase' block tx preState postState       []        -> Left NoTxs       _         -> Left TooManyTxs@@ -386,9 +472,12 @@        , allowFFI       = False        , freshAddresses = 0        , beaconRoot     = block.beaconRoot+       , parentHash     = block.parentHash+       , txdataFloorGas = txdataFloorGas feeSchedule tx        })       checkState       postState+      block.withdrawals         where           toAddr = maybe (EVM.createAddress origin (fromJust senderNonce)) LitAddr (tx.toAddr)           senderNonce = (.nonce) <$> Map.lookup origin preState@@ -426,30 +515,25 @@  checkTx :: Transaction -> Block -> BlockchainContracts -> Maybe (BlockchainContracts) checkTx tx block prestate = do-  origin <- sender tx   validateTx tx block prestate-  if (isJust tx.toAddr) then pure prestate-  else-    let senderNonce = (.nonce) <$> Map.lookup origin prestate-        addr  = case EVM.createAddress origin (fromJust senderNonce) of-                  (LitAddr a) -> a-                  _ -> internalError "Cannot happen"-        freshContract = BlockchainContract (ByteStringS "") 0 0 mempty-        (BlockchainContract (ByteStringS b) prevNonce _ _) = (fromMaybe freshContract $ Map.lookup addr prestate)-        nonEmptyAccount = not (BS.null b)-        badNonce = prevNonce /= 0-        initCodeSizeExceeded = BS.length tx.txdata > (unsafeInto maxCodeSize * 2)-    in-    if (badNonce || nonEmptyAccount || initCodeSizeExceeded) then mzero-    else pure prestate+  let initCodeSizeExceeded = isNothing tx.toAddr+        && BS.length tx.txdata > (unsafeInto maxCodeSize * 2)+  if initCodeSizeExceeded then mzero+  else pure prestate +-- EIP-7825: Maximum transaction gas limit is 2^24+maxTxGasLimit :: Word64+maxTxGasLimit = 2 ^ (24 :: Int)+ validateTx :: Transaction -> Block -> BlockchainContracts -> Maybe () validateTx tx block cs = do   origin <- sender tx   originBalance <- (.balance) <$> Map.lookup origin cs   originNonce <- (.nonce) <$> Map.lookup origin cs   let gasDeposit = (effectiveprice tx block.baseFee) * (into tx.gasLimit)-  if gasDeposit + tx.value <= originBalance+  -- EIP-7825: Reject transactions exceeding max gas limit+  if tx.gasLimit > maxTxGasLimit then Nothing+  else if gasDeposit + tx.value <= originBalance     && ((unsafeInto tx.nonce) == originNonce) && block.baseFee <= maxBaseFee tx   then Just ()   else Nothing@@ -462,6 +546,7 @@     -- TODO: we need to call this again because we override contracts in the     -- previous line     <&> setEIP4788Storage x.vmOpts+    <&> setEIP2935Storage x.vmOpts   pure $ initTx vm  forceConcreteAddrs :: Map (Expr EAddr) Contract -> Map Addr Contract
+ test/EVM/Test/FoundryTests.hs view
@@ -0,0 +1,181 @@+module EVM.Test.FoundryTests (tests) where++import Control.Monad (forM_)+import Control.Monad.Catch (MonadMask)+import Control.Monad.IO.Class+import Control.Monad.Reader (ReaderT)+import Data.Text (Text, isPrefixOf, unpack)+import Test.Tasty+import Test.Tasty.HUnit++import EVM.ABI (Sig(..))+import EVM.Dapp (TestMethodInfo(..))+import EVM.Effects+import EVM.Fetch qualified as Fetch+import EVM.Test.Utils (runForgeTest, runForgeTestCustom)+import EVM.Types (regexMatches)++foundryTestConfig :: Config+foundryTestConfig = defaultConfig+++test :: TestName -> ReaderT Env IO () -> TestTree+test a b = testCase a $ runEnv (Env foundryTestConfig) b++assertEqualM :: (MonadIO m, Eq a, Show a, HasCallStack) => String -> a -> a -> m ()+assertEqualM a b c = liftIO $ assertEqual a b c++tests :: TestTree+tests = testGroup "Foundry tests"+    [ test "Trivial-Pass" $ do+        let testFile = "test/contracts/pass/trivial.sol"+        executeSingleMethod testFile "prove_true" >>= assertEqualM "test result" (True, True)+    , test "Foundry" $ do+        -- quick smokecheck to make sure that we can parse ForgeStdLib style build outputs+        -- return is a pair of (No Cex, No Warnings)+        let cases =+              [ ("test/contracts/pass/trivial.sol",       ".*", (True, True))+              , ("test/contracts/pass/dsProvePass.sol",   ".*", (True, True))+              , ("test/contracts/pass/revertEmpty.sol",   ".*", (True, True))+              , ("test/contracts/pass/revertString.sol",  ".*", (True, True))+              , ("test/contracts/pass/requireEmpty.sol",  ".*", (True, True))+              , ("test/contracts/pass/requireString.sol", ".*", (True, True))+              -- overapproximation+              , ("test/contracts/pass/no-overapprox-staticcall.sol", ".*", (True, True))+              , ("test/contracts/pass/no-overapprox-delegatecall.sol", ".*", (True, True))+              -- failure cases+              , ("test/contracts/fail/trivial.sol",       ".*", (False, False))+              , ("test/contracts/fail/dsProveFail.sol",   "prove_add", (False, True))+              , ("test/contracts/fail/dsProveFail.sol",   "prove_multi", (False, True))+              -- all branches revert, which is a warning+              , ("test/contracts/fail/dsProveFail.sol",   "prove_trivial.*", (False, False))+              , ("test/contracts/fail/dsProveFail.sol",   "prove_distributivity", (False, True))+              , ("test/contracts/fail/assertEq.sol",      ".*", (False, True))+              -- bad cheatcode detected, hence the warning+              , ("test/contracts/fail/bad-cheatcode.sol", ".*", (False, False))+              -- symbolic failures -- either the text or the selector is symbolic+              , ("test/contracts/fail/symbolicFail.sol",      "prove_conc_fail_allrev.*", (False, False))+              , ("test/contracts/fail/symbolicFail.sol",      "prove_conc_fail_somerev.*", (False, True))+              , ("test/contracts/fail/symbolicFail.sol",      "prove_symb_fail_allrev_text.*", (False, False))+              , ("test/contracts/fail/symbolicFail.sol",      "prove_symb_fail_somerev_text.*", (False, True))+              , ("test/contracts/fail/symbolicFail.sol",      "prove_symb_fail_allrev_selector.*", (False, False))+              , ("test/contracts/fail/symbolicFail.sol",      "prove_symb_fail_somerev_selector.*", (False, True))+              -- vm.etch+              , ("test/contracts/pass/etch.sol",          "prove_etch.*", (True, True))+              , ("test/contracts/pass/etch.sol",          "prove_deal.*", (True, True))+              , ("test/contracts/fail/etchFail.sol",      "prove_etch_fail.*", (False, True))+              ]+        forM_ cases $ \(testFile, match, expected) -> do+          actual <- executeTestMethodsMatching testFile match+          assertEqualM "Must match" expected actual+    , test "Trivial-Fail" $ do+        let testFile = "test/contracts/fail/trivial.sol"+        executeSingleMethod testFile "prove_false" >>= assertEqualM "test result" (False, False)+    , test "Abstract" $ do+        let testFile = "test/contracts/pass/abstract.sol"+        executeAllMethodsWithPrefix testFile "test" >>= assertEqualM "test result" (True, True)+    , test "Constantinople" $ do+        let testFile = "test/contracts/pass/constantinople.sol"+        executeAllMethodsWithPrefix testFile "check_" >>= assertEqualM "test result" (True, True)+    , test "Fusaka" $ do+        let testFile = "test/contracts/pass/fusaka.sol"+        executeAllMethodsWithPrefix testFile "check_" >>= assertEqualM "test result" (True, True)+    , test "Prove-Tests-Pass" $ do+        let testFile = "test/contracts/pass/dsProvePass.sol"+        executeAllMethodsWithPrefix testFile "prove" >>= assertEqualM "test result" (True, True)+    , test "prefix-check" $ do+        let testFile = "test/contracts/fail/check-prefix.sol"+        executeSingleMethod testFile "check_trivial" >>= assertEqualM "test result" (False, False)+    , test "transfer-dapp" $ do+        let testFile = "test/contracts/pass/transfer.sol"+        executeSingleMethod testFile "prove_transfer" >>= assertEqualM "should prove transfer" (True, True)+    , test "nonce-issues" $ do+        let testFile = "test/contracts/pass/nonce-issues.sol"+        executeSingleMethod testFile "prove_prank_addr_exists" >>= assertEqualM "should not bail" (True, True)+        executeSingleMethod testFile "prove_nonce_addr_nonexistent" >>= assertEqualM "should not bail" (True, True)+    , test "Prove-Tests-Fail" $ do+        let testFile = "test/contracts/fail/dsProveFail.sol"+        executeSingleMethod testFile "prove_trivial" >>= assertEqualM "prove_trivial" (False, False)+        executeSingleMethod testFile "prove_trivial_dstest" >>= assertEqualM "prove_trivial_dstest" (False, False)+        executeSingleMethod testFile "prove_add" >>= assertEqualM "prove_add" (False, True)+        runForgeTestCustom testFile (\(TestMethodInfo _ (Sig name _)) -> regexMatches "prove_smtTimeout" name) (Just 1) Nothing False Fetch.noRpc+          >>= assertEqualM "prove_smtTimeout" (True, False)+        executeSingleMethod testFile "prove_multi" >>= assertEqualM "prove_multi" (False, True)+        executeSingleMethod testFile "prove_distributivity" >>= assertEqualM "prove_distributivity" (False, True)+    , test "Loop-Tests" $ do+        let testFile = "test/contracts/pass/loops.sol"+        runForgeTestCustom testFile (\(TestMethodInfo _ (Sig name _)) -> regexMatches "prove_loop" name) Nothing (Just 10) False Fetch.noRpc  >>= assertEqualM "test result" (True, False)+        runForgeTestCustom testFile (\(TestMethodInfo _ (Sig name _)) -> regexMatches "prove_loop" name) Nothing (Just 100) False Fetch.noRpc >>= assertEqualM "test result" (False, False)+    , test "Cheat-Codes-Pass" $ do+        let testFile = "test/contracts/pass/cheatCodes.sol"+        executeAllMethodsWithPrefix testFile "prove" >>= assertEqualM "test result" (True, False)+    , test "Expect-Revert-Pass" $ do+        let testFile = "test/contracts/pass/expectRevert.sol"+        executeAllMethodsWithPrefix testFile "prove" >>= assertEqualM "test result" (True, True)+    , test "Expect-Revert-Fail" $ do+        let testFile = "test/contracts/fail/expectRevert.sol"+        executeAllMethodsWithPrefix testFile "prove" >>= assertEqualM "test result" (False, True)+    , test "Cheat-Codes-Fork-Pass" $ do+        let testFile = "test/contracts/pass/cheatCodesFork.sol"+        executeAllMethodsWithPrefix testFile "prove" >>= assertEqualM "test result" (True, True)+    , test "Unwind" $ do+        let testFile = "test/contracts/pass/unwind.sol"+        executeAllMethodsWithPrefix testFile "prove" >>= assertEqualM "test result" (True, True)+    , test "Keccak" $ do+        let testFile = "test/contracts/pass/keccak.sol"+        executeSingleMethod testFile "prove_access" >>= assertEqualM "test result" (True, True)+    , test "AssertApproxEqAbs-Pass" $ do+        let testFile = "test/contracts/pass/assertApproxEqAbs.sol"+        executeAllMethodsWithPrefix testFile "prove" >>= assertEqualM "test result" (True, True)+    , test "AssertApproxEqAbs-Fail" $ do+        let testFile = "test/contracts/fail/assertApproxEqAbs.sol"+        let cases =+              -- concrete-only tests: all branches revert, hence (False, False)+              [ ("prove_approx_eq_abs_uint_exceeds_delta", (False, False))+              , ("prove_approx_eq_abs_uint_zero_delta_neq", (False, False))+              , ("prove_approx_eq_abs_int_exceeds_delta", (False, False))+              , ("prove_approx_eq_abs_int_min_zero_tight", (False, False))+              -- symbolic: not all branches revert+              , ("prove_approx_eq_abs_uint_symbolic_fail", (False, True))+              ]+        forM_ cases $ \(method, expected) -> do+          executeSingleMethod testFile method >>= assertEqualM (unpack method) expected+    , test "AssertApproxEqRel-Pass" $ do+        let testFile = "test/contracts/pass/assertApproxEqRel.sol"+        executeAllMethodsWithPrefix testFile "prove" >>= assertEqualM "test result" (True, True)+    , test "AssertApproxEqRel-Fail" $ do+        let testFile = "test/contracts/fail/assertApproxEqRel.sol"+        let cases =+              -- concrete-only tests: all branches revert, hence (False, False)+              [ ("prove_approx_eq_rel_uint_exceeds_delta", (False, False))+              , ("prove_approx_eq_rel_uint_b_zero_neq", (False, False))+              , ("prove_approx_eq_rel_uint_overflow_delta", (False, False))+              , ("prove_approx_eq_rel_int_exceeds_delta", (False, False))+              , ("prove_approx_eq_rel_int_b_zero_neq", (False, False))+              -- symbolic: not all branches revert+              , ("prove_approx_eq_rel_uint_symbolic_fail", (False, True))+              ]+        forM_ cases $ \(method, expected) -> do+          executeSingleMethod testFile method >>= assertEqualM (unpack method) expected+    , test "AssertMsg-Pass" $ do+        let testFile = "test/contracts/pass/assertMsg.sol"+        executeAllMethodsWithPrefix testFile "prove" >>= assertEqualM "test result" (True, True)+    , test "AssertMsg-Fail" $ do+        -- every message/bytes overload is violated; all concrete, so all branches+        -- revert -> (False, False)+        let testFile = "test/contracts/fail/assertMsg.sol"+        executeAllMethodsWithPrefix testFile "prove" >>= assertEqualM "test result" (False, False)+    , test "Panic-Source-Location" $ do+        -- Regression test for #895: panic in a called contract should not show "<source not found>"+        let testFile = "test/contracts/fail/assertPanic.sol"+        executeSingleMethod testFile "prove_panic" >>= assertEqualM "prove_panic" (False, False)+    ]++executeSingleMethod :: (App m, MonadMask m) => FilePath -> Text -> m (Bool, Bool)+executeSingleMethod file methodName = runForgeTest file (\(TestMethodInfo _ (Sig name _)) -> regexMatches methodName name)++executeAllMethodsWithPrefix :: (App m, MonadMask m) => FilePath -> Text -> m (Bool, Bool)+executeAllMethodsWithPrefix file prefix = runForgeTest file (\(TestMethodInfo _ (Sig name _)) -> prefix `isPrefixOf` name)++executeTestMethodsMatching :: (App m, MonadMask m) => FilePath -> Text -> m (Bool, Bool)+executeTestMethodsMatching file matcher = runForgeTest file (\(TestMethodInfo _ (Sig name _)) -> regexMatches matcher name && "prove" `isPrefixOf` name)
test/EVM/Test/FuzzSymExec.hs view
@@ -10,7 +10,7 @@ of this code, we also generate a symbolic expression then evaluate it concretely through Expr.simplify, then check that against evmtool's output. -}-module EVM.Test.FuzzSymExec where+module EVM.Test.FuzzSymExec (tests) where  import Control.Monad (when) import Control.Monad.IO.Unlift@@ -44,7 +44,7 @@ import Test.Tasty.QuickCheck hiding (Failure, Success) import Witch (into, unsafeInto) -import EVM (makeVm, initialContract, symbolify)+import EVM (makeVm, initialContract, symbolify, defaultVMOpts) import EVM.Assembler (assemble) import EVM.Expr qualified as Expr import EVM.Exec (ethrunAddress)@@ -148,20 +148,6 @@                               Lit a -> a                               _ -> internalError "Timestamp needs to be a Lit" -emptyEvmToolEnv :: EVMToolEnv-emptyEvmToolEnv = EVMToolEnv { coinbase = 0-                             , timestamp = Lit 0-                             , number     = Lit 0-                             , gasLimit   = 0xffffffffffffffff-                             , baseFee    = 0-                             , maxCodeSize= 0xffffffff-                             , schedule   = feeSchedule-                             , blockHashes = mempty-                             , withdrawals = mempty-                             , currentRandom = 42-                             , parentBeaconBlockRoot = 5-                             }- data EVMToolReceipt =   EVMToolReceipt     { _type :: String@@ -223,11 +209,6 @@                          , ("nonce", (JSON.toJSON b.nonce))                          ] -emptyEVMToolAlloc :: EVMToolAlloc-emptyEVMToolAlloc = EVMToolAlloc { balance = 0-                                 , code = mempty-                                 , nonce = 0-                                 } -- Sets up common parts such as TX, origin contract, and environment that can -- later be used to create & execute either an evmtool (from go-ethereum) or an -- HEVM transaction. Some elements here are hard-coded such as the secret key,@@ -298,7 +279,7 @@   JSON.encodeFile (evmDir </> "alloc.json") alloc   JSON.encodeFile (evmDir </> "env.json") evmEnv   let cmd = (proc "evm" [ "transition"-                        , "--state.fork", "Cancun"+                        , "--state.fork", "Osaka"                         , "--input.alloc", "alloc.json"                         , "--input.env", "env.json"                         , "--input.txs", "txs.json"@@ -340,7 +321,7 @@       when (aOp /= bOp || aPc /= bPc) $ do         putStrLn $ "HEVM: " <> (intToOpName aOp) <> " (pc " <> (show aPc) <> ") --- evmtool " <> (intToOpName bOp) <> " (pc " <> (show bPc) <> ")" -      when (isJust b.error) $ do+      when (isJust b.error && verbosity > 0) $ do         putStrLn $ "Error by evmtool: " <> (show b.error)         putStrLn $ "Error by HEVM   : " <> (show a.error) @@ -401,12 +382,12 @@   :: App m   => Fetch.RpcInfo -> EVMToolEnv -> EVMToolAlloc -> EVM.Transaction.Transaction   -> Expr EAddr -> Expr EAddr -> m (Either (EvmError, [VMTraceStep]) (([Expr 'End], [VMTraceStep], VMTraceStepResult)))-runCodeWithTrace rpcinfo evmEnv alloc txn fromAddr toAddress = withSolvers Z3 0 1 Nothing $ \solvers -> do+runCodeWithTrace rpcinfo evmEnv alloc txn fromAddr toAddress = withSolvers Z3 0 Nothing defMemLimit $ \solvers -> do   let calldata' = ConcreteBuf txn.txdata       code' = alloc.code       iterConf = IterConfig { maxIter = Nothing, askSmtIters = 1, loopHeuristic = Naive }       fetcherSym = Fetch.oracle solvers Nothing rpcinfo-      buildExpr vm = interpret fetcherSym iterConf vm runExpr pure+      buildExpr vm = interpret fetcherSym iterConf vm runExpr noopPathHandler   origVM <- liftIO $ stToIO $ vmForRuntimeCode code' calldata' evmEnv alloc txn fromAddr toAddress   expr <- buildExpr $ symbolify origVM @@ -420,12 +401,10 @@ vmForRuntimeCode runtimecode calldata' evmToolEnv alloc txn fromAddr toAddress =   let contract = initialContract (RuntimeCode (ConcreteRuntimeCode runtimecode))                  & set #balance (Lit alloc.balance)-  in (makeVm $ VMOpts+  in (makeVm $ (defaultVMOpts @Concrete)     { contract = contract-    , otherContracts = []     , calldata = (calldata', [])     , value = Lit txn.value-    , baseState = EmptyBase     , address =  toAddress     , caller = fromAddr     , origin = fromAddr@@ -442,11 +421,6 @@     , maxCodeSize = evmToolEnv.maxCodeSize     , schedule = evmToolEnv.schedule     , chainId = txn.chainId-    , create = False-    , txAccessList = mempty-    , allowFFI = False-    , freshAddresses = 0-    , beaconRoot = 0     }) <&> set (#env % #contracts % at (LitAddr ethrunAddress))              (Just (initialContract (RuntimeCode (ConcreteRuntimeCode BS.empty))))        <&> set (#state % #calldata) calldata'@@ -681,18 +655,6 @@ randItem :: [a] -> IO a randItem = generate . Test.QuickCheck.elements -getOpFromVM :: VM t -> Word8-getOpFromVM vm =-  let pcpos  = vm ^. #state % #pc-      code' = vm ^. #state % #code-      xs = case code' of-        UnknownCode _ -> internalError "UnknownCode instead of RuntimeCode"-        InitCode bs _ -> BS.drop pcpos bs-        RuntimeCode (ConcreteRuntimeCode xs') -> BS.drop pcpos xs'-        RuntimeCode (SymbolicRuntimeCode _) -> internalError "RuntimeCode is symbolic"-  in if xs == BS.empty then 0-                       else BS.head xs- testEnv :: Env testEnv = Env { config = defaultConfig } @@ -748,6 +710,8 @@               ]         checkTraceAndOutputs contract 40000 (BS.pack [1, 2, 3, 4, 5, 6])     ]+verbosity :: Int+verbosity = 0  checkTraceAndOutputs :: App m => OpContract -> Int -> ByteString -> m () checkTraceAndOutputs contract gasLimit txData = do@@ -778,13 +742,14 @@       assertEqual "Traces and gas must match" traceOK True       let resultOK = evmtoolTraceOutput.output.output == hevmTraceResult.out       if resultOK then liftIO $ do-        putStrLn $ "HEVM & evmtool's outputs match: '" <> (bsToHex $ bssToBs evmtoolTraceOutput.output.output) <> "'"+        when (verbosity > 0) $ putStrLn $ "HEVM & evmtool's outputs match: '" <> (bsToHex $ bssToBs evmtoolTraceOutput.output.output) <> "'"         assertBool "Cannot have more than one success return value" (length simplConcrExprRetval <= 1)         if null simplConcrExprRetval || (simplConcrExprRetval !! 0) == (bssToBs hevmTraceResult.out)            then do-             putStr "OK, symbolic interpretation -> concrete calldata -> Expr.simplify gives the same answer."-             if null simplConcrExprRetval then putStrLn ", but it was a Nothing, so not strong equivalence"-                                          else putStrLn ""+             when (verbosity > 0) $ do+              putStr "OK, symbolic interpretation -> concrete calldata -> Expr.simplify gives the same answer."+              if null simplConcrExprRetval then putStrLn ", but it was a Nothing, so not strong equivalence"+                                           else putStrLn ""            else do              putStrLn $ "original expr                    : " <> (show expr)              putStrLn $ "concretized expr                 : " <> (show concretizedExpr)@@ -792,7 +757,7 @@              putStrLn $ "evmtoolTraceOutput.output.output : " <> (show (evmtoolTraceOutput.output.output))              putStrLn $ "HEVM trace result output         : " <> (bsToHex (bssToBs hevmTraceResult.out))              putStrLn $ "ret value computed via symb+conc : " <> (bsToHex (simplConcrExprRetval !! 0))-             assertEqual "Simplified, concretized expression must match evmtool's output." True False+             assertBool "Simplified, concretized expression must match evmtool's output." False       else do         putStrLn $ "Name of trace file: " <> (getTraceFileName evmDir $ fromJust evmtoolResult)         putStrLn $ "HEVM result  :" <> (show hevmTraceResult)@@ -800,13 +765,13 @@         T.putStrLn $ "evm result : " <> (formatBinary $ bssToBs evmtoolTraceOutput.output.output)         putStrLn $ "HEVM result len: " <> (show (BS.length $ bssToBs hevmTraceResult.out))         putStrLn $ "evm result  len: " <> (show (BS.length $ bssToBs evmtoolTraceOutput.output.output))-      assertEqual "Contract exec successful. HEVM & evmtool's outputs must match" resultOK True+      assertBool "Contract exec successful. HEVM & evmtool's outputs must match" resultOK     Left (evmerr, hevmTrace) -> liftIO $ do-      putStrLn $ "HEVM contract exec issue: " <> (show evmerr)+      when (verbosity > 0) $ putStrLn $ "HEVM contract exec issue: " <> (show evmerr)       -- putStrLn $ "evmtool result was: " <> show (fromJust evmtoolResult)       -- putStrLn $ "output by evmtool is: '" <> bsToHex evmtoolTraceOutput.toOutput.output <> "'"       traceOK <- compareTraces hevmTrace (evmtoolTraceOutput.trace)-      assertEqual "Traces and gas must match" traceOK True+      assertBool "Traces and gas must match" traceOK   liftIO $ removeDirectoryRecursive evmDir  -- GasLimitInt@@ -815,7 +780,7 @@  instance Arbitrary GasLimitInt where   arbitrary = do-    let mkLimit = chooseInt (50000, 0xfffff)+    let mkLimit = chooseInt (60000, 0xfffff)     fmap GasLimitInt mkLimit  -- GenTxDataRaw
test/EVM/Test/Utils.hs view
@@ -1,6 +1,5 @@ module EVM.Test.Utils where -import Data.Text (Text) import Data.List (isInfixOf) import GHC.IO.Exception (IOErrorType(..)) import GHC.Natural@@ -12,7 +11,7 @@ import System.Exit import System.IO.Error (mkIOError) -import EVM.Dapp (dappInfo, emptyDapp)+import EVM.Dapp (dappInfo, emptyDapp, TestMethodFilter) import EVM.Solidity import EVM.Solvers import EVM.UnitTest@@ -29,8 +28,8 @@ -- Returns tuple of (No cex, No warnings) runForgeTestCustom   :: (MonadMask m, App m)-  => FilePath -> Text -> Maybe Natural -> Maybe Integer -> Bool -> RpcInfo -> m (Bool, Bool)-runForgeTestCustom testFile match timeout maxIter ffiAllowed rpcinfo = do+  => FilePath -> TestMethodFilter -> Maybe Natural -> Maybe Integer -> Bool -> RpcInfo -> m (Bool, Bool)+runForgeTestCustom testFile methodFilter timeout maxIter ffiAllowed rpcinfo = do   withSystemTempDirectory "dapp-test" $ \root -> do     compileWithForge root testFile >>= \case       Left e -> liftIO $ do@@ -38,18 +37,18 @@         internalError $ "Error compiling test file " <> show testFile <> " in directory "           <> show root       Right buildOut -> do-        withSolvers Bitwuzla 3 1 timeout $ \solvers -> do-          opts <- testOpts solvers root (Just buildOut) match maxIter ffiAllowed rpcinfo+        withSolvers Bitwuzla 3 timeout defMemLimit $ \solvers -> do+          opts <- testOpts solvers root (Just buildOut) methodFilter maxIter ffiAllowed rpcinfo           unitTest opts buildOut  -- Returns tuple of (No cex, No warnings) runForgeTest   :: (MonadMask m, App m)-  => FilePath -> Text -> m (Bool, Bool)-runForgeTest testFile match = runForgeTestCustom testFile match Nothing Nothing True noRpc+  => FilePath -> TestMethodFilter -> m (Bool, Bool)+runForgeTest testFile methodFilter = runForgeTestCustom testFile methodFilter Nothing Nothing False noRpc -testOpts :: forall m . App m => SolverGroup -> FilePath -> Maybe BuildOutput -> Text -> Maybe Integer -> Bool -> RpcInfo -> m (UnitTestOptions)-testOpts solvers root buildOutput match maxIter allowFFI rpcinfo = do+testOpts :: forall m . App m => SolverGroup -> FilePath -> Maybe BuildOutput -> TestMethodFilter -> Maybe Integer -> Bool -> RpcInfo -> m (UnitTestOptions)+testOpts solvers root buildOutput methodFilter maxIter allowFFI rpcinfo = do   let srcInfo = maybe emptyDapp (dappInfo root) buildOutput   sess <- Fetch.mkSessionWithoutCache   params <- paramsFromRpc rpcinfo sess@@ -61,8 +60,7 @@     , maxIter = maxIter     , askSmtIters = 1     , smtTimeout = Nothing-    , match = match-    , prefix = "prove"+    , methodFilter = methodFilter     , testParams = params     , dapp = srcInfo     , ffiAllowed = allowFFI@@ -87,11 +85,13 @@  compileWithForge :: App m => FilePath -> FilePath -> m (Either String BuildOutput) compileWithForge root src = do-  liftIO $ createDirectory (root </> "src")-  liftIO $ writeFile (root </> "src" </> "unit-tests.t.sol") =<< readFile =<< Paths.getDataFileName src-  liftIO $ initLib (root </> "lib" </> "tokens") ("test" </> "contracts" </> "lib" </> "erc20.sol") "erc20.sol"-  liftIO $ initStdForgeDir (root </> "lib" </> "forge-std")-  (res,out,err) <- liftIO $ readProcessWithExitCode "forge" ["build", "--ast", "--root", root] ""+  (res, out, err) <- liftIO $ do+    createDirectory (root </> "src")+    writeFile (root </> "foundry.toml") "[profile.default]\nevm_version = \"Osaka\"\nast = true\n"+    writeFile (root </> "src" </> "unit-tests.t.sol") =<< readFile =<< Paths.getDataFileName src+    initLib (root </> "lib" </> "tokens") ("test" </> "contracts" </> "lib" </> "erc20.sol") "erc20.sol"+    initStdForgeDir (root </> "lib" </> "forge-std")+    readProcessWithExitCode "forge" ["build", "--root", root] ""   case res of     ExitFailure _ -> pure . Left $ "compilation failed: " <> "exit code: " <> show res <> "\n\nstdout:\n" <> out <> "\n\nstderr:\n" <> err     ExitSuccess -> readFilteredBuildOutput root (\path -> "unit-tests.t.sol" `Data.List.isInfixOf` path) Foundry
test/clitest.hs view
@@ -136,6 +136,34 @@         let hexStr = Types.bsToHex c         (_, stdout, _) <- readProcessWithExitCode "cabal" ["run", "exe:hevm", "--", "symbolic", "--solver", "empty", "--code", hexStr] ""         stdout `shouldContain` "SMT solver says: Result unknown by SMT solver"++      it "print-state-merge-debug" $ do+        Just c <- runApp $ solcRuntime (T.pack "C") (T.pack [i|+          contract C {+            function f(uint256 x) public pure {+              unchecked {+              uint256 result = 1;+              if (x & 0x1 != 0) result = result * 2;+              if (x & 0x2 != 0) result = result * 3;+              if (x & 0x4 != 0) result = result * 6;+              if (x & 0x8 != 0) result = result * 8;+              assert(result > 1);+              }+            }+          }+          |])+        let hexStr = Types.bsToHex c+        (exitCode, stdout, stderr) <- readProcessWithExitCode "cabal" ["run", "exe:hevm", "--", "symbolic", "--debug", "--solver", "bitwuzla", "--code", hexStr] ""+        (T.count "Merged forward jump at addr:" (T.pack stderr)) `shouldBe` 4+        stdout `shouldContain` "Discovered the following"+        exitCode `shouldBe` (ExitFailure 1)++      it "print-state-merge-debug-lines" $ do+        (exitCode, stdout, stderr) <- runForge "test/contracts/fail/merge-test.sol" ["--debug"]+        (T.count "Merged forward jump in file" (T.pack stderr)) `shouldBe` 4+        stdout `shouldContain` "Counterexample:"+        exitCode `shouldBe` (ExitFailure 1)+       it "crash-of-hevm" $ do         let hexStrA = "608060405234801561001057600080fd5b506004361061002b5760003560e01c8063efa2978514610030575b600080fd5b61004361003e3660046102ad565b610045565b005b60006100508561007a565b9050600061005d866100a8565b905080821461006e5761006e61034c565b50505050505050505050565b600061008761032e6103aa565b8261009457610197610098565b61013e5b6100a291906103e2565b92915050565b60006100b561032e6103aa565b6100be906103aa565b6100c7906103aa565b82806100d1575060005b806100da575060005b61013157605a6100ea60006103aa565b6100f3906103aa565b6100ff6001605a610404565b61010b6001605a610404565b61011891166101976103e2565b6101229190610493565b61012c9190610493565b610149565b604061013f8161013e6103e2565b6101499190610493565b61015391906103e2565b61016061032e60006103e2565b83801561016b575060015b15801590610177575060015b80156101a05750831515801561018b575060015b15158015610197575060015b806101a0575060005b610251576101976101b3605a602d610493565b602d60006101c2600182610493565b6101cd906001610404565b6101d8906001610404565b6101e1906103aa565b6101ea906103aa565b6101f491906103e2565b6101ff90605a610404565b604e61020c8160016103e2565b6102169190610493565b61022490600116605a610404565b1661022e906103aa565b61023891906103e2565b6102429190610493565b61024c9190610493565b610283565b604561025f8161013e6103e2565b6102699190610493565b60456102778161013e6103e2565b6102819190610493565b165b61028d91906103e2565b1692915050565b80358015155b81146100a257600080fd5b80358061029a565b600080600080600080600080610100898b0312156102ca57600080fd5b6102d48a8a610294565b97506102e38a60208b01610294565b96506102f28a60408b016102a5565b95506103018a60608b016102a5565b94506103108a60808b01610294565b935061031f8a60a08b016102a5565b925061032e8a60c08b01610294565b915061033d8a60e08b016102a5565b90509295985092959890939650565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007f800000000000000000000000000000000000000000000000000000000000000082036103db576103db61037b565b5060000390565b818103600083128015838313168383129190911617156100a2576100a261037b565b60008261043a577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f800000000000000000000000000000000000000000000000000000000000000082147fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8414161561048e5761048e61037b565b500590565b80820160008212801584831290811690159190911617156100a2576100a261037b56fea26469706673582212200a37769e5bf4b8b890caac8ab643126d55feb821a0201d2f674203f23fa666ad64736f6c634300081e0033" @@ -192,11 +220,16 @@            }           |])         let hexStr = Types.bsToHex c-        _ <- readProcessWithExitCode "cabal" ["run", "exe:hevm", "--", "symbolic", "--code", hexStr, "--smttimeout", "1", "--dump-unsolved", "."] ""+        _ <- readProcessWithExitCode "cabal" ["run", "exe:hevm", "--", "symbolic", "--code", hexStr, "--smt-timeout", "1", "--dump-unsolved", "."] ""         let filename = "query-unsolved-0.smt2"         fileExists <- doesFileExist filename         shouldBe fileExists True         removeFile filename+      it "early-abort" $ do+        (exitcode, stdout, stderr) <- runForge "test/contracts/pass/early-abort.sol" ["--max-iterations", "1000"]+        putStrLn $ "Exit code: " ++ show exitcode+        putStrLn stderr+        putStrLn stdout       it "rpc-cache" $ do         (_, stdout, stderr) <- runForge "test/contracts/fail/rpc-test.sol"           ["--rpc", "http://mock.mock", "--prefix", "test_attack_symbolic"
test/rpc.hs view
@@ -7,11 +7,14 @@  import Data.Maybe import Data.Map qualified as Map-import Data.Text (Text)+import Data.Text (Text, isPrefixOf)+import Data.Text qualified as T import Data.Vector qualified as V+import System.Environment (lookupEnv) -import EVM (makeVm, symbolify)+import EVM (makeVm, symbolify, defaultVMOpts) import EVM.ABI+import EVM.Dapp (TestMethodInfo(..)) import EVM.Fetch import EVM.SMT import EVM.Solvers@@ -41,7 +44,8 @@         conf <- readConfig         sess <- mkSessionWithoutCache         liftIO $ do-          (cb, numb, basefee, prevRan) <- fetchBlockWithSession conf sess block testRpc >>= \case+          rpc <- testRpc+          (cb, numb, basefee, prevRan) <- fetchBlockWithSession conf sess block rpc >>= \case                         Nothing -> internalError "Could not fetch block"                         Just Block{..} -> pure ( coinbase                                                      , number@@ -58,7 +62,8 @@         sess <- mkSessionWithoutCache         liftIO $ do           let block = BlockNumber 16184420-          (cb, numb, basefee, prevRan) <- fetchBlockWithSession conf sess block testRpc >>= \case+          rpc <- testRpc+          (cb, numb, basefee, prevRan) <- fetchBlockWithSession conf sess block rpc >>= \case                         Nothing -> internalError "Could not fetch block"                         Just Block{..} -> pure ( coinbase                                                      , number@@ -75,7 +80,8 @@     -- execute against remote state from a ds-test harness     [ test "dapp-test" $ do         let testFile = "test/contracts/pass/rpc.sol"-        res <- runForgeTestCustom testFile ".*" Nothing Nothing False testRpcInfo+        rpcInfo <- liftIO testRpcInfo+        res <- runForgeTestCustom testFile (\(TestMethodInfo _ (Sig name _)) -> "prove" `isPrefixOf` name) Nothing Nothing False rpcInfo         liftIO $ assertEqual "test result" (True, True) res      -- concretely exec "transfer" on WETH9 using remote rpc@@ -86,8 +92,9 @@           calldata' = ConcreteBuf $ abiMethod "transfer(address,uint256)" (AbiTuple (V.fromList [AbiAddress (Addr 0xdead), AbiUInt 256 wad]))         sess <- mkSessionWithoutCache         vm <- weth9VM sess testBlockNumber (calldata', [])-        postVm <- withSolvers Z3 1 1 Nothing $ \solvers ->-          Stepper.interpret (oracle solvers (Just sess) testRpcInfo) vm Stepper.runFully+        rpcInfo <- liftIO testRpcInfo+        postVm <- withSolvers Z3 1 Nothing defMemLimit $ \solvers ->+          Stepper.interpret (oracle solvers (Just sess) rpcInfo) vm Stepper.runFully         let           wethStore = (fromJust $ Map.lookup (LitAddr 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) postVm.env.contracts).storage           wethStore' = case wethStore of@@ -104,14 +111,16 @@     -- symbolically exec "transfer" on WETH9 using remote rpc     -- https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code     , test "weth-sym" $ do-        calldata' <- symCalldata "transfer(address,uint256)" [AbiAddressType, AbiUIntType 256] ["0xdead"] (AbstractBuf "txdata")+        (cdBuf, cdProps, _) <- symCalldata "transfer(address,uint256)" [AbiAddressType, AbiUIntType 256] ["0xdead"] (AbstractBuf "txdata")         let+          calldata' = (cdBuf, cdProps)           postc _ (Failure _ _ (Revert _)) = PBool False           postc _ _ = PBool True         sess <- mkSession Nothing Nothing         vm <- weth9VM sess testBlockNumber calldata'-        (_, [Cex (_, model)]) <- withSolvers Z3 1 1 Nothing $ \s ->-          verify s (oracle s (Just sess) testRpcInfo) (defaultVeriOpts {rpcInfo = testRpcInfo}) (symbolify vm) postc Nothing+        rpcInfo <- liftIO testRpcInfo+        (_, [Cex (_, model)]) <- withSolvers Z3 1 Nothing defMemLimit $ \s ->+          verify s (oracle s (Just sess) rpcInfo) (defaultVeriOpts {rpcInfo = rpcInfo}) (symbolify vm) postc Nothing         liftIO $ assertBool "model should exceed caller balance" (getVar model "arg2" >= 695836005599316055372648)     ]   ]@@ -128,50 +137,46 @@ vmFromRpc :: App m => Session -> BlockNumber -> (Expr Buf, [Prop]) -> Expr EWord -> Expr EAddr -> Addr -> m (VM Concrete) vmFromRpc sess blockNum calldata callvalue caller address = do   conf <- readConfig-  ctrct <- liftIO $ fetchContractWithSession conf sess blockNum testRpc address >>= \case+  rpc <- liftIO testRpc+  ctrct <- liftIO $ fetchContractWithSession conf sess blockNum rpc address >>= \case         FetchFailure _ -> internalError $ "contract not found: " <> show address         FetchError e -> internalError $ "rpc error: " <> show e         FetchSuccess contract' _ -> pure contract'    liftIO $ addFetchCache sess address ctrct-  blk <- liftIO $ fetchBlockWithSession conf sess blockNum testRpc >>= \case+  blk <- liftIO $ fetchBlockWithSession conf sess blockNum rpc >>= \case     Nothing -> internalError "could not fetch block"     Just b -> pure b -  liftIO $ stToIO (makeVm $ VMOpts+  liftIO $ stToIO (makeVm $ defaultVMOpts     { contract       = makeContractFromRPC ctrct-    , otherContracts = []     , calldata       = calldata     , value          = callvalue     , address        = LitAddr address     , caller         = caller-    , origin         = LitAddr 0xacab-    , gas            = 0xffffffffffffffff-    , gaslimit       = 0xffffffffffffffff     , baseFee        = blk.baseFee-    , priorityFee    = 0     , coinbase       = blk.coinbase     , number         = blk.number     , timestamp      = blk.timestamp     , blockGaslimit  = blk.gaslimit-    , gasprice       = 0     , maxCodeSize    = blk.maxCodeSize     , prevRandao     = blk.prevRandao     , schedule       = blk.schedule-    , chainId        = 1-    , create         = False-    , baseState      = EmptyBase-    , txAccessList   = mempty-    , allowFFI       = False-    , freshAddresses = 0-    , beaconRoot     = 0     }) -testRpc :: Text-testRpc = "https://eth-mainnet.alchemyapi.io/v2/vpeKFsEF6PHifHzdtcwXSDbhV3ym5Ro4"+testRpc :: IO Text+testRpc = do+  mKey <- lookupEnv "RPC_MAINNET_KEY"+  case mKey of+    Just key | not (null key) ->+      pure $ "https://eth-mainnet.g.alchemy.com/v2/" <> T.pack key+    _ ->+      error "RPC_MAINNET_KEY environment variable is not set; required to run rpc tests"  testBlockNumber :: BlockNumber testBlockNumber = BlockNumber 16198552 -testRpcInfo :: RpcInfo-testRpcInfo = RpcInfo $ Just (testBlockNumber, testRpc)+testRpcInfo :: IO RpcInfo+testRpcInfo = do+  rpc <- testRpc+  pure $ RpcInfo $ Just (testBlockNumber, rpc)
test/test.hs view
@@ -8,6485 +8,3820 @@ import Prelude hiding (LT, GT)  import GHC.TypeLits-import Data.Proxy-import Control.Monad-import Control.Monad.ST (stToIO)-import Control.Monad.State.Strict-import Control.Monad.IO.Unlift-import Control.Monad.Reader (ReaderT)-import Data.Bits hiding (And, Xor)-import Data.ByteString (ByteString)-import Data.ByteString qualified as BS-import Data.ByteString.Base16 qualified as BS16-import Data.Binary.Put (runPut)-import Data.Binary.Get (runGetOrFail)-import Data.DoubleWord-import Data.Either-import Data.List qualified as List-import Data.Map.Strict qualified as Map-import Data.Maybe-import Data.Monoid (Any(..))-import Data.Set qualified as Set-import Data.String.Here-import Data.Text (Text)-import Data.Text qualified as T-import Data.Time (diffUTCTime, getCurrentTime)-import Data.Tuple.Extra-import Data.Tree (flatten)-import Data.Typeable-import Data.Vector qualified as V-import Data.Word (Word8, Word64)-import Data.Char (digitToInt)-import GHC.Conc (getNumProcessors)-import System.Directory-import System.Environment-import Test.Tasty-import Test.Tasty.QuickCheck hiding (Failure, Success)-import Test.QuickCheck.Instances.Text()-import Test.QuickCheck.Instances.Natural()-import Test.QuickCheck.Instances.ByteString()-import Test.Tasty.HUnit-import Test.Tasty.Runners hiding (Failure, Success)-import Test.Tasty.ExpectedFailure-import Text.RE.TDFA.String-import Text.RE.Replace-import Witch (unsafeInto, into)-import Data.Containers.ListUtils (nubOrd)--import Optics.Core hiding (pre, re, elements)-import Optics.State--import EVM-import EVM.ABI-import EVM.Assembler-import EVM.Exec-import EVM.Expr qualified as Expr-import EVM.Fetch qualified as Fetch-import EVM.Format (hexByteString, hexText, formatExpr)-import EVM.Precompiled-import EVM.RLP-import EVM.SMT hiding (one)-import EVM.Solidity-import EVM.Solvers-import EVM.Stepper qualified as Stepper-import EVM.SymExec-import EVM.Test.FuzzSymExec qualified as FuzzSymExec-import EVM.Test.Utils (runForgeTest, runForgeTestCustom)-import EVM.Traversals-import EVM.Types hiding (Env)-import EVM.Effects-import EVM.UnitTest (writeTrace, printWarnings)-import EVM.Expr (maybeLitByteSimp)-import EVM.Keccak (concreteKeccaks)--testEnv :: Env-testEnv = Env { config = defaultConfig {-  dumpQueries = False-  , dumpExprs = False-  , dumpEndStates = False-  , debug = False-  , dumpTrace = False-  , decomposeStorage = True-  , verb = 1-  } }--putStrLnM :: (MonadUnliftIO m) => String -> m ()-putStrLnM a = liftIO $ putStrLn a--assertEqualM :: (App m, Eq a, Show a, HasCallStack) => String -> a -> a -> m ()-assertEqualM a b c = liftIO $ assertEqual a b c--assertBoolM-  :: (MonadUnliftIO m, HasCallStack)-  => String -> Bool -> m ()-assertBoolM a b = liftIO $ assertBool a b--test :: TestName -> ReaderT Env IO () -> TestTree-test a b = testCase a $ runEnv testEnv b--testNoSimplify :: TestName -> ReaderT Env IO () -> TestTree-testNoSimplify a b = let testEnvNoSimp = Env { config = testEnv.config { simp = False } }-  in testCase a $ runEnv testEnvNoSimp b--prop :: Testable prop => ReaderT Env IO prop -> Property-prop a = ioProperty $ runEnv testEnv a--propNoSimp :: Testable prop => ReaderT Env IO prop -> Property-propNoSimp a = let testEnvNoSimp = Env { config = testEnv.config { simp = False } }-  in ioProperty $ runEnv testEnvNoSimp a--withDefaultSolver :: App m => (SolverGroup -> m a) -> m a-withDefaultSolver = withSolvers Z3 3 1 Nothing--withCVC5Solver :: App m => (SolverGroup -> m a) -> m a-withCVC5Solver = withSolvers CVC5 3 1 Nothing--withBitwuzlaSolver :: App m => (SolverGroup -> m a) -> m a-withBitwuzlaSolver = withSolvers Bitwuzla 3 1 Nothing--main :: IO ()-main = defaultMain tests---- | run a subset of tests in the repl. p is a tasty pattern:--- https://github.com/UnkindPartition/tasty/tree/ee6fe7136fbcc6312da51d7f1b396e1a2d16b98a#patterns-runSubSet :: String -> IO ()-runSubSet p = defaultMain . applyPattern p $ tests--tests :: TestTree-tests = testGroup "hevm"-  [ FuzzSymExec.tests-  , testGroup "simplify-storage"-    [ test "simplify-storage-array-only-static" $ do-       Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          uint[] a;-          function transfer(uint acct, uint val1, uint val2) public {-            unchecked {-              a[0] = val1 + 1;-              a[1] = val2 + 2;-              assert(a[0]+a[1] == val1 + val2 + 3);-            }-          }-        }-        |]-       paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-       assertEqualM "Expression is not clean." (badStoresInExpr paths) False-    -- This case is somewhat artificial. We can't simplify this using only-    -- static rewrite rules, because acct is totally abstract and acct + 1-    -- could overflow back to zero. we may be able to do better if we have some-    -- smt assisted simplification that can take branch conditions into account.-    , expectFail $ test "simplify-storage-array-symbolic-index" $ do-       Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          uint b;-          uint[] a;-          function transfer(uint acct, uint val1) public {-            unchecked {-              a[acct] = val1;-              assert(a[acct] == val1);-            }-          }-        }-        |]-       paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-       -- T.writeFile "symbolic-index.expr" $ formatExpr paths-       assertEqualM "Expression is not clean." (badStoresInExpr paths) False-    , expectFail $ test "simplify-storage-array-of-struct-symbolic-index" $ do-       Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          struct MyStruct {-            uint a;-            uint b;-          }-          MyStruct[] arr;-          function transfer(uint acct, uint val1, uint val2) public {-            unchecked {-              arr[acct].a = val1+1;-              arr[acct].b = val1+2;-              assert(arr[acct].a + arr[acct].b == val1+val2+3);-            }-          }-        }-        |]-       paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-       assertEqualM "Expression is not clean." (badStoresInExpr paths) False-    , test "simplify-storage-array-loop-nonstruct" $ do-       Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          uint[] a;-          function transfer(uint v) public {-            for (uint i = 0; i < a.length; i++) {-              a[i] = v;-              assert(a[i] == v);-            }-          }-        }-        |]-       let veriOpts = (defaultVeriOpts :: VeriOpts) { iterConf = defaultIterConf { maxIter = Just 5 }}-       paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "transfer(uint256)" [AbiUIntType 256])) [] veriOpts-       assertEqualM "Expression is not clean." (badStoresInExpr paths) False-    , test "simplify-storage-map-newtest1" $ do-       Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          mapping (uint => uint) a;-          mapping (uint => uint) b;-          function fun(uint v, uint i) public {-            require(i < 1000);-            require(v < 1000);-            b[i+v] = v+1;-            a[i] = v;-            b[i+1] = v+1;-            assert(a[i] == v);-            assert(b[i+1] == v+1);-          }-        }-        |]-       paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-       assertEqualM "Expression is not clean." (badStoresInExpr paths) False-       (_, []) <- withDefaultSolver $ \s -> checkAssert s [0x11] c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-       liftIO $ putStrLn "OK"-    , ignoreTest $ test "simplify-storage-map-todo" $ do-       Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          mapping (uint => uint) a;-          mapping (uint => uint) b;-          function fun(uint v, uint i) public {-            require(i < 1000);-            require(v < 1000);-            a[i] = v;-            b[i+1] = v+1;-            b[i+v] = 55; // note: this can overwrite b[i+1], hence assert below can fail-            assert(a[i] == v);-            assert(b[i+1] == v+1);-          }-        }-        |]-       -- TODO: expression below contains (load idx1 (store idx1 (store idx1 (store idx0)))), and the idx0-       --       is not stripped. This is due to us not doing all we can in this case, see-       --       note above readStorage. Decompose remedies this (when it can be decomposed)-       -- paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-       -- putStrLnM $ T.unpack $ formatExpr paths-       (_, [Cex _]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-       liftIO $ putStrLn "OK"-    , test "simplify-storage-array-loop-struct" $ do-       Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          struct MyStruct {-            uint a;-            uint b;-          }-          MyStruct[] arr;-          function transfer(uint v1, uint v2) public {-            for (uint i = 0; i < arr.length; i++) {-              arr[i].a = v1+1;-              arr[i].b = v2+2;-              assert(arr[i].a + arr[i].b == v1 + v2 + 3);-            }-          }-        }-        |]-       let veriOpts = (defaultVeriOpts :: VeriOpts) { iterConf = defaultIterConf { maxIter = Just 5 }}-       paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] veriOpts-       assertEqualM "Expression is not clean." (badStoresInExpr paths) False-    , test "decompose-1" $ do-      Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          mapping (address => uint) balances;-          function prove_mapping_access(address x, address y) public {-              require(x != y);-              balances[x] = 1;-              balances[y] = 2;-              assert(balances[x] != balances[y]);-          }-        }-        |]-      paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "prove_mapping_access(address,address)" [AbiAddressType, AbiAddressType])) [] defaultVeriOpts-      let simpExpr = map (mapExprM Expr.decomposeStorage) paths-      assertEqualM "Decompose did not succeed." (all isJust simpExpr) True-    , test "decompose-2" $ do-      Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          mapping (address => uint) balances;-          function prove_mixed_symoblic_concrete_writes(address x, uint v) public {-              balances[x] = v;-              balances[address(0)] = balances[x];-              assert(balances[address(0)] == v);-          }-        }-        |]-      paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "prove_mixed_symoblic_concrete_writes(address,uint256)" [AbiAddressType, AbiUIntType 256])) [] defaultVeriOpts-      let pathsSimp = map (mapExprM (Expr.decomposeStorage . Expr.concKeccakSimpExpr . Expr.simplify)) paths-      -- putStrLnM $ T.unpack $ formatExpr (fromJust pathsSimp)-      assertEqualM "Decompose did not succeed." (all isJust pathsSimp) True-    , test "decompose-3" $ do-      Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          uint[] a;-          function prove_array(uint x, uint v1, uint y, uint v2) public {-              require(v1 != v2);-              a[x] = v1;-              a[y] = v2;-              assert(a[x] == a[y]);-          }-        }-        |]-      paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "prove_array(uint256,uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-      let simpExpr = map (mapExprM Expr.decomposeStorage) paths-      assertEqualM "Decompose did not succeed." (all isJust simpExpr) True-    , test "decompose-4-mixed" $ do-      Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          uint[] a;-          mapping( uint => uint) balances;-          function prove_array(uint x, uint v1, uint y, uint v2) public {-              require(v1 != v2);-              balances[x] = v1+1;-              balances[y] = v1+2;-              a[x] = v1;-              assert(balances[x] != balances[y]);-          }-        }-        |]-      paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "prove_array(uint256,uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-      let simpExpr = map (mapExprM Expr.decomposeStorage) paths-      -- putStrLnM $ T.unpack $ formatExpr (fromJust simpExpr)-      assertEqualM "Decompose did not succeed." (all isJust simpExpr) True-    , test "decompose-5-mixed" $ do-      Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          mapping (address => uint) balances;-          mapping (uint => bool) auth;-          uint[] arr;-          uint a;-          uint b;-          function prove_mixed(address x, address y, uint val) public {-            b = val+1;-            require(x != y);-            balances[x] = val;-            a = val;-            arr[val] = 5;-            auth[val+1] = true;-            balances[y] = val+2;-            if (balances[y] == balances[y]) {-                assert(balances[y] == val);-            }-          }-        }-        |]-      paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "prove_mixed(address,address,uint256)" [AbiAddressType, AbiAddressType, AbiUIntType 256])) [] defaultVeriOpts-      let simpExpr = map (mapExprM Expr.decomposeStorage) paths-      -- putStrLnM $ T.unpack $ formatExpr (fromJust simpExpr)-      assertEqualM "Decompose did not succeed." (all isJust simpExpr) True-    , test "decompose-6" $ do-      Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          uint[] arr;-          function prove_mixed(uint val) public {-            arr[val] = 5;-            arr[val+1] = val+5;-            assert(arr[val] == arr[val+1]);-          }-        }-        |]-      paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "prove_mixed(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-      let simpExpr = map (mapExprM Expr.decomposeStorage) paths-      -- putStrLnM $ T.unpack $ formatExpr (fromJust simpExpr)-      assertEqualM "Decompose did not succeed." (all isJust simpExpr) True-    -- This test uses array.length, which is is concrete 0 only in case we start with an empty storage-    -- otherwise (i.e. with getExpr) it's symbolic, and the exploration loops forever-    , test "decompose-7-emtpy-storage" $ do-       Just c <- solcRuntime "MyContract" [i|-        contract MyContract {-          uint[] arr;-          function nested_append(uint v, uint w) public {-            arr.push(w);-            arr.push();-            arr.push();-            arr.push(arr[0]-1);--            arr[2] = v;-            arr[1] = arr[0]-arr[2];--            assert(arr.length == 4);-            assert(arr[0] == w);-            assert(arr[1] == w-v);-            assert(arr[2] == v);-            assert(arr[3] == w-1);-          }-       } |]-       let sig = Just $ Sig "nested_append(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256]-       paths <- withDefaultSolver $ \s -> getExprEmptyStore s c sig [] defaultVeriOpts-       assertEqualM "Expression must be clean." (badStoresInExpr paths) False-    , test "simplify-storage-map-only-static" $ do-       Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          mapping(uint => uint) items1;-          function transfer(uint acct, uint val1, uint val2) public {-            unchecked {-              items1[0] = val1+1;-              items1[1] = val2+2;-              assert(items1[0]+items1[1] == val1 + val2 + 3);-            }-          }-        }-        |]-       let sig = (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256]))-       paths <- withDefaultSolver $ \s -> getExpr s c sig [] defaultVeriOpts-       let pathsSimp = map (mapExpr (Expr.concKeccakSimpExpr . Expr.simplify)) paths-       assertEqualM "Expression is not clean." (badStoresInExpr pathsSimp) False-    , test "simplify-storage-map-only-2" $ do-       Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          mapping(uint => uint) items1;-          function transfer(uint acct, uint val1, uint val2) public {-            unchecked {-              items1[acct] = val1+1;-              items1[acct+1] = val2+2;-              assert(items1[acct]+items1[acct+1] == val1 + val2 + 3);-            }-          }-        }-        |]-       paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-       -- putStrLnM $ T.unpack $ formatExpr paths-       assertEqualM "Expression is not clean." (badStoresInExpr paths) False-    , test "simplify-storage-map-with-struct" $ do-       Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          struct MyStruct {-            uint a;-            uint b;-          }-          mapping(uint => MyStruct) items1;-          function transfer(uint acct, uint val1, uint val2) public {-            unchecked {-              items1[acct].a = val1+1;-              items1[acct].b = val2+2;-              assert(items1[acct].a+items1[acct].b == val1 + val2 + 3);-            }-          }-        }-        |]-       paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-       assertEqualM "Expression is not clean." (badStoresInExpr paths) False-    , test "simplify-storage-map-and-array" $ do-       Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          uint[] a;-          mapping(uint => uint) items1;-          mapping(uint => uint) items2;-          function transfer(uint acct, uint val1, uint val2) public {-            uint beforeVal1 = items1[acct];-            uint beforeVal2 = items2[acct];-            unchecked {-              items1[acct] = val1+1;-              items2[acct] = val2+2;-              a[0] = val1 + val2 + 1;-              a[1] = val1 + val2 + 2;-              assert(items1[acct]+items2[acct]+a[0]+a[1] > beforeVal1 + beforeVal2);-            }-          }-        }-       |]-       paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-       -- putStrLnM $ T.unpack $ formatExpr paths-       assertEqualM "Expression is not clean." (badStoresInExpr paths) False-    , test "simplify-storage-wordToAddr" $ do-       let a = "000000000000000000000000d95322745865822719164b1fc167930754c248de000000000000000000000000000000000000000000000000000000000000004a"-           store = ConcreteStore (Map.fromList[(W256 0xebd33f63ba5dda53a45af725baed5628cdad261db5319da5f5d921521fe1161d,W256 0x5842cf)])-           expr = SLoad (Keccak (ConcreteBuf (hexStringToByteString a))) store-           simpExpr = Expr.wordToAddr expr-           simpExpr2 = Expr.concKeccakSimpExpr expr-       assertEqualM "Expression should simplify to value." simpExpr (Just $ LitAddr 0x5842cf)-       assertEqualM "Expression should simplify to value." simpExpr2 (Lit 0x5842cf)-    , test "simplify-storage-wordToAddr-complex" $ do-       let a = "000000000000000000000000d95322745865822719164b1fc167930754c248de000000000000000000000000000000000000000000000000000000000000004a"-           store = ConcreteStore (Map.fromList[(W256 0xebd33f63ba5dda53a45af725baed5628cdad261db5319da5f5d921521fe1161d,W256 0x5842cf)])-           expr = SLoad (Keccak (ConcreteBuf (hexStringToByteString a))) store-           writeWChain = WriteWord (Lit 0x32) (Lit 0x72) (WriteWord (Lit 0x0) expr (ConcreteBuf ""))-           kecc = Keccak (CopySlice (Lit 0x0) (Lit 0x0) (Lit 0x20) (WriteWord (Lit 0x0) expr (writeWChain)) (ConcreteBuf ""))-           keccAnd =  (And (Lit 1461501637330902918203684832716283019655932542975) kecc)-           outer = And (Lit 1461501637330902918203684832716283019655932542975) (SLoad (keccAnd) (ConcreteStore (Map.fromList[(W256 1184450375068808042203882151692185743185288360635, W256 0xacab)])))-           simp = Expr.concKeccakSimpExpr outer-       assertEqualM "Expression should simplify to value." simp (Lit 0xacab)-    ]-  , testGroup "StorageTests"-    [ test "read-from-sstore" $ assertEqualM ""-        (Lit 0xab)-        (Expr.readStorage' (Lit 0x0) (SStore (Lit 0x0) (Lit 0xab) (AbstractStore (LitAddr 0x0) Nothing)))-    , test "read-from-concrete" $ assertEqualM ""-        (Lit 0xab)-        (Expr.readStorage' (Lit 0x0) (ConcreteStore $ Map.fromList [(0x0, 0xab)]))-    , test "read-past-write" $ assertEqualM ""-        (Lit 0xab)-        (Expr.readStorage' (Lit 0x0) (SStore (Lit 0x1) (Var "b") (ConcreteStore $ Map.fromList [(0x0, 0xab)])))-    , test "accessStorage uses fetchedStorage" $ do-        let dummyContract =-              (initialContract (RuntimeCode (ConcreteRuntimeCode mempty)))-                { external = True }-        vm :: VM Concrete <- liftIO $ stToIO $ vmForEthrunCreation ""-        -- perform the initial access-        let ?conf = testEnv.config-        vm1 <- liftIO $ stToIO $ execStateT (EVM.accessStorage (LitAddr 0) (Lit 0) (pure . pure ())) vm-        -- it should fetch the contract first-        vm2 <- case vm1.result of-                Just (HandleEffect (Query (PleaseFetchContract _addr _ continue))) ->-                  liftIO $ stToIO $ execStateT (continue dummyContract) vm1-                _ -> internalError "unexpected result"-            -- then it should fetch the slow-        vm3 <- case vm2.result of-                    Just (HandleEffect (Query (PleaseFetchSlot _addr _slot continue))) ->-                      liftIO $ stToIO $ execStateT (continue 1337) vm2-                    _ -> internalError "unexpected result"-            -- perform the same access as for vm1-        vm4 <- liftIO $ stToIO $ execStateT (EVM.accessStorage (LitAddr 0) (Lit 0) (pure . pure ())) vm3--        -- there won't be query now as accessStorage uses fetch cache-        assertBoolM (show vm4.result) (isNothing vm4.result)-    ]-  , testGroup "SimplifierUnitTests"-    -- common overflow cases that the simplifier was getting wrong-    [ test "writeWord-overflow" $ do-        let e = ReadByte (Lit 0x0) (WriteWord (Lit 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd) (Lit 0x0) (ConcreteBuf "\255\255\255\255"))-        b <- checkEquiv e (Expr.simplify e)-        assertBoolM "Simplifier failed" b-    , test "copyslice-simps" $ do-        let e a b =  CopySlice (Lit 0) (Lit 0) (BufLength (AbstractBuf "buff")) (CopySlice (Lit 0) (Lit 0) (BufLength (AbstractBuf "buff")) (AbstractBuf "buff") (ConcreteBuf a)) (ConcreteBuf b)-            expr1 = e "" ""-            expr2 = e "" "aasdfasdf"-            expr3 = e "9832478932" ""-            expr4 = e "9832478932" "aasdfasdf"-        assertEqualM "Not full simp" (Expr.simplify expr1) (AbstractBuf "buff")-        assertEqualM "Not full simp" (Expr.simplify expr2) $ CopySlice (Lit 0x0) (Lit 0x0) (BufLength (AbstractBuf "buff")) (AbstractBuf "buff") (ConcreteBuf "aasdfasdf")-        assertEqualM "Not full simp" (Expr.simplify expr3) (AbstractBuf "buff")-        assertEqualM "Not full simp" (Expr.simplify expr4) $ CopySlice (Lit 0x0) (Lit 0x0) (BufLength (AbstractBuf "buff")) (AbstractBuf "buff") (ConcreteBuf "aasdfasdf")-    , test "buffer-length-copy-slice-beyond-source1" $ do-        let e = BufLength (CopySlice (Lit 0x2) (Lit 0x2) (Lit 0x1) (ConcreteBuf "a") (ConcreteBuf ""))-        b <- checkEquiv e (Expr.simplify e)-        assertBoolM "Simplifier failed" b-    , test "buffer-length-copy-slice-beyond-source2" $ do-        let e = BufLength (CopySlice (Lit 0x2) (Lit 0x2) (Lit 0x1) (ConcreteBuf "") (ConcreteBuf ""))-        b <- checkEquiv e (Expr.simplify e)-        assertBoolM "Simplifier failed" b-    , test "simp-readByte1" $ do-      let srcOffset = (ReadWord (Lit 0x1) (AbstractBuf "stuff1"))-          size = (ReadWord (Lit 0x1) (AbstractBuf "stuff2"))-          src = (AbstractBuf "stuff2")-          e = ReadByte (Lit 0x0) (CopySlice srcOffset (Lit 0x10) size src (AbstractBuf "dst"))-          simp = Expr.simplify e-      assertEqualM "readByte simplification" simp (ReadByte (Lit 0x0) (AbstractBuf "dst"))-    , test "simp-readByte2" $ do-      let srcOffset = (ReadWord (Lit 0x1) (AbstractBuf "stuff1"))-          size = (Lit 0x1)-          src = (AbstractBuf "stuff2")-          e = ReadByte (Lit 0x0) (CopySlice srcOffset (Lit 0x10) size src (AbstractBuf "dst"))-          simp = Expr.simplify e-      res <- checkEquiv e simp-      assertEqualM "readByte simplification"  res True-    , test "simp-readWord1" $ do-      let srcOffset = (ReadWord (Lit 0x1) (AbstractBuf "stuff1"))-          size = (ReadWord (Lit 0x1) (AbstractBuf "stuff2"))-          src = (AbstractBuf "stuff2")-          e = ReadWord (Lit 0x1) (CopySlice srcOffset (Lit 0x40) size src (AbstractBuf "dst"))-          simp = Expr.simplify e-      assertEqualM "readWord simplification" simp (ReadWord (Lit 0x1) (AbstractBuf "dst"))-    , test "simp-readWord2" $ do-      let srcOffset = (ReadWord (Lit 0x12) (AbstractBuf "stuff1"))-          size = (Lit 0x1)-          src = (AbstractBuf "stuff2")-          e = ReadWord (Lit 0x12) (CopySlice srcOffset (Lit 0x50) size src (AbstractBuf "dst"))-          simp = Expr.simplify e-      res <- checkEquiv e simp-      assertEqualM "readWord simplification"  res True-    , test "simp-max-buflength" $ do-      let simp = Expr.simplify $ Max (Lit 0) (BufLength (AbstractBuf "txdata"))-      assertEqualM "max-buflength rules" simp $ BufLength (AbstractBuf "txdata")-    , test "simp-PLT-max" $ do-      let simp = Expr.simplifyProp $ PLT (Max (Lit 5) (BufLength (AbstractBuf "txdata"))) (Lit 99)-      assertEqualM "max-buflength rules" simp $ PLT (BufLength (AbstractBuf "txdata")) (Lit 99)-    , test "simp-assoc-add1" $ do-      let simp = Expr.simplify $        Add (Add (Var "c") (Var "a")) (Var "b")-      assertEqualM "assoc rules" simp $ Add (Var "a") (Add (Var "b") (Var "c"))-    , test "simp-assoc-add2" $ do-      let simp = Expr.simplify $        Add (Add (Lit 1) (Var "c")) (Var "b")-      assertEqualM "assoc rules" simp $ Add (Lit 1) (Add (Var "b") (Var "c"))-    , test "simp-assoc-add3" $ do-      let simp = Expr.simplify $        Add (Lit 1) (Add (Lit 2) (Var "c"))-      assertEqualM "assoc rules" simp $ Add (Lit 3) (Var "c")-    , test "simp-assoc-add4" $ do-      let simp = Expr.simplify $        Add (Lit 1) (Add (Var "b") (Lit 2))-      assertEqualM "assoc rules" simp $ Add (Lit 3) (Var "b")-    , test "simp-assoc-add5" $ do-      let simp = Expr.simplify $        Add (Var "a") (Add (Lit 1) (Lit 2))-      assertEqualM "assoc rules" simp $ Add (Lit 3) (Var "a")-    , test "simp-assoc-add6" $ do-      let simp = Expr.simplify $        Add (Lit 7) (Add (Lit 1) (Lit 2))-      assertEqualM "assoc rules" simp $ Lit 10-    , test "simp-assoc-add-7" $ do-      let simp = Expr.simplify $        Add (Var "a") (Add (Var "b") (Lit 2))-      assertEqualM "assoc rules" simp $ Add (Lit 2) (Add (Var "a") (Var "b"))-    , test "simp-assoc-add8" $ do-      let simp = Expr.simplify $        Add (Add (Var "a") (Add (Lit 0x2) (Var "b"))) (Add (Var "c") (Add (Lit 0x2) (Var "d")))-      assertEqualM "assoc rules" simp $ Add (Lit 4) (Add (Var "a") (Add (Var "b") (Add (Var "c") (Var "d"))))-    , test "simp-assoc-mul1" $ do-      let simp = Expr.simplify $        Mul (Mul (Var "b") (Var "a")) (Var "c")-      assertEqualM "assoc rules" simp $ Mul (Var "a") (Mul (Var "b") (Var "c"))-    , test "simp-assoc-mul2" $ do-      let simp = Expr.simplify       $  Mul (Lit 2) (Mul (Var "a") (Lit 3))-      assertEqualM "assoc rules" simp $ Mul (Lit 6) (Var "a")-    , test "simp-assoc-xor1" $ do-      let simp = Expr.simplify       $  Xor (Lit 2) (Xor (Var "a") (Lit 3))-      assertEqualM "assoc rules" simp $ Xor (Lit 1) (Var "a")-    , test "simp-assoc-xor2" $ do-      let simp = Expr.simplify       $  Xor (Lit 2) (Xor (Var "b") (Xor (Var "a") (Lit 3)))-      assertEqualM "assoc rules" simp $ Xor (Lit 1) (Xor (Var "a") (Var "b"))-    , test "simp-zero-write-extend-buffer-len" $ do-        let-          expr = BufLength $ CopySlice (Lit 0) (Lit 0x10) (Lit 0) (AbstractBuf "buffer") (ConcreteBuf "bimm")-          simp = Expr.simplify expr-        ret <-  checkEquiv expr simp-        assertEqualM "Must be equivalent" True ret-    , test "bufLength-simp" $ do-      let-        a = BufLength (ConcreteBuf "ab")-        simp = Expr.simplify a-      assertEqualM "Must be simplified down to a Lit" simp (Lit 2)-    , test "stripWrites-overflow" $ do-        -- below eventually boils down to-        -- unsafeInto (0xf0000000000000000000000000000000000000000000000000000000000000+1) :: Int-        -- which failed before-        let-          a = ReadByte (Lit 0xf0000000000000000000000000000000000000000000000000000000000000) (WriteByte (And (SHA256 (ConcreteBuf "")) (Lit 0x1)) (LitByte 0) (ConcreteBuf ""))-          b = Expr.simplify a-        ret <- checkEquiv a b-        assertBoolM "must be equivalent" ret-    , test "read-beyond-bound (negative-test)" $ do-      let-        e1 = CopySlice (Lit 1) (Lit 0) (Lit 2) (ConcreteBuf "a") (ConcreteBuf "")-        e2 = ConcreteBuf "Definitely not the same!"-      equal <- checkEquiv e1 e2-      assertBoolM "Should not be equivalent!" $ not equal-    , testNoSimplify "simplify-comparison-GEq" $ do-      let-        expr = PEq (Lit 0x1) (GEq (Var "v") (Lit 0x2))-        simp = Expr.simplifyProp expr-      ret <- checkEquivPropAndLHS expr simp-      assertEqualM "Must be equivalent" True ret-    , test "buffer-length-zero" $ do-        let-          p = PEq (BufLength (AbstractBuf "b")) (Lit 0x0)-          simp = Expr.simplifyProp p-        liftIO $ print simp-        ret <-  checkEquivProp p simp-        assertEqualM "Must be equivalent" True ret-    ]-  -- These tests fuzz the simplifier by generating a random expression,-  -- applying some simplification rules, and then using the smt encoding to-  -- check that the simplified version is semantically equivalent to the-  -- unsimplified one-  , adjustOption (\(Test.Tasty.QuickCheck.QuickCheckTests n) -> Test.Tasty.QuickCheck.QuickCheckTests (div n 2)) $ testGroup "SimplifierTests"-    [ testProperty  "buffer-simplification" $ \(expr :: Expr Buf) -> propNoSimp $ do-        let simplified = Expr.simplify expr-        checkEquivAndLHS expr simplified-    , testProperty  "buffer-simplification-len" $ \(expr :: Expr Buf) -> propNoSimp $ do-        let simplified = Expr.simplify (BufLength expr)-        checkEquivAndLHS (BufLength expr) simplified-    , testProperty "store-simplification" $ \(expr :: Expr Storage) -> propNoSimp $ do-        let simplified = Expr.simplify expr-        checkEquivAndLHS expr simplified-    , testProperty "load-simplification" $ \(GenWriteStorageLoad expr) -> propNoSimp $ do-        let simplified = Expr.simplify expr-        checkEquivAndLHS expr simplified-    , ignoreTest $ testProperty "load-decompose" $ \(GenWriteStorageLoad expr) -> propNoSimp $ do-        putStrLnM $ T.unpack $ formatExpr expr-        let simp = Expr.simplify expr-        let decomposed = fromMaybe simp $ mapExprM Expr.decomposeStorage simp-        -- putStrLnM $ "-----------------------------------------"-        -- putStrLnM $ T.unpack $ formatExpr decomposed-        -- putStrLnM $ "\n\n\n\n"-        checkEquiv expr decomposed-    , testProperty "byte-simplification" $ \(expr :: Expr Byte) -> propNoSimp $ do-        let simplified = Expr.simplify expr-        checkEquivAndLHS expr simplified-    , askOption $ \(QuickCheckTests n) -> testProperty "word-simplification" $ withMaxSuccess (min n 20) $ \(ZeroDepthWord expr) -> propNoSimp $ do-        let simplified = Expr.simplify expr-        checkEquivAndLHS expr simplified-    , testProperty "readStorage-equivalance" $ \(store, slot) -> propNoSimp $ do-        let simplified = Expr.readStorage' slot store-            full = SLoad slot store-        checkEquiv full simplified-    , testProperty "writeStorage-equivalance" $ \(val, GenWriteStorageExpr (slot, store)) -> propNoSimp $ do-        let simplified = Expr.writeStorage slot val store-            full = SStore slot val store-        checkEquiv full simplified-    , testProperty "readWord-equivalance" $ \(buf, idx) -> propNoSimp $ do-        let simplified = Expr.readWord idx buf-            full = ReadWord idx buf-        checkEquiv full simplified-    , testProperty "writeWord-equivalance" $ \(idx, val, WriteWordBuf buf) -> propNoSimp $ do-        let simplified = Expr.writeWord idx val buf-            full = WriteWord idx val buf-        checkEquiv full simplified-    , testProperty "arith-simplification" $ \(_ :: Int) -> propNoSimp $ do-        expr <- liftIO $ generate . sized $ genWordArith 15-        let simplified = Expr.simplify expr-        checkEquivAndLHS expr simplified-    , testProperty "readByte-equivalance" $ \(buf, idx) -> propNoSimp $ do-        let simplified = Expr.readByte idx buf-            full = ReadByte idx buf-        checkEquiv full simplified-    -- we currently only simplify concrete writes over concrete buffers so that's what we test here-    , testProperty "writeByte-equivalance" $ \(LitOnly val, LitOnly buf, GenWriteByteIdx idx) -> propNoSimp $ do-        let simplified = Expr.writeByte idx val buf-            full = WriteByte idx val buf-        checkEquiv full simplified-    , testProperty "copySlice-equivalance" $ \(srcOff, GenCopySliceBuf src, GenCopySliceBuf dst, LitWord @300 size) -> propNoSimp $ do-        -- we bias buffers to be concrete more often than not-        dstOff <- liftIO $ generate (maybeBoundedLit 100_000)-        let simplified = Expr.copySlice srcOff dstOff size src dst-            full = CopySlice srcOff dstOff size src dst-        checkEquiv full simplified-    , testProperty "indexWord-equivalence" $ \(src, LitWord @50 idx) -> propNoSimp $ do-        let simplified = Expr.indexWord idx src-            full = IndexWord idx src-        checkEquiv full simplified-    , testProperty "pow-base2-simp" $ \(_ :: Int) -> propNoSimp $ do-        expo <- liftIO $ generate . sized $ genWordArith 15-        let full = Exp (Lit 2) expo-            simplified = Expr.simplify full-        checkEquiv full simplified-    , testProperty "pow-low-exponent-simp" $ \(LitWord @100 expo) -> propNoSimp $ do-        base <- liftIO $ generate . sized $ genWordArith 15-        let full = Exp base expo-            simplified = Expr.simplify full-        checkEquiv full simplified-    , testProperty "indexWord-mask-equivalence" $ \(src :: Expr EWord, LitWord @35 idx) -> propNoSimp $ do-        mask <- liftIO $ generate $ do-          pow <- arbitrary :: Gen Int-          frequency-           [ (1, pure $ Lit $ (shiftL 1 (pow `mod` 256)) - 1)        -- potentially non byte aligned-           , (1, pure $ Lit $ (shiftL 1 ((pow * 8) `mod` 256)) - 1)  -- byte aligned-           ]-        let-          input = And mask src-          simplified = Expr.indexWord idx input-          full = IndexWord idx input-        checkEquiv full simplified-    , testProperty "toList-equivalance" $ \buf -> propNoSimp $ do-        let-          -- transforms the input buffer to give it a known length-          fixLength :: Expr Buf -> Gen (Expr Buf)-          fixLength = mapExprM go-            where-              go :: Expr a -> Gen (Expr a)-              go = \case-                WriteWord _ val b -> liftM3 WriteWord idx (pure val) (pure b)-                WriteByte _ val b -> liftM3 WriteByte idx (pure val) (pure b)-                CopySlice so _ sz src dst -> liftM5 CopySlice (pure so) idx (pure sz) (pure src) (pure dst)-                AbstractBuf _ -> cbuf-                e -> pure e-              cbuf = do-                bs <- arbitrary-                pure $ ConcreteBuf bs-              idx = do-                w <- arbitrary-                -- we use 100_000 as an upper bound for indices to keep tests reasonably fast here-                pure $ Lit (w `mod` 100_000)--        input <- liftIO $ generate $ fixLength buf-        case Expr.toList input of-          Nothing -> do-            putStrLnM "skip"-            pure True -- ignore cases where the buf cannot be represented as a list-          Just asList -> do-            let asBuf = Expr.fromList asList-            checkEquiv asBuf input-    , testProperty "simplifyProp-equivalence-lit" $ \(LitProp p) -> propNoSimp $ do-        let simplified = Expr.simplifyProps [p]-        case simplified of-          [] -> checkEquivProp (PBool True) p-          [val@(PBool _)] -> checkEquivProp val p-          _ -> liftIO $ assertFailure "must evaluate down to a literal bool"-    , testProperty "simplifyProp-equivalence-sym" $ \(p) -> propNoSimp $ do-        let simplified = Expr.simplifyProp p-        checkEquivPropAndLHS p simplified-    , testProperty "simplify-joinbytes" $ \(SymbolicJoinBytes exprList) -> propNoSimp $ do-        let x = joinBytesFromList exprList-        let simplified = Expr.simplify x-        y <- checkEquiv x simplified-        assertBoolM "Must be equal" y-    , testProperty "simpProp-equivalence-sym-Prop" $ \(ps :: [Prop]) -> propNoSimp $ do-        let simplified = pand (Expr.simplifyProps ps)-        checkEquivPropAndLHS (pand ps) simplified-    , testProperty "simpProp-equivalence-sym-LitProp" $ \(LitProp p) -> propNoSimp $ do-        let simplified = pand (Expr.simplifyProps [p])-        checkEquivPropAndLHS p simplified-    , testProperty "storage-slot-simp-property" $ \(StorageExp s) -> propNoSimp $ do-        -- we have to run `Expr.litToKeccak` on the unsimplified system, or-        -- we'd need some form of minimal simplifier for things to work out. As long as-        -- we trust the litToKeccak, this is fine, as that function is stand-alone,-        -- and quite minimal-        let s2 = Expr.litToKeccak s-        let simplified = Expr.simplify s2-        checkEquivAndLHS s2 simplified-    , test "storage-slot-single" $ do-        -- this tests that "" and "0"x32 is not equivalent in Keccak-        let x = SLoad (Add (Keccak (ConcreteBuf "")) (Lit 1)) (SStore (Keccak (ConcreteBuf "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL")) (Lit 0) (AbstractStore (SymAddr "stuff") Nothing))-        let simplified = Expr.simplify x-        y <- checkEquivAndLHS x simplified-        assertBoolM "Must be equal" y-    , test "word-eq-bug" $ do-        -- This test is actually OK because the simplified takes into account that it's impossible to find a-        -- near-collision in the keccak hash-        let x =  (SLoad (Keccak (AbstractBuf "es")) (SStore (Add (Keccak (ConcreteBuf "")) (Lit 0x1)) (Lit 0xacab) (ConcreteStore (Map.empty))))-        let simplified = Expr.simplify x-        y <- checkEquiv x simplified-        assertBoolM "Must be equal, given keccak distance axiom" y-    ]-  {- NOTE: These tests were designed to test behaviour on reading from a buffer such that the indices overflow 2^256.-           However, such scenarios are impossible in the real world (the operation would run out of gas). The problem-           is that the behaviour of bytecode interpreters does not match the semantics of SMT. Intrepreters just-           return all zeroes for any read beyond buffer size, while in SMT reading multiple bytes may lead to overflow-           on indices and subsequently to reading from the beginning of the buffer (wrap-around semantics).-  , testGroup "concrete-buffer-simplification-large-index" [-      test "copy-slice-large-index-nooverflow" $ do-        let-          e = CopySlice (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (Lit 0x0) (Lit 0x1) (ConcreteBuf "a") (ConcreteBuf "")-          s = Expr.simplify e-        equal <- checkEquiv e s-        assertEqualM "Must be equal" True equal-    , test "copy-slice-overflow-back-into-source" $ do-        let-          e = CopySlice (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (Lit 0x0) (Lit 0x2) (ConcreteBuf "a") (ConcreteBuf "")-          s = Expr.simplify e-        equal <- checkEquiv e s-        assertEqualM "Must be equal" True equal-    , test "copy-slice-overflow-beyond-source" $ do-        let-          e = CopySlice (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (Lit 0x0) (Lit 0x3) (ConcreteBuf "a") (ConcreteBuf "")-          s = Expr.simplify e-        equal <- checkEquiv e s-        assertEqualM "Must be equal" True equal-    , test "copy-slice-overflow-beyond-source-into-nonempty" $ do-        let-          e = CopySlice (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (Lit 0x0) (Lit 0x3) (ConcreteBuf "a") (ConcreteBuf "b")-          s = Expr.simplify e-        equal <- checkEquiv e s-        assertEqualM "Must be equal" True equal-    , test "read-word-overflow-back-into-source" $ do-        let-          e = ReadWord (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (ConcreteBuf "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk")-          s = Expr.simplify e-        equal <- checkEquiv e s-        assertEqualM "Must be equal" True equal-  ]-  -}-  , testGroup "isUnsat-concrete-tests" [-      test "disjunction-left-false" $ do-        let-          t = [PEq (Var "x") (Lit 1), POr (PEq (Var "x") (Lit 0)) (PEq (Var "y") (Lit 1)), PEq (Var "y") (Lit 2)]-          propagated = Expr.constPropagate t-        assertEqualM "Must contain PBool False" True ((PBool False) `elem` propagated)-    , test "disjunction-right-false" $ do-        let-          t = [PEq (Var "x") (Lit 1), POr (PEq (Var "y") (Lit 1)) (PEq (Var "x") (Lit 0)), PEq (Var "y") (Lit 2)]-          propagated = Expr.constPropagate t-        assertEqualM "Must contain PBool False" True ((PBool False) `elem` propagated)-    , test "disjunction-both-false" $ do-        let-          t = [PEq (Var "x") (Lit 1), POr (PEq (Var "x") (Lit 2)) (PEq (Var "x") (Lit 0)), PEq (Var "y") (Lit 2)]-          propagated = Expr.constPropagate t-        assertEqualM "Must contain PBool False" True ((PBool False) `elem` propagated)-    , ignoreTest $ test "disequality-and-equality" $ do-        let-          t = [PNeg (PEq (Lit 1) (Var "arg1")), PEq (Lit 1) (Var "arg1")]-          propagated = Expr.constPropagate t-        assertEqualM "Must contain PBool False" True ((PBool False) `elem` propagated)-    , test "equality-and-disequality" $ do-        let-          t = [PEq (Lit 1) (Var "arg1"), PNeg (PEq (Lit 1) (Var "arg1"))]-          propagated = Expr.constPropagate t-        assertEqualM "Must contain PBool False" True ((PBool False) `elem` propagated)-  ]-  , testGroup "inequality-propagation-tests" [-      test "PLT-detects-impossible-constraint" $ do-        let-          -- x < 0 is impossible for unsigned integers-          t = [PLT (Var "x") (Lit 0)]-          propagated = Expr.constPropagate t-        assertEqualM "Must contain PBool False" True ((PBool False) `elem` propagated)-    , test "PLT-overflow-check" $ do-        let-          -- maxLit < y is impossible-          t = [PLT (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (Var "y")]-          propagated = Expr.constPropagate t-        assertEqualM "Must contain PBool False" True ((PBool False) `elem` propagated)-    , test "PGT-detects-impossible-constraint" $ do-        let-          -- x > maxLit is impossible-          t = [PGT (Var "x") (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)]-          propagated = Expr.constPropagate t-        assertEqualM "Must contain PBool False" True ((PBool False) `elem` propagated)-    , test "PGT-overflow-check" $ do-        let-          -- 0 > y is impossible-          t = [PGT (Lit 0) (Var "y")]-          propagated = Expr.constPropagate t-        assertEqualM "Must contain PBool False" True ((PBool False) `elem` propagated)-    , test "inequality-conflict-detection-narrow" $ do-        let-          -- x < 2 && x > 5 is impossible-          t = [PLT (Var "x") (Lit 2), PGT (Var "x") (Lit 5)]-          propagated = Expr.constPropagate t-        assertEqualM "Must contain PBool False" True ((PBool False) `elem` propagated)-    , test "inequality-conflict-detection-wide" $ do-        let-          -- x < 5 && x > 10 is impossible-          t = [PLT (Var "x") (Lit 5), PGT (Var "x") (Lit 10)]-          propagated = Expr.constPropagate t-        assertEqualM "Must contain PBool False" True ((PBool False) `elem` propagated)-    , test "inequality-tight-bounds-satisfied" $ do-        let-          -- x >= 5 && x <= 5 and x == 5 should be consistent-          t = [PGEq (Var "x") (Lit 5), PLEq (Var "x") (Lit 5), PEq (Var "x") (Lit 5)]-          propagated = Expr.constPropagate t-        assertEqualM "Must not contain PBool False" False ((PBool False) `elem` propagated)-    , test "inequality-tight-bounds-violated" $ do-        let-          -- x >= 5 && x <= 5 and x != 5 should be inconsistent-          t = [PGEq (Var "x") (Lit 5), PLEq (Var "x") (Lit 5), PNeg (PEq (Var "x") (Lit 5))]-          propagated = Expr.constPropagate t-        assertEqualM "Must contain PBool False" True ((PBool False) `elem` propagated)-    , test "inequality-with-existing-equality-consistent" $ do-        let-          -- x == 5 && x < 10 is consistent-          t = [PEq (Var "x") (Lit 5), PLT (Var "x") (Lit 10)]-          propagated = Expr.constPropagate t-        assertEqualM "Must not contain PBool False" False ((PBool False) `elem` propagated)-    , test "inequality-with-existing-equality-inconsistent" $ do-        let-          -- x == 5 && x < 5 is inconsistent-          t = [PEq (Var "x") (Lit 5), PLT (Var "x") (Lit 5)]-          propagated = Expr.constPropagate t-        assertEqualM "Must contain PBool False" True ((PBool False) `elem` propagated)-  ]-  , testGroup "simpProp-concrete-tests" [-      test "simpProp-concrete-trues" $ do-        let-          t = [PBool True, PBool True]-          simplified = Expr.simplifyProps t-        assertEqualM "Must be equal" [] simplified-    , test "simpProp-concrete-false1" $ do-        let-          t = [PBool True, PBool False]-          simplified = Expr.simplifyProps t-        assertEqualM "Must be equal" [PBool False] simplified-    , test "simpProp-concrete-false2" $ do-        let-          t = [PBool False, PBool False]-          simplified = Expr.simplifyProps t-        assertEqualM "Must be equal" [PBool False] simplified-    , test "simpProp-concrete-or-1" $ do-        let-          -- a = 5 && (a=4 || a=3)  -> False-          t = [PEq (Lit 5) (Var "a"), POr (PEq (Var "a") (Lit 4)) (PEq (Var "a") (Lit 3))]-          simplified = Expr.simplifyProps t-        assertEqualM "Must be equal" [PBool False] simplified-    , ignoreTest $ test "simpProp-concrete-or-2" $ do-        let-          -- Currently does not work, because we don't do simplification inside-          --   POr/PAnd using canBeSat-          -- a = 5 && (a=4 || a=5)  -> a=5-          t = [PEq (Lit 5) (Var "a"), POr (PEq (Var "a") (Lit 4)) (PEq (Var "a") (Lit 5))]-          simplified = Expr.simplifyProps t-        assertEqualM "Must be equal" [] simplified-    , test "simpProp-concrete-and-1" $ do-        let-          -- a = 5 && (a=4 && a=3)  -> False-          t = [PEq (Lit 5) (Var "a"), PAnd (PEq (Var "a") (Lit 4)) (PEq (Var "a") (Lit 3))]-          simplified = Expr.simplifyProps t-        assertEqualM "Must be equal" [PBool False] simplified-    , test "simpProp-concrete-or-of-or" $ do-        let-          -- a = 5 && ((a=4 || a=6) || a=3)  -> False-          t = [PEq (Lit 5) (Var "a"), POr (POr (PEq (Var "a") (Lit 4)) (PEq (Var "a") (Lit 6))) (PEq (Var "a") (Lit 3))]-          simplified = Expr.simplifyProps t-        assertEqualM "Must be equal" [PBool False] simplified-    , test "simpProp-inner-expr-simp" $ do-        let-          -- 5+1 = 6-          t = [PEq (Add (Lit 5) (Lit 1)) (Var "a")]-          simplified = Expr.simplifyProps t-        assertEqualM "Must be equal" [PEq (Lit 6) (Var "a")] simplified-    , test "simpProp-inner-expr-simp-with-canBeSat" $ do-        let-          -- 5+1 = 6, 6 != 7-          t = [PAnd (PEq (Add (Lit 5) (Lit 1)) (Var "a")) (PEq (Var "a") (Lit 7))]-          simplified = Expr.simplifyProps t-        assertEqualM "Must be equal" [PBool False] simplified-    , test "simpProp-inner-expr-bitwise-and" $ do-        let-          -- 1 & 2 != 2-          t = [PEq (And (Lit 1) (Lit 2)) (Lit 2)]-          simplified = Expr.simplifyProps t-        assertEqualM "Must be equal" [PBool False] simplified-    , test "simpProp-inner-expr-bitwise-or" $ do-        let-          -- 2 | 4 == 6-          t = [PEq (Or (Lit 2) (Lit 4)) (Lit 6)]-          simplified = Expr.simplifyProps t-        assertEqualM "Must be equal" [] simplified-    , test "simpProp-constpropagate-1" $ do-        let-          -- 5+1 = 6-          t = [PEq (Add (Lit 5) (Lit 1)) (Var "a"), PEq (Var "b") (Var "a")]-          simplified = Expr.simplifyProps t-        assertEqualM "Must be equal" [PEq (Lit 6) (Var "a"), PEq (Lit 6) (Var "b")] simplified-    , test "simpProp-constpropagate-2" $ do-        let-          -- 5+1 = 6-          t = [PEq (Add (Lit 5) (Lit 1)) (Var "a"), PEq (Var "b") (Var "a"), PEq (Var "c") (Sub (Var "b") (Lit 1))]-          simplified = Expr.simplifyProps t-        assertEqualM "Must be equal" [PEq (Lit 6) (Var "a"), PEq (Lit 6) (Var "b"), PEq (Lit 5) (Var "c")] simplified-    , test "simpProp-constpropagate-3" $ do-        let-          t = [ PEq (Add (Lit 5) (Lit 1)) (Var "a") -- a = 6-              , PEq (Var "b") (Var "a")             -- b = 6-              , PEq (Var "c") (Sub (Var "b") (Lit 1)) -- c = 5-              , PEq (Var "d") (Sub (Var "b") (Var "c"))] -- d = 1-          simplified = Expr.simplifyProps t-        assertEqualM "Must  know d == 1" ((PEq (Lit 1) (Var "d")) `elem` simplified) True-  ]-  , testGroup "MemoryTests"-    [ test "read-write-same-byte"  $ assertEqualM ""-        (LitByte 0x12)-        (Expr.readByte (Lit 0x20) (WriteByte (Lit 0x20) (LitByte 0x12) mempty))-    , test "read-write-same-word"  $ assertEqualM ""-        (Lit 0x12)-        (Expr.readWord (Lit 0x20) (WriteWord (Lit 0x20) (Lit 0x12) mempty))-    , test "read-byte-write-word"  $ assertEqualM ""-        -- reading at byte 31 a word that's been written should return LSB-        (LitByte 0x12)-        (Expr.readByte (Lit 0x1f) (WriteWord (Lit 0x0) (Lit 0x12) mempty))-    , test "read-byte-write-word2"  $ assertEqualM ""-        -- Same as above, but offset not 0-        (LitByte 0x12)-        (Expr.readByte (Lit 0x20) (WriteWord (Lit 0x1) (Lit 0x12) mempty))-    ,test "read-write-with-offset"  $ assertEqualM ""-        -- 0x3F = 63 decimal, 0x20 = 32. 0x12 = 18-        --    We write 128bits (32 Bytes), representing 18 at offset 32.-        --    Hence, when reading out the 63rd byte, we should read out the LSB 8 bits-        --           which is 0x12-        (LitByte 0x12)-        (Expr.readByte (Lit 0x3F) (WriteWord (Lit 0x20) (Lit 0x12) mempty))-    ,test "read-write-with-offset2"  $ assertEqualM ""-        --  0x20 = 32, 0x3D = 61-        --  we write 128 bits (32 Bytes) representing 0x10012, at offset 32.-        --  we then read out a byte at offset 61.-        --  So, at 63 we'd read 0x12, at 62 we'd read 0x00, at 61 we should read 0x1-        (LitByte 0x1)-        (Expr.readByte (Lit 0x3D) (WriteWord (Lit 0x20) (Lit 0x10012) mempty))-    , test "read-write-with-extension-to-zero" $ assertEqualM ""-        -- write word and read it at the same place (i.e. 0 offset)-        (Lit 0x12)-        (Expr.readWord (Lit 0x0) (WriteWord (Lit 0x0) (Lit 0x12) mempty))-    , test "read-write-with-extension-to-zero-with-offset" $ assertEqualM ""-        -- write word and read it at the same offset of 4-        (Lit 0x12)-        (Expr.readWord (Lit 0x4) (WriteWord (Lit 0x4) (Lit 0x12) mempty))-    , test "read-write-with-extension-to-zero-with-offset2" $ assertEqualM ""-        -- write word and read it at the same offset of 16-        (Lit 0x12)-        (Expr.readWord (Lit 0x20) (WriteWord (Lit 0x20) (Lit 0x12) mempty))-    , test "read-word-over-write-byte" $ assertEqualM ""-        (ReadWord (Lit 0x4) (AbstractBuf "abs"))-        (Expr.readWord (Lit 0x4) (WriteByte (Lit 0x1) (LitByte 0x12) (AbstractBuf "abs")))-    , test "read-word-copySlice-overlap" $ assertEqualM ""-        -- we should not recurse into a copySlice if the read index + 32 overlaps the sliced region-        (ReadWord (Lit 40) (CopySlice (Lit 0) (Lit 30) (Lit 12) (WriteWord (Lit 10) (Lit 0x64) (AbstractBuf "hi")) (AbstractBuf "hi")))-        (Expr.readWord (Lit 40) (CopySlice (Lit 0) (Lit 30) (Lit 12) (WriteWord (Lit 10) (Lit 0x64) (AbstractBuf "hi")) (AbstractBuf "hi")))-    , test "read-word-copySlice-after-slice" $ assertEqualM "Read word simplification missing!"-        (ReadWord (Lit 100) (AbstractBuf "dst"))-        (Expr.readWord (Lit 100) (CopySlice (Var "srcOff") (Lit 12) (Lit 60) (AbstractBuf "src") (AbstractBuf "dst")))-    , test "indexword-MSB" $ assertEqualM ""-        -- 31st is the LSB byte (of 32)-        (LitByte 0x78)-        (Expr.indexWord (Lit 31) (Lit 0x12345678))-    , test "indexword-LSB" $ assertEqualM ""-        -- 0th is the MSB byte (of 32), Lit 0xff22bb... is exactly 32 Bytes.-        (LitByte 0xff)-        (Expr.indexWord (Lit 0) (Lit 0xff22bb4455667788990011223344556677889900112233445566778899001122))-    , test "indexword-LSB2" $ assertEqualM ""-        -- same as above, but with offset 2-        (LitByte 0xbb)-        (Expr.indexWord (Lit 2) (Lit 0xff22bb4455667788990011223344556677889900112233445566778899001122))-    , test "encodeConcreteStore-overwrite" $-      assertEqualM ""-        (pure "(store (store ((as const Storage) #x0000000000000000000000000000000000000000000000000000000000000000) (_ bv1 256) (_ bv2 256)) (_ bv3 256) (_ bv4 256))")-        (EVM.SMT.encodeConcreteStore $ Map.fromList [(W256 1, W256 2), (W256 3, W256 4)])-    , test "indexword-oob-sym" $ assertEqualM ""-        -- indexWord should return 0 for oob access-        (LitByte 0x0)-        (Expr.indexWord (Lit 100) (JoinBytes-          (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0)-          (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0)-          (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0)-          (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0) (LitByte 0)))-    , test "stripbytes-concrete-bug" $ assertEqualM ""-        (Expr.simplifyReads (ReadByte (Lit 0) (ConcreteBuf "5")))-        (LitByte 53)-    ]-  , testGroup "ABI"-    [ testProperty "Put/get inverse" $ \x ->-        case runGetOrFail (getAbi (abiValueType x)) (runPut (putAbi x)) of-          Right ("", _, x') -> x' == x-          _ -> False-    , test "ABI-negative-small-int" $ do-        let bs = hex "ffffd6" -- -42 as int24-        let padded = BS.replicate (32 - BS.length bs) 0 <> bs -- padded to 32 bytes-        let withSelector = BS.replicate 4 0 <> padded -- added extra 4 bytes, simulating selector-        case decodeAbiValues [AbiIntType 24] withSelector of-          [AbiInt 24 val] -> assertEqualM "Incorrectly decoded int24 value" (-42) val-          _ -> internalError "Error in decoding function"-    ]-  , testGroup "Solidity-Expressions"-    [ test "Trivial" $-        SolidityCall "x = 3;" []-          ===> AbiUInt 256 3-    , test "Arithmetic" $ do-        SolidityCall "x = a + 1;"-          [AbiUInt 256 1] ===> AbiUInt 256 2-        SolidityCall "unchecked { x = a - 1; }"-          [AbiUInt 8 0] ===> AbiUInt 8 255-    , test "negative-numbers-nonzero-comp-1" $ do-        Just c <- solcRuntime "C" [i|-            contract C {-              function fun(int256 x) public {-                  // Cheatcode address-                  address vm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;-                  bytes memory data = abi.encodeWithSignature("assertGe(int256,int256)", x, -1);-                  (bool success, ) = vm.staticcall(data);-                  assert(success == true);-              }-            } |]-        let sig = Just $ Sig "fun(int256)" [AbiIntType 256]-        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        assertBoolM "The expression must not be partial" $ not (any isPartial e)-        let numCexes = sum $ map (fromEnum . isCex) ret-        let numErrs = sum $ map (fromEnum . isError) ret-        assertEqualM "number of counterexamples" 1 numCexes-        assertEqualM "number of errors" 0 numErrs-    , test "negative-numbers-nonzero-comp-2" $ do-        Just c <- solcRuntime "C" [i|-            contract C {-              function fun(int256 x) public {-                  // Cheatcode address-                  address vm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;-                  bytes memory data = abi.encodeWithSignature("assertGe(int256,int256)", x, 1);-                  (bool success, ) = vm.staticcall(data);-                  assert(success == true);-              }-            } |]-        let sig = Just $ Sig "fun(int256)" [AbiIntType 256]-        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        assertBoolM "The expression must not be partial" $ not (any isPartial e)-        let numCexes = sum $ map (fromEnum . isCex) ret-        let numErrs = sum $ map (fromEnum . isError) ret-        assertEqualM "number of counterexamples" 1 numCexes-        assertEqualM "number of errors" 0 numErrs-    , test "negative-numbers-min" $ do-        Just c <- solcRuntime "C" [i|-            contract C {-              function fun(int256 x) public {-                  // Cheatcode address-                  address vm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;-                  bytes memory data = abi.encodeWithSignature("assertLt(int256,int256)", x, type(int256).min);-                  (bool success, ) = vm.staticcall(data);-                  assert(success == true);-              }-            } |]-        let sig = Just $ Sig "fun(int256)" [AbiIntType 256]-        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        assertBoolM "The expression must not be partial" $ not (any isPartial e)-        let numCexes = sum $ map (fromEnum . isCex) ret-        let numErrs = sum $ map (fromEnum . isError) ret-        assertEqualM "number of counterexamples" 1 numCexes-        assertEqualM "number of errors" 0 numErrs-    , test "negative-numbers-int128-1" $ do-        Just c <- solcRuntime "C" [i|-            contract C {-              function fun(int128 y) public {-                  int256 x = int256(y);-                  // Cheatcode address-                  address vm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;-                  bytes memory data = abi.encodeWithSignature("assertLt(int256,int256)", x, -1);-                  (bool success, ) = vm.staticcall(data);-                  assert(success == true);-              }-            } |]-        let sig = Just $ Sig "fun(int128)" [AbiIntType 128]-        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        assertBoolM "The expression must not be partial" $ not (any isPartial e)-        let numCexes = sum $ map (fromEnum . isCex) ret-        let numErrs = sum $ map (fromEnum . isError) ret-        assertEqualM "number of counterexamples" 1 numCexes-        assertEqualM "number of errors" 0 numErrs-    , test "negative-numbers-zero-comp-simpleassert" $ do-        Just c <- solcRuntime "C" [i|-            contract C {-              function fun(int256 x) public {-                assert(x >= 0);-              }-            } |]-        let sig = Just $ Sig "fun(int256)" [AbiIntType 256]-        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        assertBoolM "The expression must not be partial" $ not (any isPartial e)-        let numCexes = sum $ map (fromEnum . isCex) ret-        let numErrs = sum $ map (fromEnum . isError) ret-        assertEqualM "number of counterexamples" 1 numCexes-        assertEqualM "number of errors" 0 numErrs-    , test "signed-int8-range" $ do-        Just c <- solcRuntime "C" [i|-          contract C {-            function fun(int8 x) public {-              int256 y = x;-              assert (y != 1000);-            }-          } |]-        let sig = Just $ Sig "fun(int8)" [AbiIntType 8]-        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        assertBoolM "The expression must not be partial" $ not (any isPartial e)-        let numCexes = sum $ map (fromEnum . isCex) ret-        let numErrs = sum $ map (fromEnum . isError) ret-        assertEqualM "number of counterexamples" 0 numCexes-        assertEqualM "number of errors" 0 numErrs-    , test "base-2-exp-uint8" $ do-        Just c <- solcRuntime "C" [i|-          contract C {-            function fun(uint8 x) public {-              unchecked {-                require(x < 10);-                uint256 y = 2**x;-                assert (y <= 512);-              }-            }-          } |]-        let sig = Just $ Sig "fun(uint8)" [AbiUIntType 8]-        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        assertBoolM "The expression must not be partial" $ not (any isPartial e)-        let numCexes = sum $ map (fromEnum . isCex) ret-        let numErrs = sum $ map (fromEnum . isError) ret-        assertEqualM "number of counterexamples" 0 numCexes-        assertEqualM "number of errors" 0 numErrs-    , test "base-2-exp-no-rollaround" $ do-        Just c <- solcRuntime "C" [i|-          contract C {-            function fun(uint256 x) public {-              unchecked {-                require(x > 10);-                require(x < 256);-                uint256 y = 2**x;-                assert (y > 512);-              }-            }-          } |]-        let sig = Just $ Sig "fun(uint256)" [AbiUIntType 256]-        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        assertBoolM "The expression must not be partial" $ not (any isPartial e)-        let numCexes = sum $ map (fromEnum . isCex) ret-        let numErrs = sum $ map (fromEnum . isError) ret-        assertEqualM "number of counterexamples" 0 numCexes-        assertEqualM "number of errors" 0 numErrs-    , test "base-2-exp-rollaround" $ do-        Just c <- solcRuntime "C" [i|-          contract C {-            function fun(uint256 x) public {-              unchecked {-                require(x == 256);-                uint256 y = 2**x;-                assert (y > 512);-              }-            }-          } |]-        let sig = Just $ Sig "fun(uint256)" [AbiUIntType 256]-        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        assertBoolM "The expression must not be partial" $ not (any isPartial e)-        let numCexes = sum $ map (fromEnum . isCex) ret-        let numErrs = sum $ map (fromEnum . isError) ret-        assertEqualM "number of counterexamples" 1 numCexes-        assertEqualM "number of errors" 0 numErrs-    , test "unsigned-int8-range" $ do-        Just c <- solcRuntime "C" [i|-          contract C {-            function fun(uint8 x) public {-              uint256 y = x;-              assert (y != 1000);-            }-          } |]-        let sig = Just $ Sig "fun(uint8)" [AbiUIntType 8]-        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        assertBoolM "The expression must not be partial" $ not (any isPartial e)-        let numCexes = sum $ map (fromEnum . isCex) ret-        let numErrs = sum $ map (fromEnum . isError) ret-        assertEqualM "number of counterexamples" 0 numCexes-        assertEqualM "number of errors" 0 numErrs-    , test "negative-numbers-zero-comp" $ do-        Just c <- solcRuntime "C" [i|-            contract C {-              function fun(int256 x) public {-                  // Cheatcode address-                  address vm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;-                  bytes memory data = abi.encodeWithSignature("assertGe(int256,int256)", x, 0);-                  (bool success, ) = vm.staticcall(data);-                  assert(success == true);-              }-            } |]-        let sig = Just $ Sig "fun(int256)" [AbiIntType 256]-        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        assertBoolM "The expression must not be partial" $ not (any isPartial e)-        let numCexes = sum $ map (fromEnum . isCex) ret-        let numErrs = sum $ map (fromEnum . isError) ret-        assertEqualM "number of counterexamples" 1 numCexes-        assertEqualM "number of errors" 0 numErrs-    , test "positive-numbers-cex" $ do-        Just c <- solcRuntime "C" [i|-            contract C {-              function fun(uint256 x) public {-                  // Cheatcode address-                  address vm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;-                  bytes memory data = abi.encodeWithSignature("assertGe(uint256,uint256)", x, 1);-                  (bool success, ) = vm.staticcall(data);-                  assert(success == true);-              }-            } |]-        let sig = Just $ Sig "fun(uint256)" [AbiUIntType 256]-        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        assertBoolM "The expression must not be partial" $ not (any isPartial e)-        let numCexes = sum $ map (fromEnum . isCex) ret-        let numErrs = sum $ map (fromEnum . isError) ret-        assertEqualM "number of counterexamples" 1 numCexes-        assertEqualM "number of errors" 0 numErrs-    , test "positive-numbers-qed" $ do-        Just c <- solcRuntime "C" [i|-            contract C {-              function fun(uint256 x) public {-                  // Cheatcode address-                  address vm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;-                  bytes memory data = abi.encodeWithSignature("assertGe(uint256,uint256)", x, 0);-                  (bool success, ) = vm.staticcall(data);-                  assert(success == true);-              }-            } |]-        let sig = Just $ Sig "fun(uint256)" [AbiUIntType 256]-        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        assertBoolM "The expression must not be partial" $ not (any isPartial e)-        let numCexes = sum $ map (fromEnum . isCex) ret-        let numErrs = sum $ map (fromEnum . isError) ret-        assertEqualM "number of counterexamples" 0 numCexes-        assertEqualM "number of errors" 0 numErrs--    , test "keccak256()" $-        SolidityCall "x = uint(keccak256(abi.encodePacked(a)));"-          [AbiString ""] ===> AbiUInt 256 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470--    , testProperty "symbolic-abi-enc-vs-solidity" $ \(SymbolicAbiVal y) -> prop $ do-          Just encoded <- runStatements [i| x = abi.encode(a);|] [y] AbiBytesDynamicType-          let solidityEncoded = case decodeAbiValue (AbiTupleType $ V.fromList [AbiBytesDynamicType]) (BS.fromStrict encoded) of-                AbiTuple (V.toList -> [e]) -> e-                _ -> internalError "AbiTuple expected"-          let-              frag = [symAbiArg "y" (AbiTupleType $ V.fromList [abiValueType y])]-              (hevmEncoded, _) = first (Expr.drop 4) $ combineFragments frag (ConcreteBuf "")-              expectedVals = expectedConcVals "y" (AbiTuple . V.fromList $ [y])-              hevmConcretePre = fromRight (error "cannot happen") $ subModel expectedVals hevmEncoded-              hevmConcrete = case Expr.simplify hevmConcretePre of-                               ConcreteBuf b -> b-                               buf -> internalError ("valMap: " <> show expectedVals <> "\ny:" <> show y <> "\n" <> "buf: " <> show buf)-          -- putStrLnM $ "frag: " <> show frag-          -- putStrLnM $ "expectedVals: " <> show expectedVals-          -- putStrLnM $ "frag: " <> show frag-          -- putStrLnM $ "hevmEncoded: " <> show hevmEncoded-          -- putStrLnM $ "solidity encoded: " <> show solidityEncoded-          -- putStrLnM $ "our encoded     : " <> show (AbiBytesDynamic hevmConcrete)-          -- putStrLnM $ "y     : " <> show y-          -- putStrLnM $ "y type: " <> showAlter y-          -- putStrLnM $ "hevmConcretePre: " <> show hevmConcretePre-          assertEqualM "abi encoding mismatch" solidityEncoded (AbiBytesDynamic hevmConcrete)-    , testProperty "symbolic-abi encoding-vs-solidity-2-args" $ \(SymbolicAbiVal x', SymbolicAbiVal y') -> prop $ do-          Just encoded <- runStatements [i| x = abi.encode(a, b);|] [x', y'] AbiBytesDynamicType-          let solidityEncoded = case decodeAbiValue (AbiTupleType $ V.fromList [AbiBytesDynamicType]) (BS.fromStrict encoded) of-                AbiTuple (V.toList -> [e]) -> e-                _ -> internalError "AbiTuple expected"-          let hevmEncoded = encodeAbiValue (AbiTuple $ V.fromList [x',y'])-          assertEqualM "abi encoding mismatch" solidityEncoded (AbiBytesDynamic hevmEncoded)-    , testProperty "abi-encoding-vs-solidity" $ forAll (arbitrary >>= genAbiValue) $-      \y -> prop $ do-          Just encoded <- runStatements [i| x = abi.encode(a);|]-            [y] AbiBytesDynamicType-          let solidityEncoded = case decodeAbiValue (AbiTupleType $ V.fromList [AbiBytesDynamicType]) (BS.fromStrict encoded) of-                AbiTuple (V.toList -> [e]) -> e-                _ -> internalError "AbiTuple expected"-          let hevmEncoded = encodeAbiValue (AbiTuple $ V.fromList [y])-          assertEqualM "abi encoding mismatch" solidityEncoded (AbiBytesDynamic hevmEncoded)--    , testProperty "abi-encoding-vs-solidity-2-args" $ forAll (arbitrary >>= bothM genAbiValue) $-      \(x', y') -> prop $ do-          Just encoded <- runStatements [i| x = abi.encode(a, b);|]-            [x', y'] AbiBytesDynamicType-          let solidityEncoded = case decodeAbiValue (AbiTupleType $ V.fromList [AbiBytesDynamicType]) (BS.fromStrict encoded) of-                AbiTuple (V.toList -> [e]) -> e-                _ -> internalError "AbiTuple expected"-          let hevmEncoded = encodeAbiValue (AbiTuple $ V.fromList [x',y'])-          assertEqualM "abi encoding mismatch" solidityEncoded (AbiBytesDynamic hevmEncoded)--    -- we need a separate test for this because the type of a function is "function() external" in solidity but just "function" in the abi:-    , askOption $ \(QuickCheckTests n) -> testProperty "abi-encoding-vs-solidity-function-pointer" $ withMaxSuccess (min n 20) $ forAll (genAbiValue AbiFunctionType) $-      \y -> prop $ do-          Just encoded <- runFunction [i|-              function foo(function() external a) public pure returns (bytes memory x) {-                x = abi.encode(a);-              }-            |] (abiMethod "foo(function)" (AbiTuple (V.singleton y)))-          let solidityEncoded = case decodeAbiValue (AbiTupleType $ V.fromList [AbiBytesDynamicType]) (BS.fromStrict encoded) of-                AbiTuple (V.toList -> [e]) -> e-                _ -> internalError "AbiTuple expected"-          let hevmEncoded = encodeAbiValue (AbiTuple $ V.fromList [y])-          assertEqualM "abi encoding mismatch" solidityEncoded (AbiBytesDynamic hevmEncoded)-    ]--  , testGroup "Precompiled contracts"-      [ testGroup "Example (reverse)"-          [ test "success" $-              assertEqualM "example contract reverses"-                (execute 0xdeadbeef "foobar" 6) (Just "raboof")-          , test "failure" $-              assertEqualM "example contract fails on length mismatch"-                (execute 0xdeadbeef "foobar" 5) Nothing-          ]--      , testGroup "ECRECOVER"-          [ test "success" $ do-              let-                r = hex "c84e55cee2032ea541a32bf6749e10c8b9344c92061724c4e751600f886f4732"-                s = hex "1542b6457e91098682138856165381453b3d0acae2470286fd8c8a09914b1b5d"-                v = hex "000000000000000000000000000000000000000000000000000000000000001c"-                h = hex "513954cf30af6638cb8f626bd3f8c39183c26784ce826084d9d267868a18fb31"-                a = hex "0000000000000000000000002d5e56d45c63150d937f2182538a0f18510cb11f"-              assertEqualM "successful recovery"-                (Just a)-                (execute 1 (h <> v <> r <> s) 32)-          , test "fail on made up values" $ do-              let-                r = hex "c84e55cee2032ea541a32bf6749e10c8b9344c92061724c4e751600f886f4731"-                s = hex "1542b6457e91098682138856165381453b3d0acae2470286fd8c8a09914b1b5d"-                v = hex "000000000000000000000000000000000000000000000000000000000000001c"-                h = hex "513954cf30af6638cb8f626bd3f8c39183c26784ce826084d9d267868a18fb31"-              assertEqualM "fail because bit flip"-                Nothing-                (execute 1 (h <> v <> r <> s) 32)-          ]-      ]-  , testGroup "Byte/word manipulations"-    [ testProperty "padLeft length" $ \n (Bytes bs) ->-        BS.length (padLeft n bs) == max n (BS.length bs)-    , testProperty "padLeft identity" $ \(Bytes bs) ->-        padLeft (BS.length bs) bs == bs-    , testProperty "padRight length" $ \n (Bytes bs) ->-        BS.length (padLeft n bs) == max n (BS.length bs)-    , testProperty "padRight identity" $ \(Bytes bs) ->-        padLeft (BS.length bs) bs == bs-    , testProperty "padLeft zeroing" $ \(NonNegative n) (Bytes bs) ->-        let x = BS.take n (padLeft (BS.length bs + n) bs)-            y = BS.replicate n 0-        in x == y-    ]--  , testGroup "Word/Addr encoding"-    [ testProperty "word256Bytes" $ \w ->-        word256Bytes w == slow_word256Bytes w-    , testProperty "word160Bytes" $ \a ->-        word160Bytes a == slow_word160Bytes a-    ]--  , testGroup "Unresolved link detection"-    [ test "holes detected" $ do-        let code' = "608060405234801561001057600080fd5b5060405161040f38038061040f83398181016040528101906100329190610172565b73__$f3cbc3eb14e5bd0705af404abcf6f741ec$__63ab5c1ffe826040518263ffffffff1660e01b81526004016100699190610217565b60206040518083038186803b15801561008157600080fd5b505af4158015610095573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100b99190610145565b50506103c2565b60006100d36100ce84610271565b61024c565b9050828152602081018484840111156100ef576100ee610362565b5b6100fa8482856102ca565b509392505050565b600081519050610111816103ab565b92915050565b600082601f83011261012c5761012b61035d565b5b815161013c8482602086016100c0565b91505092915050565b60006020828403121561015b5761015a61036c565b5b600061016984828501610102565b91505092915050565b6000602082840312156101885761018761036c565b5b600082015167ffffffffffffffff8111156101a6576101a5610367565b5b6101b284828501610117565b91505092915050565b60006101c6826102a2565b6101d081856102ad565b93506101e08185602086016102ca565b6101e981610371565b840191505092915050565b60006102016003836102ad565b915061020c82610382565b602082019050919050565b6000604082019050818103600083015261023181846101bb565b90508181036020830152610244816101f4565b905092915050565b6000610256610267565b905061026282826102fd565b919050565b6000604051905090565b600067ffffffffffffffff82111561028c5761028b61032e565b5b61029582610371565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b60008115159050919050565b60005b838110156102e85780820151818401526020810190506102cd565b838111156102f7576000848401525b50505050565b61030682610371565b810181811067ffffffffffffffff821117156103255761032461032e565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f6261720000000000000000000000000000000000000000000000000000000000600082015250565b6103b4816102be565b81146103bf57600080fd5b50565b603f806103d06000396000f3fe6080604052600080fdfea26469706673582212207d03b26e43dc3d116b0021ddc9817bde3762a3b14315351f11fc4be384fd14a664736f6c63430008060033"-        assertBoolM "linker hole not detected" (containsLinkerHole code'),-      test "no false positives" $ do-        let code' = "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806317bf8bac1461003b578063acffee6b1461005d575b600080fd5b610043610067565b604051808215151515815260200191505060405180910390f35b610065610073565b005b60008060015414905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8a8fd6d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156100da57600080fd5b505afa1580156100ee573d6000803e3d6000fd5b505050506040513d602081101561010457600080fd5b810190808051906020019092919050505060018190555056fea265627a7a723158205d775f914dcb471365a430b5f5b2cfe819e615cbbb5b2f1ccc7da1fd802e43c364736f6c634300050b0032"-        assertBoolM "false positive" (not . containsLinkerHole $ code')-    ]--  , testGroup "metadata stripper"-    [ test "it strips the metadata for solc => 0.6" $ do-        let code' = hexText "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806317bf8bac1461003b578063acffee6b1461005d575b600080fd5b610043610067565b604051808215151515815260200191505060405180910390f35b610065610073565b005b60008060015414905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8a8fd6d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156100da57600080fd5b505afa1580156100ee573d6000803e3d6000fd5b505050506040513d602081101561010457600080fd5b810190808051906020019092919050505060018190555056fea265627a7a723158205d775f914dcb471365a430b5f5b2cfe819e615cbbb5b2f1ccc7da1fd802e43c364736f6c634300050b0032"-            stripped = stripBytecodeMetadata code'-        assertEqualM "failed to strip metadata" (show (ByteStringS stripped)) "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806317bf8bac1461003b578063acffee6b1461005d575b600080fd5b610043610067565b604051808215151515815260200191505060405180910390f35b610065610073565b005b60008060015414905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8a8fd6d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156100da57600080fd5b505afa1580156100ee573d6000803e3d6000fd5b505050506040513d602081101561010457600080fd5b810190808051906020019092919050505060018190555056fe"-    ,-      testCase "it strips the metadata and constructor args" $ do-        let srccode =-              [i|-                contract A {-                  uint y;-                  constructor(uint x) public {-                    y = x;-                  }-                }-                |]--        Just initCode <- solidity "A" srccode-        assertEqual "constructor args screwed up metadata stripping" (stripBytecodeMetadata (initCode <> encodeAbiValue (AbiUInt 256 1))) (stripBytecodeMetadata initCode)-    ]--  , testGroup "RLP encodings"-    [ testProperty "rlp decode is a retraction (bytes)" $ \(Bytes bs) ->-      rlpdecode (rlpencode (BS bs)) == Just (BS bs)-    , testProperty "rlp encode is a partial inverse (bytes)" $ \(Bytes bs) ->-        case rlpdecode bs of-          Just r -> rlpencode r == bs-          Nothing -> True-    ,  testProperty "rlp decode is a retraction (RLP)" $ \(RLPData r) ->-       rlpdecode (rlpencode r) == Just r-    ]- , testGroup "Panic code tests via symbolic execution"-  [-     test "assert-fail" $ do-       Just c <- solcRuntime "MyContract"-           [i|-           contract MyContract {-             function fun(uint256 a) external pure {-               assert(a != 0);-             }-            }-           |]-       (_, [Cex (_, ctr)]) <- withDefaultSolver $ \s -> checkAssert s [0x01] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-       assertEqualM "Must be 0" 0 $ getVar ctr "arg1"-       putStrLnM  $ "expected counterexample found, and it's correct: " <> (show $ getVar ctr "arg1")-     ,-     test "safeAdd-fail" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 a, uint256 b) external pure returns (uint256 c) {-               c = a+b;-              }-             }-            |]-        (_, [Cex (_, ctr)]) <- withDefaultSolver $ \s -> checkAssert s [0x11] c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-        let x = getVar ctr "arg1"-        let y = getVar ctr "arg2"--        let maxUint = 2 ^ (256 :: Integer) :: Integer-        assertBoolM "Overflow must occur" (toInteger x + toInteger y >= maxUint)-        putStrLnM "expected counterexample found"-     ,-     test "div-by-zero-fail" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 a, uint256 b) external pure returns (uint256 c) {-               c = a/b;-              }-             }-            |]-        (_, [Cex (_, ctr)]) <- withSolvers Bitwuzla 1 1 Nothing $ \s -> checkAssert s [0x12] c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-        assertEqualM "Division by 0 needs b=0" (getVar ctr "arg2") 0-        putStrLnM "expected counterexample found"-     ,-      test "unused-args-fail" $ do-         Just c <- solcRuntime "C"-             [i|-             contract C {-               function fun(uint256 a) public pure {-                 assert(false);-               }-             }-             |]-         (_, [Cex _]) <- withSolvers Bitwuzla 1 1 Nothing $ \s -> checkAssert s [0x1] c Nothing [] defaultVeriOpts-         putStrLnM "expected counterexample found"-     , test "gas-decrease-monotone" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint8 a) external {-                uint a = gasleft();-                uint b = gasleft();-                assert(a > b);-              }-             }-            |]-        let sig = (Just (Sig "fun(uint8)" [AbiUIntType 8]))-        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        putStrLnM "expected Qed found"-     , test "enum-conversion-fail" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              enum MyEnum { ONE, TWO }-              function fun(uint256 a) external pure returns (MyEnum b) {-                b = MyEnum(a);-              }-             }-            |]-        (_, [Cex (_, ctr)]) <- withSolvers Bitwuzla 1 1 Nothing $ \s -> checkAssert s [0x21] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-        assertBoolM "Enum is only defined for 0 and 1" $ (getVar ctr "arg1") > 1-        putStrLnM "expected counterexample found"-     ,-     -- TODO 0x22 is missing: "0x22: If you access a storage byte array that is incorrectly encoded."-     -- TODO below should NOT fail-     -- TODO this has a loop that depends on a symbolic value and currently causes interpret to loop-     ignoreTest $ test "pop-empty-array" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              uint[] private arr;-              function fun(uint8 a) external {-                arr.push(1);-                arr.push(2);-                for (uint i = 0; i < a; i++) {-                  arr.pop();-                }-              }-             }-            |]-        a <- withDefaultSolver $ \s -> checkAssert s [0x31] c (Just (Sig "fun(uint8)" [AbiUIntType 8])) [] defaultVeriOpts-        liftIO $ do-          print $ length a-          print $ show a-          putStrLnM "expected counterexample found"-     ,-     test "access-out-of-bounds-array" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              uint[] private arr;-              function fun(uint8 a) external returns (uint x){-                arr.push(1);-                arr.push(2);-                x = arr[a];-              }-             }-            |]-        (_, [Cex (_, _)]) <- withDefaultSolver $ \s -> checkAssert s [0x32] c (Just (Sig "fun(uint8)" [AbiUIntType 8])) [] defaultVeriOpts-        -- assertBoolM "Access must be beyond element 2" $ (getVar ctr "arg1") > 1-        putStrLnM "expected counterexample found"-      ,-      -- Note: we catch the assertion here, even though we are only able to explore partially-      test "alloc-too-much" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 a) external {-                uint[] memory arr = new uint[](a);-              }-             }-            |]-        (_, [Cex _]) <- withDefaultSolver $ \s ->-          checkAssert s [0x41] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-        putStrLnM "expected counterexample found"-      ,-      test "vm.deal unknown address" $ do-        Just c <- solcRuntime "C"-          [i|-            interface Vm {-              function deal(address,uint256) external;-            }-            contract C {-              // this is not supported yet due to restrictions around symbolic address aliasing...-              function f(address e, uint val) external {-                  Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);-                  vm.deal(e, val);-                  assert(e.balance == val);-              }-            }-          |]-        Right e <- reachableUserAsserts c (Just $ Sig "f(address,uint256)" [AbiAddressType, AbiUIntType 256])-        assertBoolM "The expression is not partial" $ any isPartial e-      ,-      test "vm.prank-create" $ do-        Just c <- solcRuntime "C"-            [i|-              interface Vm {-                function prank(address) external;-              }-              contract Owned {-                address public owner;-                constructor() {-                  owner = msg.sender;-                }-              }-              contract C {-                function f() external {-                  Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);--                  Owned target = new Owned();-                  assert(target.owner() == address(this));--                  address usr = address(1312);-                  vm.prank(usr);-                  target = new Owned();-                  assert(target.owner() == usr);-                  target = new Owned();-                  assert(target.owner() == address(this));-                }-              }-            |]-        Right _ <- reachableUserAsserts c (Just $ Sig "f()" [])-        liftIO $ putStrLn "no reachable assertion violations"-      ,-      test "vm.prank underflow" $ do-        Just c <- solcRuntime "C"-            [i|-              interface Vm {-                function prank(address) external;-              }-              contract Payable {-                  function hi() public payable {}-              }-              contract C {-                function f() external {-                  Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);--                  uint amt = 10;-                  address from = address(0xacab);-                  require(from.balance < amt);--                  Payable target = new Payable();-                  vm.prank(from);-                  target.hi{value : amt}();-                }-              }-            |]-        r <- allBranchesFail c Nothing-        assertBoolM "all branches must fail" (isRight r)-      , test "cheatcode-nonexistent" $ do-        Just c <- solcRuntime "C"-            [i|-              interface Vm {-                function nonexistent_cheatcode(uint) external;-              }-            contract C {-              function fun(uint a) public {-                  // Cheatcode address-                  Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);-                  vm.nonexistent_cheatcode(a);-                  assert(1 == 1);-              }-            }-            |]-        let sig = Just (Sig "fun(uint256)" [AbiUIntType 256])-        (e, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        assertBoolM "The expression must contain Partial." $ any isPartial e-      , test "cheatcode-with-selector" $ do-        Just c <- solcRuntime "C"-            [i|-            contract C {-            function prove_warp_symbolic(uint128 jump) public {-                    uint pre = block.timestamp;-                    address hevm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;-                    (bool success, ) = hevm.call(abi.encodeWithSelector(bytes4(keccak256("warp(uint256)")), block.timestamp+jump));-                    require(success, "Call to hevm.warp failed");-                    assert(block.timestamp == pre + jump);-                }-            }-            |]-        Right e <- reachableUserAsserts c Nothing-        assertBoolM "The expression should not contain Partial." $ Prelude.not $ any isPartial e-      ,-      test "call ffi when disabled" $ do-        Just c <- solcRuntime "C"-            [i|-              interface Vm {-                function ffi(string[] calldata) external;-              }-              contract C {-                function f() external {-                  Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);--                  string[] memory inputs = new string[](2);-                  inputs[0] = "echo";-                  inputs[1] = "acab";--                  // should fail to explore this branch-                  vm.ffi(inputs);-                }-              }-            |]-        r <- allBranchesFail c Nothing-        assertBoolM "all branches must fail" (isRight r)-      ,-      -- TODO: we can't deal with symbolic jump conditions-      expectFail $ test "call-zero-inited-var-thats-a-function" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function (uint256) internal returns (uint) funvar;-              function fun2(uint256 a) internal returns (uint){-                return a;-              }-              function fun(uint256 a) external returns (uint) {-                if (a != 44) {-                  funvar = fun2;-                }-                return funvar(a);-              }-             }-            |]-        (_, [Cex (_, cex)]) <- withDefaultSolver $-          \s -> checkAssert s [0x51] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-        let a = fromJust $ Map.lookup (Var "arg1") cex.vars-        assertEqualM "unexpected cex value" a 44-        putStrLnM "expected counterexample found"-      , test "symbolic-exp-0-to-n" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 a, uint256 b, uint256 k) external pure {-                uint x = 0 ** b;-                assert (x == 1);-              }-             }-            |]-        let sig = Just (Sig "fun(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])-        a <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        case a of-          (_, [Cex (_, ctr)]) -> do-            let b = getVar ctr "arg2"-            putStrLnM $ "b:" <> show b-            assertBoolM "b must be non-0" (b /= 0)-          _ -> assertBoolM "Wrong" False-      , test "symbolic-exp-0-to-n2" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 a, uint256 b, uint256 k) external pure {-                uint x = 0 ** b;-                assert (x == 0);-              }-             }-            |]-        let sig = Just (Sig "fun(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])-        a <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        case a of-          (_, [Cex (_, ctr)]) -> do-            let b = getVar ctr "arg2"-            putStrLnM $ "b:" <> show b-            assertBoolM "b must be 0" (b == 0)-          _ -> assertBoolM "Wrong" False-      ,-      test "symbolic-mcopy" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 a, uint256 s) external returns (uint) {-                require(a < 5);-                assembly {-                    mcopy(0x2, 0, s)-                    a:=mload(s)-                }-                assert(a < 5);-                return a;-              }-             }-            |]-        let sig = Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])-        (_, k) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        let numErrs = sum $ map (fromEnum . isError) k-        assertEqualM "number of errors (i.e. copySlice issues) is 1" 1 numErrs-        let errStrings = mapMaybe getResError k-        assertEqualM "All errors are from copyslice" True $ all ("CopySlice" `List.isInfixOf`) errStrings-      -- below we hit the limit of the depth of the symbolic execution tree-      , testCase "limit-num-explore-hit-limit" $ do-        let conf = testEnv.config {maxDepth = Just 3}-        let myTestEnv :: Env = (testEnv :: Env) {config = conf :: Config}-        runEnv myTestEnv $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-                function checkval(uint256 a, uint256 b, uint256 c) public {-                  if (a == b) {-                    if (b == c) {-                      assert(false);-                    }-                  }-                }-            }-            |]-          let sig = Just (Sig "checkval(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])-          (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-          let numCexes = sum $ map (fromEnum . isCex) ret-          let numErrs = sum $ map (fromEnum . isError) ret-          assertBoolM "The expression MUST be partial" (any isPartial paths)-          assertEqualM "number of errors" 0 numErrs-          assertEqualM "number of counterexamples" 0 numCexes-      -- below we don't hit the limit of the depth of the symbolic execution tree-      , testCase "limit-num-explore-no-hit-limit" $ do-        let conf = testEnv.config {maxDepth = Just 7}-        let myTestEnv :: Env = (testEnv :: Env) {config = conf :: Config}-        runEnv myTestEnv $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-                function checkval(uint256 a, uint256 b, uint256 c) public {-                  if (a == b) {-                    if (b == c) {-                      assert(false);-                    }-                  }-                }-            }-            |]-          let sig = Just (Sig "checkval(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])-          (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-          let numCexes = sum $ map (fromEnum . isCex) ret-          let numErrs = sum $ map (fromEnum . isError) ret-          assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)-          assertEqualM "number of errors" 0 numErrs-          assertEqualM "number of counterexamples" 1 numCexes-      , test "symbolic-copyslice" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 a, uint256 s) external returns (uint) {-                require(a < 10);-                if (a >= 8) {-                  assembly {-                      calldatacopy(0x5, s, s)-                      a:=mload(s)-                  }-                } else {-                  assembly {-                      calldatacopy(0x2, 0x2, 5)-                      a:=mload(s)-                  }-                }-                assert(a < 9);-                return a;-              }-             }-            |]-        let sig = Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])-        (_, k) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        let numErrs = sum $ map (fromEnum . isError) k-        assertEqualM "number of errors (i.e. copySlice issues) is 1" 1 numErrs-        let errStrings = mapMaybe getResError k-        assertEqualM "All errors are from copyslice" True $ all ("CopySlice" `List.isInfixOf`) errStrings-  ]-  , testGroup "Symbolic-Constructor-Args"-    -- this produced some hard to debug failures. keeping it around since it seemed to exercise the contract creation code in interesting ways...-    [ test "multiple-symbolic-constructor-calls" $ do-        Just initCode <- solidity "C"-          [i|-            contract A {-                uint public x;-                constructor (uint z)  {}-            }--            contract B {-                constructor (uint i)  {}--            }--            contract C {-                constructor(uint u) {-                  new A(u);-                  new B(u);-                }-            }-          |]-        withSolvers Bitwuzla 1 1 Nothing $ \s -> do-          let calldata = (WriteWord (Lit 0x0) (Var "u") (ConcreteBuf ""), [])-          initVM <- liftIO $ stToIO $ abstractVM calldata initCode Nothing True-          let iterConf = IterConfig {maxIter=Nothing, askSmtIters=1, loopHeuristic=StackBased }-          paths <- interpret (Fetch.noRpcFetcher s) iterConf initVM runExpr pure-          let exprSimp = map Expr.simplify paths-          assertBoolM "unexptected partial execution" (not $ any isPartial exprSimp)-    , test "mixed-concrete-symbolic-args" $ do-        Just c <- solcRuntime "C"-          [i|-            contract B {-                uint public x;-                uint public y;-                constructor (uint i, uint j)  {-                  x = i;-                  y = j;-                }--            }--            contract C {-                function foo(uint i) public {-                  B b = new B(10, i);-                  assert(b.x() == 10);-                  assert(b.y() == i);-                }-            }-          |]-        Right paths <- reachableUserAsserts c (Just $ Sig "foo(uint256)" [AbiUIntType 256])-        assertBoolM "unexptected partial execution" $ Prelude.not (any isPartial paths)-    , test "extcodesize-symbolic" $ do-        Just c <- solcRuntime "C"-          [i|-            contract C {-              function foo(address a, uint x) public {-               require(x > 10);-                uint size;-                assembly {-                  size := extcodesize(a)-                }-                assert(x >= 5);-              }-            }-          |]-        let sig = (Just $ Sig "foo(address,uint256)" [AbiAddressType, AbiUIntType 256])-        (e, res) <- withDefaultSolver $-          \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        liftIO $ printWarnings Nothing e res "the contracts under test"-        assertEqualM "Must be QED" res []-    , test "extcodesize-symbolic2" $ do-        Just c <- solcRuntime "C"-          [i|-            contract C {-              function foo(address a, uint x) public {-                uint size;-                assembly {-                  size := extcodesize(a)-                }-                assert(size > 5);-              }-            }-          |]-        let sig = (Just $ Sig "foo(address,uint256)" [AbiAddressType, AbiUIntType 256])-        (e, res@[Cex _]) <- withDefaultSolver $-          \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        liftIO $ printWarnings Nothing e res "the contracts under test"-    , test "jump-into-symbolic-region" $ do-        let-          -- our initCode just jumps directly to the end-          code = BS.pack . mapMaybe maybeLitByteSimp $ V.toList $ assemble-              [ OpPush (Lit 0x85)-              , OpJump-              , OpPush (Lit 1)-              , OpPush (Lit 1)-              , OpPush (Lit 1)-              , OpJumpdest-              ]-          -- we write a symbolic word to the middle, so the jump above should-          -- fail since the target is not in the concrete region-          initCode = (WriteWord (Lit 0x43) (Var "HI") (ConcreteBuf code), [])--          -- we pass in the above initCode buffer as calldata, and then copy-          -- it into memory before calling Create-          runtimecode = RuntimeCode (SymbolicRuntimeCode $ assemble-              [ OpPush (Lit 0x85)-              , OpPush (Lit 0x0)-              , OpPush (Lit 0x0)-              , OpCalldatacopy-              , OpPush (Lit 0x85)-              , OpPush (Lit 0x0)-              , OpPush (Lit 0x0)-              , OpCreate-              ])-        withDefaultSolver $ \s -> do-          vm <- liftIO $ stToIO $ loadSymVM runtimecode (Lit 0) initCode False-          let iterConf = IterConfig {maxIter=Nothing, askSmtIters=1, loopHeuristic=StackBased }-          paths <- interpret (Fetch.noRpcFetcher s) iterConf vm runExpr pure-          let exprSimp = map Expr.simplify paths-          assertBoolM "expected partial execution" (any isPartial exprSimp)-    ]-  , testGroup "Dapp-Tests"-    [ test "Trivial-Pass" $ do-        let testFile = "test/contracts/pass/trivial.sol"-        runForgeTest testFile ".*" >>= assertEqualM "test result" (True, True)-    , test "Foundry" $ do-        -- quick smokecheck to make sure that we can parse ForgeStdLib style build outputs-        -- return is a pair of (No Cex, No Warnings)-        let cases =-              [ ("test/contracts/pass/trivial.sol",       ".*", (True, True))-              , ("test/contracts/pass/dsProvePass.sol",   ".*", (True, True))-              , ("test/contracts/pass/revertEmpty.sol",   ".*", (True, True))-              , ("test/contracts/pass/revertString.sol",  ".*", (True, True))-              , ("test/contracts/pass/requireEmpty.sol",  ".*", (True, True))-              , ("test/contracts/pass/requireString.sol", ".*", (True, True))-              -- overapproximation-              , ("test/contracts/pass/no-overapprox-staticcall.sol", ".*", (True, True))-              , ("test/contracts/pass/no-overapprox-delegatecall.sol", ".*", (True, True))-              -- failure cases-              , ("test/contracts/fail/trivial.sol",       ".*", (False, False))-              , ("test/contracts/fail/dsProveFail.sol",   "prove_add", (False, True))-              , ("test/contracts/fail/dsProveFail.sol",   "prove_multi", (False, True))-              -- all branches revert, which is a warning-              , ("test/contracts/fail/dsProveFail.sol",   "prove_trivial.*", (False, False))-              , ("test/contracts/fail/dsProveFail.sol",   "prove_distributivity", (False, True))-              , ("test/contracts/fail/assertEq.sol",      ".*", (False, True))-              -- bad cheatcode detected, hence the warning-              , ("test/contracts/fail/bad-cheatcode.sol", ".*", (False, False))-              -- symbolic failures -- either the text or the selector is symbolic-              , ("test/contracts/fail/symbolicFail.sol",      "prove_conc_fail_allrev.*", (False, False))-              , ("test/contracts/fail/symbolicFail.sol",      "prove_conc_fail_somerev.*", (False, True))-              , ("test/contracts/fail/symbolicFail.sol",      "prove_symb_fail_allrev_text.*", (False, False))-              , ("test/contracts/fail/symbolicFail.sol",      "prove_symb_fail_somerev_text.*", (False, True))-              , ("test/contracts/fail/symbolicFail.sol",      "prove_symb_fail_allrev_selector.*", (False, False))-              , ("test/contracts/fail/symbolicFail.sol",      "prove_symb_fail_somerev_selector.*", (False, True))]-        forM_ cases $ \(testFile, match, expected) -> do-          actual <- runForgeTestCustom testFile match Nothing Nothing False Fetch.noRpc-          putStrLnM $ "Test result for " <> testFile <> " match: " <> T.unpack match <> ": " <> show actual-          assertEqualM "Must match" expected actual-    , test "Trivial-Fail" $ do-        let testFile = "test/contracts/fail/trivial.sol"-        runForgeTest testFile "prove_false" >>= assertEqualM "test result" (False, False)-    , test "Abstract" $ do-        let testFile = "test/contracts/pass/abstract.sol"-        runForgeTest testFile ".*" >>= assertEqualM "test result" (True, True)-    , test "Constantinople" $ do-        let testFile = "test/contracts/pass/constantinople.sol"-        runForgeTest testFile ".*" >>= assertEqualM "test result" (True, True)-    , test "ConstantinopleMin" $ do-        let testFile = "test/contracts/pass/constantinople_min.sol"-        runForgeTest testFile ".*" >>= assertEqualM "test result" (True, True)-    , test "Prove-Tests-Pass" $ do-        let testFile = "test/contracts/pass/dsProvePass.sol"-        runForgeTest testFile ".*" >>= assertEqualM "test result" (True, True)-    , test "prefix-check-for-dapp" $ do-        let testFile = "test/contracts/fail/check-prefix.sol"-        runForgeTest testFile "prove_trivial" >>= assertEqualM "test result" (False, False)-    , test "transfer-dapp" $ do-        let testFile = "test/contracts/pass/transfer.sol"-        runForgeTest testFile "prove_transfer" >>= assertEqualM "should prove transfer" (True, True)-    , test "nonce-issues" $ do-        let testFile = "test/contracts/pass/nonce-issues.sol"-        runForgeTest testFile "prove_prank_addr_exists" >>= assertEqualM "should not bail" (True, True)-        runForgeTest testFile "prove_nonce_addr_nonexistent" >>= assertEqualM "should not bail" (True, True)-    , test "Prove-Tests-Fail" $ do-        let testFile = "test/contracts/fail/dsProveFail.sol"-        runForgeTest testFile "prove_trivial" >>= assertEqualM "prove_trivial" (False, False)-        runForgeTest testFile "prove_trivial_dstest" >>= assertEqualM "prove_trivial_dstest" (False, False)-        runForgeTest testFile "prove_add" >>= assertEqualM "prove_add" (False, True)-        runForgeTestCustom testFile "prove_smtTimeout" (Just 1) Nothing False Fetch.noRpc-          >>= assertEqualM "prove_smtTimeout" (True, False)-        runForgeTest testFile "prove_multi" >>= assertEqualM "prove_multi" (False, True)-        runForgeTest testFile "prove_distributivity" >>= assertEqualM "prove_distributivity" (False, True)-    , test "Loop-Tests" $ do-        let testFile = "test/contracts/pass/loops.sol"-        runForgeTestCustom testFile "prove_loop" Nothing (Just 10) False Fetch.noRpc  >>= assertEqualM "test result" (True, False)-        runForgeTestCustom testFile "prove_loop" Nothing (Just 100) False Fetch.noRpc >>= assertEqualM "test result" (False, False)-    , test "Cheat-Codes-Pass" $ do-        let testFile = "test/contracts/pass/cheatCodes.sol"-        runForgeTest testFile ".*" >>= assertEqualM "test result" (True, False)-    , test "Cheat-Codes-Fork-Pass" $ do-        let testFile = "test/contracts/pass/cheatCodesFork.sol"-        runForgeTest testFile ".*" >>= assertEqualM "test result" (True, True)-    , test "Unwind" $ do-        let testFile = "test/contracts/pass/unwind.sol"-        runForgeTest testFile ".*" >>= assertEqualM "test result" (True, True)-    , test "Keccak" $ do-        let testFile = "test/contracts/pass/keccak.sol"-        runForgeTest testFile "prove_access" >>= assertEqualM "test result" (True, True)-    ]-  , testGroup "max-iterations"-    [ test "concrete-loops-reached" $ do-        Just c <- solcRuntime "C"-            [i|-            contract C {-              function fun() external payable returns (uint) {-                uint count = 0;-                for (uint i = 0; i < 5; i++) count++;-                return count;-              }-            }-            |]-        let sig = Just $ Sig "fun()" []-            opts = (defaultVeriOpts :: VeriOpts) { iterConf = defaultIterConf {maxIter = Just 3 }}-        (e, []) <- withDefaultSolver $-          \s -> checkAssert s defaultPanicCodes c sig [] opts-        assertBoolM "The expression is not partial" $ any isPartial e-    , test "concrete-loops-not-reached" $ do-        Just c <- solcRuntime "C"-            [i|-            contract C {-              function fun() external payable returns (uint) {-                uint count = 0;-                for (uint i = 0; i < 5; i++) count++;-                return count;-              }-            }-            |]--        let sig = Just $ Sig "fun()" []-            opts = (defaultVeriOpts :: VeriOpts) { iterConf = defaultIterConf {maxIter = Just 6 }}-        (e, []) <- withDefaultSolver $-          \s -> checkAssert s defaultPanicCodes c sig [] opts-        assertBoolM "The expression is partial" $ not $ any isPartial e-    , test "symbolic-loops-reached" $ do-        Just c <- solcRuntime "C"-            [i|-            contract C {-              function fun(uint j) external payable returns (uint) {-                uint count = 0;-                for (uint i = 0; i < j; i++) count++;-                return count;-              }-            }-            |]-        let veriOpts = (defaultVeriOpts :: VeriOpts) { iterConf = defaultIterConf { maxIter = Just 5 }}-        (e, []) <- withDefaultSolver $-          \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] veriOpts-        assertBoolM "The expression MUST be partial" $ any (Expr.containsNode isPartial) e-    , test "inconsistent-paths" $ do-        Just c <- solcRuntime "C"-            [i|-            contract C {-              function fun(uint j) external payable returns (uint) {-                require(j <= 3);-                uint count = 0;-                for (uint i = 0; i < j; i++) count++;-                return count;-              }-            }-            |]-        let sig = Just $ Sig "fun(uint256)" [AbiUIntType 256]-            -- we don't ask the solver about the loop condition until we're-            -- already in an inconsistent path (i == 5, j <= 3, i < j), so we-            -- will continue looping here until we hit max iterations-            opts = (defaultVeriOpts :: VeriOpts) { iterConf = defaultIterConf { maxIter = Just 10, askSmtIters = 5 }}-        (e, []) <- withDefaultSolver $-          \s -> checkAssert s defaultPanicCodes c sig [] opts-        assertBoolM "The expression MUST be partial" $ any (Expr.containsNode isPartial) e-    , test "mem-tuple" $ do-        Just c <- solcRuntime "C"-          [i|-            contract C {-              struct Pair {-                uint x;-                uint y;-              }-              function prove_tuple_pass(Pair memory p) public pure {-                uint256 f = p.x;-                uint256 g = p.y;-                unchecked {-                  p.x+=p.y;-                  assert(p.x == (f + g));-                }-              }-            }-          |]-        let opts = defaultVeriOpts-        let sig = Just $ Sig "prove_tuple_pass((uint256,uint256))" [AbiTupleType (V.fromList [AbiUIntType 256, AbiUIntType 256])]-        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] opts-        putStrLnM "Qed, memory tuple is good"-    , test "symbolic-loops-not-reached" $ do-        Just c <- solcRuntime "C"-            [i|-            contract C {-              function fun(uint j) external payable returns (uint) {-                require(j <= 3);-                uint count = 0;-                for (uint i = 0; i < j; i++) count++;-                return count;-              }-            }-            |]-        let sig = Just $ Sig "fun(uint256)" [AbiUIntType 256]-            -- askSmtIters is low enough here to avoid the inconsistent path-            -- conditions, so we never hit maxIters-            opts = (defaultVeriOpts :: VeriOpts) { iterConf = defaultIterConf {maxIter = Just 5, askSmtIters = 1 }}-        (e, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] opts-        assertBoolM "The expression MUST NOT be partial" $ not (any (Expr.containsNode isPartial) e)-    ]-  , testGroup "Symbolic Addresses"-    -- TODO ignore only because Martin has a fix for this-- it should not be using `verify`-    [ test "symbolic-address-create" $ do-        let src = [i|-                  contract A {-                    constructor() payable {}-                  }-                  contract C {-                    function fun(uint256 a) external{-                      require(address(this).balance > a);-                      new A{value:a}();-                    }-                  }-                  |]-        Just a <- solcRuntime "A" src-        Just c <- solcRuntime "C" src-        let sig = Sig "fun(uint256)" [AbiUIntType 256]-        paths <- withDefaultSolver $ \s -> exploreContract s c (Just sig) [] defaultVeriOpts Nothing-        let isSuc (Success {}) = True-            isSuc _ = False-        case filter isSuc paths of-          [Success _ _ _ store] -> do-            let ca = fromJust (Map.lookup (SymAddr "freshSymAddr1") store)-            let code = case ca.code of-                  RuntimeCode (ConcreteRuntimeCode c') -> c'-                  _ -> internalError "expected concrete code"-            assertEqualM "balance mismatch" (Var "arg1") (Expr.simplify ca.balance)-            assertEqualM "code mismatch" (stripBytecodeMetadata a) (stripBytecodeMetadata code)-            assertEqualM "nonce mismatch" (Just 1) ca.nonce-          _ -> assertBoolM "too many/too few success nodes!" False-    , test "symbolic-balance-call" $ do-        let src = [i|-                  contract A {-                    function f() public payable returns (uint) {-                      return msg.value;-                    }-                  }-                  contract C {-                    function fun(uint256 x) external {-                      require(address(this).balance > x);-                      A a = new A();-                      uint res = a.f{value:x}();-                      assert(res == x);-                    }-                  }-                  |]-        Just c <- solcRuntime "C" src-        res <- reachableUserAsserts c Nothing-        assertBoolM "unexpected cex" (isRight res)-    , test "deployed-contract-addresses-cannot-alias1" $ do-        Just c <- solcRuntime "C"-          [i|-            contract A {}-            contract C {-              function f() external {-                A a = new A();-                uint256 addr = uint256(uint160(address(a)));-                uint256 addr2 = uint256(uint160(address(this)));-                assert(addr != addr2);-              }-            }-          |]-        res <- reachableUserAsserts c Nothing-        assertBoolM "should not be able to alias" (isRight res)-    , test "deployed-contract-addresses-cannot-alias2" $ do-        Just c <- solcRuntime "C"-          [i|-            contract A {}-            contract C {-              function f() external {-                A a = new A();-                assert(address(a) != address(this));-              }-            }-          |]-        res <- reachableUserAsserts c Nothing-        assertBoolM "should not be able to alias" (isRight res)-    , test "addresses-in-args-can-alias-anything" $ do-        let addrs :: [Text]-            addrs = ["address(this)", "tx.origin", "block.coinbase", "msg.sender"]-            sig = Just $ Sig "f(address)" [AbiAddressType]-            checkVs vs = [i|-                           contract C {-                             function f(address a) external {-                               if (${vs} == a) assert(false);-                             }-                           }-                         |]--        [self, origin, coinbase, caller] <- forM addrs $ \addr -> do-          Just c <- solcRuntime "C" (checkVs addr)-          Left [cex] <- reachableUserAsserts c sig-          pure cex.addrs--        liftIO $ do-          let check as a = (Map.lookup (SymAddr "arg1") as) @?= (Map.lookup a as)-          check self (SymAddr "entrypoint")-          check origin (SymAddr "origin")-          check coinbase (SymAddr "coinbase")-          check caller (SymAddr "caller")-    , test "addresses-in-args-can-alias-themselves" $ do-        Just c <- solcRuntime "C"-          [i|-            contract C {-              function f(address a, address b) external {-                if (a == b) assert(false);-              }-            }-          |]-        let sig = Just $ Sig "f(address,address)" [AbiAddressType,AbiAddressType]-        Left [cex] <- reachableUserAsserts c sig-        let arg1 = fromJust $ Map.lookup (SymAddr "arg1") cex.addrs-            arg2 = fromJust $ Map.lookup (SymAddr "arg1") cex.addrs-        assertEqualM "should match" arg1 arg2-    -- TODO: fails due to missing aliasing rules-    , expectFail $ test "tx.origin cannot alias deployed contracts" $ do-        Just c <- solcRuntime "C"-          [i|-            contract A {}-            contract C {-              function f() external {-                address a = address(new A());-                if (tx.origin == a) assert(false);-              }-            }-          |]-        cexs <- reachableUserAsserts c Nothing-        assertBoolM "unexpected cex" (isRight cexs)-    , test "tx.origin can alias everything else" $ do-        let addrs = ["address(this)", "block.coinbase", "msg.sender", "arg"] :: [Text]-            sig = Just $ Sig "f(address)" [AbiAddressType]-            checkVs vs = [i|-                           contract C {-                             function f(address arg) external {-                               if (${vs} == tx.origin) assert(false);-                             }-                           }-                         |]--        [self, coinbase, caller, arg] <- forM addrs $ \addr -> do-          Just c <- solcRuntime "C" (checkVs addr)-          Left [cex] <- reachableUserAsserts c sig-          pure cex.addrs--        liftIO $ do-          let check as a = (Map.lookup (SymAddr "origin") as) @?= (Map.lookup a as)-          check self (SymAddr "entrypoint")-          check coinbase (SymAddr "coinbase")-          check caller (SymAddr "caller")-          check arg (SymAddr "arg1")-    , test "coinbase can alias anything" $ do-        let addrs = ["address(this)", "tx.origin", "msg.sender", "a", "arg"] :: [Text]-            sig = Just $ Sig "f(address)" [AbiAddressType]-            checkVs vs = [i|-                           contract A {}-                           contract C {-                             function f(address arg) external {-                               address a = address(new A());-                               if (${vs} == block.coinbase) assert(false);-                             }-                           }-                         |]--        [self, origin, caller, a, arg] <- forM addrs $ \addr -> do-          Just c <- solcRuntime "C" (checkVs addr)-          Left [cex] <- reachableUserAsserts c sig-          pure cex.addrs--        liftIO $ do-          let check as a' = (Map.lookup (SymAddr "coinbase") as) @?= (Map.lookup a' as)-          check self (SymAddr "entrypoint")-          check origin (SymAddr "origin")-          check caller (SymAddr "caller")-          check a (SymAddr "freshSymAddr1")-          check arg (SymAddr "arg1")-    , test "caller can alias anything" $ do-        let addrs = ["address(this)", "tx.origin", "block.coinbase", "a", "arg"] :: [Text]-            sig = Just $ Sig "f(address)" [AbiAddressType]-            checkVs vs = [i|-                           contract A {}-                           contract C {-                             function f(address arg) external {-                               address a = address(new A());-                               if (${vs} == msg.sender) assert(false);-                             }-                           }-                         |]--        [self, origin, coinbase, a, arg] <- forM addrs $ \addr -> do-          Just c <- solcRuntime "C" (checkVs addr)-          Left [cex] <- reachableUserAsserts c sig-          pure cex.addrs--        liftIO $ do-          let check as a' = (Map.lookup (SymAddr "caller") as) @?= (Map.lookup a' as)-          check self (SymAddr "entrypoint")-          check origin (SymAddr "origin")-          check coinbase (SymAddr "coinbase")-          check a (SymAddr "freshSymAddr1")-          check arg (SymAddr "arg1")-    , test "vm.load fails for a potentially aliased address" $ do-        Just c <- solcRuntime "C"-          [i|-            interface Vm {-              function load(address,bytes32) external returns (bytes32);-            }-            contract C {-              function f() external {-                Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);-                vm.load(msg.sender, 0x0);-              }-            }-          |]-        -- NOTE: we have a postcondition here, not just a regular verification-        (_, [Cex _]) <- withDefaultSolver $ \s ->-          verifyContract s c Nothing [] defaultVeriOpts Nothing (checkBadCheatCode "load(address,bytes32)")-        pure ()-    , test "vm.store fails for a potentially aliased address" $ do-        Just c <- solcRuntime "C"-          [i|-            interface Vm {-                function store(address,bytes32,bytes32) external;-            }-            contract C {-              function f() external {-                Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);-                vm.store(msg.sender, 0x0, 0x0);-              }-            }-          |]-        -- NOTE: we have a postcondition here, not just a regular verification-        (_, [Cex _]) <- withDefaultSolver $ \s ->-          verifyContract s c Nothing [] defaultVeriOpts Nothing (checkBadCheatCode "store(address,bytes32,bytes32)")-        pure ()-    -- TODO: make this work properly-    , test "transfering-eth-does-not-dealias" $ do-        Just c <- solcRuntime "C"-          [i|-            // we can't do calls to unknown code yet so we use selfdestruct-            contract Send {-              constructor(address payable dst) payable {-                selfdestruct(dst);-              }-            }-            contract C {-              function f() external {-                uint preSender = msg.sender.balance;-                uint preOrigin = tx.origin.balance;--                new Send{value:10}(payable(msg.sender));-                new Send{value:5}(payable(tx.origin));--                if (msg.sender == tx.origin) {-                  assert(preSender == preOrigin-                      && msg.sender.balance == preOrigin + 15-                      && tx.origin.balance == preSender + 15);-                } else {-                  assert(msg.sender.balance == preSender + 10-                      && tx.origin.balance == preOrigin + 5);-                }-              }-            }-          |]-        Right e <- reachableUserAsserts c Nothing-        -- TODO: this should work one day-        assertBoolM "should be partial" (any isPartial e)-    , test "symbolic-addresses-cannot-be-zero-or-precompiles" $ do-        let addrs = [T.pack . show . Addr $ a | a <- [0x0..0x09]]-            mkC a = fromJust <$> solcRuntime "A"-              [i|-                contract A {-                  function f() external {-                    assert(msg.sender != address(${a}));-                  }-                }-              |]-        codes <- mapM mkC addrs-        results <- mapM (flip reachableUserAsserts (Just (Sig "f()" []))) codes-        let ok = and $ fmap (isRight) results-        assertBoolM "unexpected cex" ok-    , test "addresses-in-context-are-symbolic" $ do-        Just a <- solcRuntime "A"-          [i|-            contract A {-              function f() external {-                assert(msg.sender != address(0x10));-              }-            }-          |]-        Just b <- solcRuntime "B"-          [i|-            contract B {-              function f() external {-                assert(block.coinbase != address(0x11));-              }-            }-          |]-        Just c <- solcRuntime "C"-          [i|-            contract C {-              function f() external {-                assert(tx.origin != address(0x12));-              }-            }-          |]-        Just d <- solcRuntime "D"-          [i|-            contract D {-              function f() external {-                assert(address(this) != address(0x13));-              }-            }-          |]-        [acex,bcex,ccex,dcex] <- forM [a,b,c,d] $ \con -> do-          Left [cex] <- reachableUserAsserts con Nothing-          assertEqualM "wrong number of addresses" 1 (length (Map.keys cex.addrs))-          pure cex--        -- Lowest allowed address is 0x10 due to reserved addresses up to 0x9-        assertEqualM "wrong model for a" (Addr 0x10) (fromJust $ Map.lookup (SymAddr "caller") acex.addrs)-        assertEqualM "wrong model for b" (Addr 0x11) (fromJust $ Map.lookup (SymAddr "coinbase") bcex.addrs)-        assertEqualM "wrong model for c" (Addr 0x12) (fromJust $ Map.lookup (SymAddr "origin") ccex.addrs)-        assertEqualM "wrong model for d" (Addr 0x13) (fromJust $ Map.lookup (SymAddr "entrypoint") dcex.addrs)-    ]-  , testGroup "Symbolic execution"-      [-     test "require-test" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(int256 a) external pure {-              require(a <= 0);-              assert (a <= 0);-              }-             }-            |]-        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256)" [AbiIntType 256])) [] defaultVeriOpts-        putStrLnM "Require works as expected"-     , test "symbolic-block-number" $ do-       Just c <- solcRuntime "C" [i|-           interface Vm {-               function roll(uint) external;-           }-           contract C {-             function myfun(uint x, uint y) public {-               Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);-               vm.roll(x);-               assert(block.number == y);-             }-           } |]-       (e, [Cex _]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts-       assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial e)-     , test "symbolic-to-concrete-multi" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            interface Vm {-              function deal(address,uint256) external;-            }-            contract MyContract {-              function fun(uint160 a) external {-                Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);-                uint160 c = 10 + (a % 2);-                address b = address(c);-                vm.deal(b, 10);-              }-             }-            |]-        let sig = Just (Sig "fun(uint160)" [AbiUIntType 160])-        (e, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        assertBoolM "The expression is not partial" $ Prelude.not (any isPartial e)-     ,-     -- here test-     test "ITE-with-bitwise-AND" $ do-       Just c <- solcRuntime "C"-         [i|-         contract C {-           function f(uint256 x) public pure {-             require(x > 0);-             uint256 a = (x & 8);-             bool w;-             // assembly is needed here, because solidity doesn't allow uint->bool conversion-             assembly {-                 w:=a-             }-             if (!w) assert(false); //we should get a CEX: when x has a 0 at bit 3-           }-         }-         |]-       -- should find a counterexample-       (_, [Cex _]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-       putStrLnM "expected counterexample found"-     ,-     test "ITE-with-bitwise-OR" $ do-       Just c <- solcRuntime "C"-         [i|-         contract C {-           function f(uint256 x) public pure {-             uint256 a = (x | 8);-             bool w;-             // assembly is needed here, because solidity doesn't allow uint->bool conversion-             assembly {-                 w:=a-             }-             assert(w); // due to bitwise OR with positive value, this must always be true-           }-         }-         |]-       (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-       putStrLnM "this should always be true, due to bitwise OR with positive value"-     ,-     test "abstract-returndata-size" $ do-       Just c <- solcRuntime "C"-         [i|-         contract C {-           function f(uint256 x) public pure {-             assembly {-                 return(0, x)-             }-           }-         }-         |]-       paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "f(uint256)" [])) [] defaultVeriOpts-       assertBoolM "The expression is partial" $ Prelude.not (any isPartial paths)-    ,-    -- CopySlice check-    -- uses identity precompiled contract (0x4) to copy memory-    -- checks 9af114613075a2cd350633940475f8b6699064de (readByte + CopySlice had src/dest mixed up)-    -- without 9af114613 it dies with: `Exception: UnexpectedSymbolicArg 296 "MSTORE index"`-    --       TODO: check  9e734b9da90e3e0765128b1f20ce1371f3a66085 (bufLength + copySlice was off by 1)-    test "copyslice-check" $ do-      Just c <- solcRuntime "C"-        [i|-        contract C {-          function checkval(uint8 a) public {-            bytes memory data = new bytes(5);-            for(uint i = 0; i < 5; i++) data[i] = bytes1(a);-            bytes memory ret = new bytes(data.length);-            assembly {-                let len := mload(data)-                if iszero(call(0xff, 0x04, 0, add(data, 0x20), len, add(ret,0x20), len)) {-                    invalid()-                }-            }-            for(uint i = 0; i < 5; i++) assert(ret[i] == data[i]);-          }-        }-        |]-      let sig = Just (Sig "checkval(uint8)" [AbiUIntType 8])-      (res, []) <- withDefaultSolver $ \s ->-        checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-      putStrLnM $ "successfully explored " <> show (length res) <> " paths"-    , test "staticcall-check-orig" $ do-      Just c <- solcRuntime "C"-        [i|-        contract Target {-            function add(uint256 x, uint256 y) external pure returns (uint256) {-              unchecked {-                return x + y;-              }-            }-        }--        contract C {-            function checkval(uint256 x, uint256 y) public {-                Target t = new Target();-                address realAddr = address(t);-                bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);-                (bool success, bytes memory returnData) = realAddr.staticcall(data);-                assert(success);--                uint result = abi.decode(returnData, (uint256));-                uint expected;-                unchecked {-                  expected = x + y;-                }-                assert(result == expected);-            }-        }-        |]-      let sig = Just (Sig "checkval(uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])-      (res, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-      putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-      let numCexes = sum $ map (fromEnum . isCex) ret-      let numErrs = sum $ map (fromEnum . isError) ret-      assertEqualM "number of counterexamples" 0 numCexes-      assertEqualM "number of errors" 0 numErrs-    , test "staticcall-check-orig2" $ do-      Just c <- solcRuntime "C"-        [i|-        contract Target {-            function add(uint256 x, uint256 y) external pure returns (uint256) {-              assert(1 == 0);-            }-        }-        contract C {-            function checkval(uint256 x, uint256 y) public {-                Target t = new Target();-                address realAddr = address(t);-                bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);-                (bool success, bytes memory returnData) = realAddr.staticcall(data);-                assert(success);-            }-        }-        |]-      let sig = Just (Sig "checkval(uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])-      (res, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-      putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-      assertBoolM "The expression is NOT partial" $ Prelude.not (any isPartial res)-      let numCexes = sum $ map (fromEnum . isCex) ret-      let numErrs = sum $ map (fromEnum . isError) ret-      assertEqualM "number of counterexamples" 1 numCexes-      assertEqualM "number of errors" 0 numErrs-    , test "copyslice-symbolic-ok" $ do-      Just c <- solcRuntime "C"-        [i|-         contract Target {-           function get(address addr) external view returns (uint256) {-               return 55;-           }-         }-         contract C {-           function retFor(address addr) public returns (uint256) {-               Target mm = new Target();-               uint256 ret = mm.get(addr);-               assert(ret == 4);-               return ret;-           }-         }-        |]-      let sig2 = Just (Sig "retFor(address)" [AbiAddressType])-      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig2 [] defaultVeriOpts-      putStrLnM $ "successfully explored: " <> show (length paths) <> " paths"-      assertBoolM "The expression is NOT error" $ not $ any isError ret-      assertBoolM "The expression is NOT partial" $ not (any isPartial paths)-    , test "no-overapprox-when-present" $ do-      Just c <- solcRuntime "C" [i|-        contract ERC20 {-          function f() public {-          }-        }--        contract C {-          address token;--          function no_overapp() public {-            token = address(new ERC20());-            token.delegatecall(abi.encodeWithSignature("f()"));-          }-        } |]-      let sig2 = Just (Sig "no_overapp()" [])-      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig2 [] defaultVeriOpts-      -- putStrLnM $ "paths: " <> show paths-      putStrLnM $ "successfully explored: " <> show (length paths) <> " paths"-      assertBoolM "The expression is NOT error" $ not $ any isError ret-      assertBoolM "The expression is NOT partial" $ not (any isPartial paths)-      let numCexes = sum $ map (fromEnum . isCex) ret-      assertEqualM "number of counterexamples" 0 numCexes-    -- NOTE: below used to be symbolic copyslice copy error before new copyslice-    --       simplifications in Expr.simplify-    , test "overapproximates-undeployed-contract-symbolic" $ do-      Just c <- solcRuntime "C"-        [i|-         contract Target {-           function get(address addr) external view returns (uint256) {-               return 55;-           }-         }-         contract C {-           Target mm;-           function retFor(address addr) public returns (uint256) {-               // NOTE: this is symbolic execution, and no setUp has been ran-               //       hence, this below calls unknown code! It's trying to load:-               //       (SLoad (Lit 0x0) (AbstractStore (SymAddr "entrypoint") Nothing))-               //       So it overapproximates.-               uint256 ret = mm.get(addr);-               assert(ret == 4);-               return ret;-           }-         }-        |]-      let sig2 = Just (Sig "retFor(address)" [AbiAddressType])-      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig2 [] defaultVeriOpts-      putStrLnM $ "successfully explored: " <> show (length paths) <> " paths"-      assertBoolM "The expression is NOT error" $ not $ any isError ret-      assertBoolM "The expression is NOT partial" $ not (any isPartial paths)-      let numCexes = sum $ map (fromEnum . isCex) ret-      -- There are 2 CEX-es-      -- This is because with one CEX, the return DATA-      -- is empty, and in the other, the return data is non-empty (but symbolic)-      assertEqualM "number of counterexamples" 2 numCexes-    , test "overapproximates-unknown-addr" $ do-      Just c <- solcRuntime "C"-        [i|-         contract Target {-           function get() external view returns (uint256) {-               return 55;-           }-         }-         contract C {-           Target mm;-           function retFor(address addr) public returns (uint256) {-               Target target = Target(addr);-               uint256 ret = target.get();-               assert(ret == 4);-               return ret;-           }-         }-        |]-      let sig2 = Just (Sig "retFor(address)" [AbiAddressType])-      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig2 [] defaultVeriOpts-      putStrLnM $ "successfully explored: " <> show (length paths) <> " paths"-      assertBoolM "The expression is NOT error" $ not $ any isError ret-      let numCexes = sum $ map (fromEnum . isCex) ret-      assertBoolM "The expression is NOT partial" $ not (any isPartial paths)-      -- There are 2 CEX-es-      -- This is because with one CEX, the return DATA-      -- is empty, and in the other, the return data is non-empty (but symbolic)-      assertEqualM "number of counterexamples" 2 numCexes-    , test "overapproximates-fixed-zero-addr" $ do-      Just c <- solcRuntime "C"-        [i|-         contract Target {-           function get() external view returns (uint256) {-               return 55;-           }-         }-         contract C {-           Target mm;-           function retFor() public returns (uint256) {-               Target target = Target(address(0));-               uint256 ret = target.get();-               assert(ret == 4);-               return ret;-           }-         }-        |]-      let sig2 = Just (Sig "retFor()" [])-      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig2 [] defaultVeriOpts-      putStrLnM $ "successfully explored: " <> show (length paths) <> " paths"-      assertBoolM "The expression is NOT error" $ not $ any isError ret-      let numCexes = sum $ map (fromEnum . isCex) ret-      assertBoolM "The expression is NOT partial" $ not (any isPartial paths)-      -- There are 2 CEX-es-      -- This is because with one CEX, the return DATA-      -- is empty, and in the other, the return data is non-empty (but symbolic)-      assertEqualM "number of counterexamples" 2 numCexes-    , test "overapproximates-fixed-wrong-addr" $ do-      Just c <- solcRuntime "C"-        [i|-         contract Target {-           function get() external view returns (uint256) {-               return 55;-           }-         }-         contract C {-           Target mm;-           function retFor() public returns (uint256) {-               Target target = Target(address(0xacab));-               uint256 ret = target.get();-               assert(ret == 4);-               return ret;-           }-         }-        |]-      let sig2 = Just (Sig "retFor()" [])-      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig2 [] defaultVeriOpts-      putStrLnM $ "successfully explored: " <> show (length paths) <> " paths"-      assertBoolM "The expression is NOT error" $ not $ any isError ret-      assertBoolM "The expression is NOT partial" $ not (any isPartial paths)-      let numCexes = sum $ map (fromEnum . isCex) ret-      -- There are 2 CEX-es-      -- This is because with one CEX, the return DATA-      -- is empty, and in the other, the return data is non-empty (but symbolic)-      assertEqualM "number of counterexamples" 2 numCexes-    , test "staticcall-no-overapprox-2" $ do-      Just c <- solcRuntime "C"-        [i|-        contract Target {-            function add(uint256 x, uint256 y) external pure returns (uint256) {-              unchecked {-                return x + y;-              }-            }-        }-        contract C {-            function checkval(uint256 x, uint256 y) public {-                Target t = new Target();-                address realAddr = address(t);-                bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);-                (bool success, bytes memory returnData) = realAddr.staticcall(data);-                assert(success);-                assert(returnData.length == 32);--                // Decode the return value-                uint256 result = abi.decode(returnData, (uint256));--                // Assert that the result is equal to x + y-                unchecked {-                  assert(result == x + y);-                }-            }-        }-        |]-      let sig = Just (Sig "checkval(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])-      (res, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-      putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-      assertBoolM "The expression is NOT partial" $ not (any isPartial res)-      assertBoolM "The expression is NOT unknown" $ not $ any isUnknown ret-      assertBoolM "The expression is NOT error" $ not $ any isError ret-      let numCexes = sum $ map (fromEnum . isCex) ret-      let numErrs = sum $ map (fromEnum . isError) ret-      assertEqualM "number of counterexamples" 0 numCexes-      assertEqualM "number of errors" 0 numErrs-    , test "staticcall-check-symbolic1" $ do-      Just c <- solcRuntime "C"-        [i|-        contract C {-            function checkval(address inputAddr, uint256 x, uint256 y) public {-                bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);-                (bool success, bytes memory returnData) = inputAddr.staticcall(data);-                assert(success);-            }-        }-        |]-      let sig = Just (Sig "checkval(address,uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])-      (res, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-      putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-      let numCexes = sum $ map (fromEnum . isCex) ret-      let numErrs = sum $ map (fromEnum . isError) ret-      -- There are 2 CEX-es, in contrast to the above (staticcall-check-orig2).-      -- This is because with one CEX, the return DATA-      -- is empty, and in the other, the return data is non-empty (but symbolic)-      assertEqualM "number of counterexamples" 2 numCexes-      assertEqualM "number of errors" 0 numErrs-    -- This checks that calling a symbolic address with staticcall will ALWAYS return 0/1-    -- which is the semantic of the EVM. We insert a  constraint over the return value-    -- even when overapproximation is used, as below.-    , test "staticcall-check-symbolic-yul" $ do-      Just c <- solcRuntime "C"-        [i|-        contract C {-            function checkval(address inputAddr, uint256 x, uint256 y) public {-            uint success;-            assembly {-              // Allocate memory for the call data-              let callData := mload(0x40)--              // Function signature for "add(uint256,uint256)" is "0x771602f7"-              mstore(callData, 0x771602f700000000000000000000000000000000000000000000000000000000)--              // Store the parameters x and y-              mstore(add(callData, 4), x)-              mstore(add(callData, 36), y)--              // Perform the static call-              success := staticcall(-                  gas(),          // Forward all available gas-                  inputAddr,      // Address to call-                  callData,       // Input data location-                  68,             // Input data size (4 bytes for function signature + 32 bytes each for x and y)-                  0,              // Output data location (0 means we don't care about the output)-                  0               // Output data size-              )-              }-              assert(success <= 1);-          }-        }-        |]-      let sig = Just (Sig "checkval(address,uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])-      (res, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-      putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-      let numCexes = sum $ map (fromEnum . isCex) ret-      let numErrs = sum $ map (fromEnum . isError) ret-      assertEqualM "number of counterexamples" 0 numCexes -- no counterexamples, because it is always  0/1-      assertEqualM "number of errors" 0 numErrs-    , test "staticcall-check-symbolic2" $ do-      Just c <- solcRuntime "C"-        [i|-        contract C {-            function checkval(address inputAddr, uint256 x, uint256 y) public {-                bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);-                (bool success, bytes memory returnData) = inputAddr.staticcall(data);-                assert(success);--                uint result = abi.decode(returnData, (uint256));-                uint expected;-                unchecked {-                  expected = x + y;-                }-                assert(result == expected);-            }-        }-        |]-      let sig = Just (Sig "checkval(address,uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])-      (res, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-      putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-      let numCexes = sum $ map (fromEnum . isCex) ret-      let numErrs = sum $ map (fromEnum . isError) ret-      assertEqualM "number of counterexamples" 2 numCexes-      assertEqualM "number of errors" 1 numErrs-    , testCase "call-symbolic-noreent" $ do-      let conf = testEnv.config {promiseNoReent = True}-      let myTestEnv :: Env = (testEnv :: Env) {config = conf :: Config}-      runEnv myTestEnv $ do-        Just c <- solcRuntime "C"-          [i|-          contract C {-              function checkval(address inputAddr, uint256 x, uint256 y) public {-                  bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);-                  (bool success, bytes memory returnData) = inputAddr.call(data);-                  assert(success);-              }-          }-          |]-        let sig = Just (Sig "checkval(address,uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])-        (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        let numCexes = sum $ map (fromEnum . isCex) ret-        let numErrs = sum $ map (fromEnum . isError) ret-        assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)-      -- There are 2 CEX-es-      -- This is because with one CEX, the return DATA-      -- is empty, and in the other, the return data is non-empty and success is false-        assertEqualM "number of errors" 0 numErrs-        assertEqualM "number of counterexamples" 2 numCexes-    , test "call-symbolic-reent" $ do-      Just c <- solcRuntime "C"-        [i|-        contract C {-            function checkval(address inputAddr, uint256 x, uint256 y) public {-                bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);-                (bool success, bytes memory returnData) = inputAddr.call(data);-                assert(success);-            }-        }-        |]-      let sig = Just (Sig "checkval(address,uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])-      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-      assertBoolM "The expression MUST be partial due to CALL to unknown code and no promise" (any isPartial paths)-      let numCexes = sum $ map (fromEnum . isCex) ret-      let numErrs = sum $ map (fromEnum . isError) ret-      assertEqualM "number of errors" 0 numErrs-      assertEqualM "number of counterexamples" 0 numCexes-    , testCase "call-symbolic-noreent-maxbufsize16" $ do-      let conf = testEnv.config {promiseNoReent = True, maxBufSize = 4}-      let myTestEnv :: Env = (testEnv :: Env) {config = conf :: Config}-      runEnv myTestEnv $ do-        Just c <- solcRuntime "C"-          [i|-          contract C {-              function checkval(address inputAddr, uint256 x, uint256 y) public {-                  bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);-                  (bool success, bytes memory returnData) = inputAddr.call(data);-                  assert(returnData.length < 16);-              }-          }-          |]-        let sig = Just (Sig "checkval(address,uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])-        (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        let numCexes = sum $ map (fromEnum . isCex) ret-        let numErrs = sum $ map (fromEnum . isError) ret-        assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)-        assertEqualM "number of errors" 0 numErrs-        assertEqualM "number of counterexamples" 0 numCexes-    , testCase "call-symbolic-noreent-maxbufsize16-fail" $ do-      let conf = testEnv.config {promiseNoReent = True, maxBufSize = 20}-      let myTestEnv :: Env = (testEnv :: Env) {config = conf :: Config}-      runEnv myTestEnv $ do-        Just c <- solcRuntime "C"-          [i|-          contract C {-              function checkval(address inputAddr, uint256 x, uint256 y) public {-                  bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);-                  (bool success, bytes memory returnData) = inputAddr.call(data);-                  assert(returnData.length < 16);-              }-          }-          |]-        let sig = Just (Sig "checkval(address,uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])-        (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-        let numCexes = sum $ map (fromEnum . isCex) ret-        let numErrs = sum $ map (fromEnum . isError) ret-        assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)-        assertEqualM "number of errors" 0 numErrs-        assertEqualM "number of counterexamples" 1 numCexes-    , test "call-balance-symb" $ do-      Just c <- solcRuntime "C"-        [i|-        contract C {-            function checkval(address inputAddr) public {-                uint256 balance = inputAddr.balance;-                assert(balance < 10);-            }-        }-        |]-      let sig = Just (Sig "checkval(address)" [AbiAddressType])-      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-      let numCexes = sum $ map (fromEnum . isCex) ret-      let numErrs = sum $ map (fromEnum . isError) ret-      assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)-      assertEqualM "number of errors" 0 numErrs-      assertEqualM "number of counterexamples" 1 numCexes-    , test "call-balance-symb2" $ do-      Just c <- solcRuntime "C"-        [i|-        contract C {-            function checkval() public {-                uint256 balance = address(0xacab).balance;-                assert(balance < 10);-            }-        }-        |]-      let sig = Just (Sig "checkval()" [])-      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-      let numCexes = sum $ map (fromEnum . isCex) ret-      let numErrs = sum $ map (fromEnum . isError) ret-      assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)-      assertEqualM "number of errors" 0 numErrs-      assertEqualM "number of counterexamples" 1 numCexes-    , test "call-balance-concrete-pass" $ do-      Just c <- solcRuntime "C"-        [i|-        interface Vm {-          function deal(address,uint256) external;-        }-        contract Target {-        }-        contract C {-            function checkval() public {-                Target t = new Target();-                Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);-                vm.deal(address(t), 5);-                uint256 balance = address(t).balance;-                assert(balance < 10);-            }-        }-        |]-      let sig = Just (Sig "checkval()" [])-      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-      let numErrs = sum $ map (fromEnum . isError) ret-      assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)-      assertEqualM "number of errors" 0 numErrs-    , test "call-balance-concrete-fail" $ do-      Just c <- solcRuntime "C"-        [i|-        interface Vm {-          function deal(address,uint256) external;-        }-        contract Target {-        }-        contract C {-            function checkval() public {-                Target t = new Target();-                Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);-                vm.deal(address(t), 5);-                uint256 balance = address(t).balance;-                assert(balance < 5);-            }-        }-        |]-      let sig = Just (Sig "checkval()" [])-      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-      let numErrs = sum $ map (fromEnum . isError) ret-      let numCexes = sum $ map (fromEnum . isCex) ret-      assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)-      assertEqualM "number of errors" 0 numErrs-      assertEqualM "number of counterexamples" 1 numCexes-    , test "call-extcodehash-symb1" $ do-      Just c <- solcRuntime "C"-        [i|-        contract C {-            function checkval(address inputAddr) public {-                bytes32 hash = inputAddr.codehash;-                assert(uint(hash) < 10);-            }-        }-        |]-      let sig = Just (Sig "checkval(address)" [AbiAddressType])-      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-      let numCexes = sum $ map (fromEnum . isCex) ret-      let numErrs = sum $ map (fromEnum . isError) ret-      assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)-      assertEqualM "number of errors" 0 numErrs-      assertEqualM "number of counterexamples" 1 numCexes-    , test "call-extcodehash-symb2" $ do-      Just c <- solcRuntime "C"-        [i|-        contract C {-            function checkval() public {-                bytes32 hash = address(0xacab).codehash;-                assert(uint(hash) < 10);-            }-        }-        |]-      let sig = Just (Sig "checkval()" [])-      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-      let numCexes = sum $ map (fromEnum . isCex) ret-      let numErrs = sum $ map (fromEnum . isError) ret-      assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)-      assertEqualM "number of errors" 0 numErrs-      assertEqualM "number of counterexamples" 1 numCexes-    , test "call-extcodehash-concrete-fail" $ do-      Just c <- solcRuntime "C"-        [i|-        contract Target {-        }-        contract C {-            function checkval() public {-                Target t = new Target();-                bytes32 hash = address(t).codehash;-                assert(uint(hash) == 8);-            }-        }-        |]-      let sig = Just (Sig "checkval()" [])-      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-      let numErrs = sum $ map (fromEnum . isError) ret-      let numCexes = sum $ map (fromEnum . isCex) ret-      assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)-      assertEqualM "number of errors" 0 numErrs-      assertEqualM "number of counterexamples" 1 numCexes-    , test "jump-symbolic" $ do-      Just c <- solcRuntime "C"-        [i|-        // Target contract with a view function-        contract Target {-        }--        // Caller contract using staticcall-        contract C {-            function checkval(address inputAddr, uint256 x, uint256 y) public {-                Target t = new Target();-                address realAddr = address(t);--                bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);-                (bool success, bytes memory returnData) = inputAddr.staticcall(data);-                assert(success == true);-            }-        }-        |]-      let sig = Just (Sig "checkval(address,uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])-      (res, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-      putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-      let numCexes = sum $ map (fromEnum . isCex) ret-      let numErrs = sum $ map (fromEnum . isError) ret-      assertEqualM "number of counterexamples" numCexes 2-      assertEqualM "number of symbolic copy errors" numErrs 0-     ,-     test "opcode-mul-assoc" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(int256 a, int256 b, int256 c) external pure {-              int256 tmp1;-              int256 out1;-              int256 tmp2;-              int256 out2;-              assembly {-                tmp1 := mul(a, b)-                out1 := mul(tmp1,c)-                tmp2 := mul(b, c)-                out2 := mul(a, tmp2)-              }-              assert (out1 == out2);-              }-             }-            |]-        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256,int256)" [AbiIntType 256, AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts-        putStrLnM "MUL is associative"-     ,-     -- TODO look at tests here for SAR: https://github.com/dapphub/dapptools/blob/01ef8ea418c3fe49089a44d56013d8fcc34a1ec2/src/dapp-tests/pass/constantinople.sol#L250-     test "opcode-sar-neg" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(int256 shift_by, int256 val) external pure returns (int256 out) {-              require(shift_by >= 0);-              require(val <= 0);-              assembly {-                out := sar(shift_by,val)-              }-              assert (out <= 0);-              }-             }-            |]-        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256)" [AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts-        putStrLnM "SAR works as expected"-     ,-     test "opcode-sar-pos" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(int256 shift_by, int256 val) external pure returns (int256 out) {-              require(shift_by >= 0);-              require(val >= 0);-              assembly {-                out := sar(shift_by,val)-              }-              assert (out >= 0);-              }-             }-            |]-        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256)" [AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts-        putStrLnM "SAR works as expected"-     ,-     test "opcode-sar-fixedval-pos" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(int256 shift_by, int256 val) external pure returns (int256 out) {-              require(shift_by == 1);-              require(val == 64);-              assembly {-                out := sar(shift_by,val)-              }-              assert (out == 32);-              }-             }-            |]-        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256)" [AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts-        putStrLnM "SAR works as expected"-     ,-     test "opcode-sar-fixedval-neg" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(int256 shift_by, int256 val) external pure returns (int256 out) {-                require(shift_by == 1);-                require(val == -64);-                assembly {-                  out := sar(shift_by,val)-                }-                assert (out == -32);-              }-             }-            |]-        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256)" [AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts-        putStrLnM "SAR works as expected"-     ,-     test "opcode-div-zero-1" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 val) external pure {-                uint out;-                assembly {-                  out := div(val, 0)-                }-                assert(out == 0);--              }-            }-            |]-        (_, [])  <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-        putStrLnM "sdiv works as expected"-      ,-     test "opcode-sdiv-zero-1" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 val) external pure {-                uint out;-                assembly {-                  out := sdiv(val, 0)-                }-                assert(out == 0);--              }-            }-            |]-        (_, [])  <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-        putStrLnM "sdiv works as expected"-      ,-     test "opcode-sdiv-zero-2" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 val) external pure {-                uint out;-                assembly {-                  out := sdiv(0, val)-                }-                assert(out == 0);--              }-            }-            |]-        (_, [])  <- withCVC5Solver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-        putStrLnM "sdiv works as expected"-      ,-     test "signed-overflow-checks" $ do-        Just c <- solcRuntime "C"-            [i|-            contract C {-              function fun(int256 a) external returns (int256) {-                  return a + a;-              }-            }-            |]-        (_, [Cex (_, _)]) <- withDefaultSolver $ \s -> checkAssert s [0x11] c (Just (Sig "fun(int256)" [AbiIntType 256])) [] defaultVeriOpts-        putStrLnM "expected cex discovered"-      ,-     test "opcode-signextend-neg" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 val, uint8 b) external pure {-                require(b <= 31);-                require(b >= 0);-                require(val < (1 <<(b*8)));-                require(val & (1 <<(b*8-1)) != 0); // MSbit set, i.e. negative-                uint256 out;-                assembly {-                  out := signextend(b, val)-                }-                if (b == 31) assert(out == val);-                else assert(out > val);-                assert(out & (1<<254) != 0); // MSbit set, i.e. negative-              }-            }-            |]-        (_, [])  <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-        putStrLnM "signextend works as expected"-      ,-     test "opcode-signextend-pos-nochop" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 val, uint8 b) external pure {-                require(val < (1 <<(b*8)));-                require(val & (1 <<(b*8-1)) == 0); // MSbit not set, i.e. positive-                uint256 out;-                assembly {-                  out := signextend(b, val)-                }-                assert (out == val);-              }-            }-            |]-        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint8)" [AbiUIntType 256, AbiUIntType 8])) [] defaultVeriOpts-        putStrLnM "signextend works as expected"-      ,-      test "opcode-signextend-pos-chopped" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 val, uint8 b) external pure {-                require(b == 0); // 1-byte-                require(val == 514); // but we set higher bits-                uint256 out;-                assembly {-                  out := signextend(b, val)-                }-                assert (out == 2); // chopped-              }-            }-            |]-        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint8)" [AbiUIntType 256, AbiUIntType 8])) [] defaultVeriOpts-        putStrLnM "signextend works as expected"-      ,-      -- when b is too large, value is unchanged-      test "opcode-signextend-pos-b-toolarge" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 val, uint8 b) external pure {-                require(b >= 31);-                uint256 out;-                assembly {-                  out := signextend(b, val)-                }-                assert (out == val);-              }-            }-            |]-        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint8)" [AbiUIntType 256, AbiUIntType 8])) [] defaultVeriOpts-        putStrLnM "signextend works as expected"-     ,-     test "opcode-shl" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 shift_by, uint256 val) external pure {-              require(val < (1<<16));-              require(shift_by < 16);-              uint256 out;-              assembly {-                out := shl(shift_by,val)-              }-              assert (out >= val);-              }-             }-            |]-        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-        putStrLnM "SAR works as expected"-     ,-     test "opcode-xor-cancel" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 a, uint256 b) external pure {-              require(a == b);-              uint256 c;-              assembly {-                c := xor(a,b)-              }-              assert (c == 0);-              }-             }-            |]-        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-        putStrLnM "XOR works as expected"-      ,-      test "opcode-xor-reimplement" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 a, uint256 b) external pure {-              uint256 c;-              assembly {-                c := xor(a,b)-              }-              assert (c == (~(a & b)) & (a | b));-              }-             }-            |]-        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-        putStrLnM "XOR works as expected"-      ,-      test "opcode-add-commutative" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 a, uint256 b) external pure {-                uint256 res1;-                uint256 res2;-                assembly {-                  res1 := add(a,b)-                  res2 := add(b,a)-                }-                assert (res1 == res2);-              }-            }-            |]-        a <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-        case a of-          (_, [Cex (_, ctr)]) -> do-            let x = getVar ctr "arg1"-            let y = getVar ctr "arg2"-            putStrLnM $ "y:" <> show y-            putStrLnM $ "x:" <> show x-            assertEqualM "Addition is not commutative... that's wrong" False True-          (_, []) -> do-            putStrLnM "adding is commutative"-          _ -> internalError "Unexpected"-      ,-      test "opcode-div-res-zero-on-div-by-zero" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint16 a) external pure {-                uint16 b = 0;-                uint16 res;-                assembly {-                  res := div(a,b)-                }-                assert (res == 0);-              }-            }-            |]-        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint16)" [AbiUIntType 16])) [] defaultVeriOpts-        putStrLnM "DIV by zero is zero"-      ,-      -- Somewhat tautological since we are asserting the precondition-      -- on the same form as the actual "requires" clause.-      test "SafeAdd success case" $ do-        Just safeAdd <- solcRuntime "SafeAdd"-          [i|-          contract SafeAdd {-            function add(uint x, uint y) public pure returns (uint z) {-                 require((z = x + y) >= x);-            }-          }-          |]-        let pre preVM = let (x, y) = case getStaticAbiArgs 2 preVM of-                                       [x', y'] -> (x', y')-                                       _ -> internalError "expected 2 args"-                        in (x .<= Expr.add x y)-                        -- TODO check if it's needed-                           .&& preVM.state.callvalue .== Lit 0-            post prestate leaf =-              let (x, y) = case getStaticAbiArgs 2 prestate of-                             [x', y'] -> (x', y')-                             _ -> internalError "expected 2 args"-              in case leaf of-                   Success _ _ b _ -> (ReadWord (Lit 0) b) .== (Add x y)-                   _ -> PBool True-            sig = Just (Sig "add(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])-        (res, []) <- withDefaultSolver $ \s ->-          verifyContract s safeAdd sig [] defaultVeriOpts (Just pre) post-        putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-     ,--      test "x == y => x + y == 2 * y" $ do-        Just safeAdd <- solcRuntime "SafeAdd"-          [i|-          contract SafeAdd {-            function add(uint x, uint y) public pure returns (uint z) {-                 require((z = x + y) >= x);-            }-          }-          |]-        let pre preVM = let (x, y) = case getStaticAbiArgs 2 preVM of-                                       [x', y'] -> (x', y')-                                       _ -> internalError "expected 2 args"-                        in (x .<= Expr.add x y)-                           .&& (x .== y)-                           .&& preVM.state.callvalue .== Lit 0-            post prestate leaf =-              let (_, y) = case getStaticAbiArgs 2 prestate of-                             [x', y'] -> (x', y')-                             _ -> internalError "expected 2 args"-              in case leaf of-                   Success _ _ b _ -> (ReadWord (Lit 0) b) .== (Mul (Lit 2) y)-                   _ -> PBool True-        (res, []) <- withDefaultSolver $ \s ->-          verifyContract s safeAdd (Just (Sig "add(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts (Just pre) post-        putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-      ,-      test "summary storage writes" $ do-        Just c <- solcRuntime "A"-          [i|-          contract A {-            uint x;-            function f(uint256 y) public {-               unchecked {-                 x += y;-                 x += y;-               }-            }-          }-          |]-        let pre vm = Lit 0 .== vm.state.callvalue-            post prestate leaf =-              let y = case getStaticAbiArgs 1 prestate of-                        [y'] -> y'-                        _ -> internalError "expected 1 arg"-                  this = prestate.state.codeContract-                  prestore = (fromJust (Map.lookup this prestate.env.contracts)).storage-                  prex = Expr.readStorage' (Lit 0) prestore-              in case leaf of-                Success _ _ _ postState -> let-                    poststore = (fromJust (Map.lookup this postState)).storage-                  in Expr.add prex (Expr.mul (Lit 2) y) .== (Expr.readStorage' (Lit 0) poststore)-                _ -> PBool True-            sig = Just (Sig "f(uint256)" [AbiUIntType 256])-        (res, []) <- withDefaultSolver $ \s ->-          verifyContract s c sig [] defaultVeriOpts (Just pre) post-        putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-        ,-        -- tests how whiffValue handles Neg via application of the triple IsZero simplification rule-        -- regression test for: https://github.com/dapphub/dapptools/pull/698-        test "Neg" $ do-            let src =-                  [i|-                    object "Neg" {-                      code {-                        // Deploy the contract-                        datacopy(0, dataoffset("runtime"), datasize("runtime"))-                        return(0, datasize("runtime"))-                      }-                      object "runtime" {-                        code {-                          let v := calldataload(4)-                          if iszero(iszero(and(v, not(0xffffffffffffffffffffffffffffffffffffffff)))) {-                            invalid()-                          }-                        }-                      }-                    }-                    |]-            Right c <- liftIO $ yulRuntime "Neg" src-            (res, []) <- withSolvers Z3 4 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "hello(address)" [AbiAddressType])) [] defaultVeriOpts-            putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-        ,-        test "catch-storage-collisions-noproblem" $ do-          Just c <- solcRuntime "A"-            [i|-            contract A {-              function f(uint x, uint y) public {-                 if (x != y) {-                   assembly {-                     let newx := sub(sload(x), 1)-                     let newy := add(sload(y), 1)-                     sstore(x,newx)-                     sstore(y,newy)-                   }-                 }-              }-            }-            |]-          let pre vm = (Lit 0) .== vm.state.callvalue-              post prestate poststate =-                let (x,y) = case getStaticAbiArgs 2 prestate of-                        [x',y'] -> (x',y')-                        _ -> error "expected 2 args"-                    this = prestate.state.codeContract-                    prestore = (fromJust (Map.lookup this prestate.env.contracts)).storage-                    prex = Expr.readStorage' x prestore-                    prey = Expr.readStorage' y prestore-                in case poststate of-                     Success _ _ _ postcs -> let-                           poststore = (fromJust (Map.lookup this postcs)).storage-                           postx = Expr.readStorage' x poststore-                           posty = Expr.readStorage' y poststore-                       in Expr.add prex prey .== Expr.add postx posty-                     _ -> PBool True-              sig = Just (Sig "f(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])-          (_, []) <- withDefaultSolver $ \s ->-            verifyContract s c sig [] defaultVeriOpts (Just pre) post-          putStrLnM "Correct, this can never fail"-        ,-        -- Inspired by these `msg.sender == to` token bugs-        -- which break linearity of totalSupply.-        test "catch-storage-collisions-good" $ do-          Just c <- solcRuntime "A"-            [i|-            contract A {-              function f(uint x, uint y) public {-                 assembly {-                   let newx := sub(sload(x), 1)-                   let newy := add(sload(y), 1)-                   sstore(x,newx)-                   sstore(y,newy)-                 }-              }-            }-            |]-          let pre vm = (Lit 0) .== vm.state.callvalue-              post prestate leaf =-                let (x,y) = case getStaticAbiArgs 2 prestate of-                        [x',y'] -> (x',y')-                        _ -> error "expected 2 args"-                    this = prestate.state.codeContract-                    prestore = (fromJust (Map.lookup this prestate.env.contracts)).storage-                    prex = Expr.readStorage' x prestore-                    prey = Expr.readStorage' y prestore-                in case leaf of-                     Success _ _ _ poststate -> let-                           poststore = (fromJust (Map.lookup this poststate)).storage-                           postx = Expr.readStorage' x poststore-                           posty = Expr.readStorage' y poststore-                       in Expr.add prex prey .== Expr.add postx posty-                     _ -> PBool True-              sig = Just (Sig "f(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])-          (_, [Cex (_, ctr)]) <- withDefaultSolver $ \s ->-            verifyContract s c sig [] defaultVeriOpts (Just pre) post-          let x = getVar ctr "arg1"-          let y = getVar ctr "arg2"-          putStrLnM $ "y:" <> show y-          putStrLnM $ "x:" <> show x-          assertEqualM "Catch storage collisions" x y-          putStrLnM "expected counterexample found"-        ,-        test "simple-assert" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              function foo() external pure {-                assert(false);-              }-             }-            |]-          (_, [Cex (Failure _ _ (Revert msg), _)]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo()" [])) [] defaultVeriOpts-          assertEqualM "incorrect revert msg" msg (ConcreteBuf $ panicMsg 0x01)-        ,-        test "simple-assert-2" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              function foo(uint256 x) external pure {-                assert(x != 10);-              }-             }-            |]-          (_, [(Cex (_, ctr))]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          assertEqualM "Must be 10" 10 $ getVar ctr "arg1"-          putStrLnM "Got 10 Cex, as expected"-        ,-        test "assert-fail-equal" $ do-          Just c <- solcRuntime "AssertFailEqual"-            [i|-            contract AssertFailEqual {-              function fun(uint256 deposit_count) external pure {-                assert(deposit_count == 0);-                assert(deposit_count == 11);-              }-             }-            |]-          (_, [Cex (_, a), Cex (_, b)]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          let ints = map (flip getVar "arg1") [a,b]-          assertBoolM "0 must be one of the Cex-es" $ isJust $ List.elemIndex 0 ints-          putStrLnM "expected 2 counterexamples found, one Cex is the 0 value"-        ,-        test "assert-fail-notequal" $ do-          Just c <- solcRuntime "AssertFailNotEqual"-            [i|-            contract AssertFailNotEqual {-              function fun(uint256 deposit_count) external pure {-                assert(deposit_count != 0);-                assert(deposit_count != 11);-              }-             }-            |]-          (_, [Cex (_, a), Cex (_, b)]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          let x = getVar a "arg1"-          let y = getVar b "arg1"-          assertBoolM "At least one has to be 0, to go through the first assert" (x == 0 || y == 0)-          putStrLnM "expected 2 counterexamples found."-        ,-        test "assert-fail-twoargs" $ do-          Just c <- solcRuntime "AssertFailTwoParams"-            [i|-            contract AssertFailTwoParams {-              function fun(uint256 deposit_count1, uint256 deposit_count2) external pure {-                assert(deposit_count1 != 0);-                assert(deposit_count2 != 11);-              }-             }-            |]-          (_, [Cex _, Cex _]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-          putStrLnM "expected 2 counterexamples found"-        ,-        test "assert-2nd-arg" $ do-          Just c <- solcRuntime "AssertFailTwoParams"-            [i|-            contract AssertFailTwoParams {-              function fun(uint256 deposit_count1, uint256 deposit_count2) external pure {-                assert(deposit_count2 != 666);-              }-             }-            |]-          (_, [Cex (_, ctr)]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-          assertEqualM "Must be 666" 666 $ getVar ctr "arg2"-          putStrLnM "Found arg2 Ctx to be 666"-        ,-        -- LSB is zeroed out, byte(31,x) takes LSB, so y==0 always holds-        test "check-lsb-msb1" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              function foo(uint256 x) external pure {-                x &= 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00;-                uint8 y;-                assembly { y := byte(31,x) }-                assert(y == 0);-              }-            }-            |]-          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-        ,-        -- We zero out everything but the LSB byte. However, byte(31,x) takes the LSB byte-        -- so there is a counterexamle, where LSB of x is not zero-        test "check-lsb-msb2" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              function foo(uint256 x) external pure {-                x &= 0x00000000000000000000000000000000000000000000000000000000000000ff;-                uint8 y;-                assembly { y := byte(31,x) }-                assert(y == 0);-              }-            }-            |]-          (_, [Cex (_, ctr)]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          assertBoolM "last byte must be non-zero" $ ((Data.Bits..&.) (getVar ctr "arg1") 0xff) > 0-          putStrLnM "Expected counterexample found"-        ,-        -- We zero out everything but the 2nd LSB byte. However, byte(31,x) takes the 2nd LSB byte-        -- so there is a counterexamle, where 2nd LSB of x is not zero-        test "check-lsb-msb3 -- 2nd byte" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              function foo(uint256 x) external pure {-                x &= 0x000000000000000000000000000000000000000000000000000000000000ff00;-                uint8 y;-                assembly { y := byte(30,x) }-                assert(y == 0);-              }-            }-            |]-          (_, [Cex (_, ctr)]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          assertBoolM "second to last byte must be non-zero" $ ((Data.Bits..&.) (getVar ctr "arg1") 0xff00) > 0-          putStrLnM "Expected counterexample found"-        ,-        -- Reverse of test above-        test "check-lsb-msb4 2nd byte rev" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              function foo(uint256 x) external pure {-                x &= 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff;-                uint8 y;-                assembly {-                    y := byte(30,x)-                }-                assert(y == 0);-              }-            }-            |]-          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-        ,-        -- Bitwise OR operation test-        test "opcode-bitwise-or-full-1s" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              function foo(uint256 x) external pure {-                uint256 y;-                uint256 z = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;-                assembly { y := or(x, z) }-                assert(y == 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);-              }-            }-            |]-          (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLnM "When OR-ing with full 1's we should get back full 1's"-        ,-        -- Bitwise OR operation test-        test "opcode-bitwise-or-byte-of-1s" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              function foo(uint256 x) external pure {-                uint256 y;-                uint256 z = 0x000000000000000000000000000000000000000000000000000000000000ff00;-                assembly { y := or(x, z) }-                assert((y & 0x000000000000000000000000000000000000000000000000000000000000ff00) ==-                  0x000000000000000000000000000000000000000000000000000000000000ff00);-              }-            }-            |]-          (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLnM "When OR-ing with a byte of 1's, we should get 1's back there"-        ,-        test "Deposit contract loop (z3)" $ do-          Just c <- solcRuntime "Deposit"-            [i|-            contract Deposit {-              function deposit(uint256 deposit_count) external pure {-                require(deposit_count < 2**32 - 1);-                ++deposit_count;-                bool found = false;-                for (uint height = 0; height < 32; height++) {-                  if ((deposit_count & 1) == 1) {-                    found = true;-                    break;-                  }-                 deposit_count = deposit_count >> 1;-                 }-                assert(found);-              }-             }-            |]-          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "deposit(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-        ,-        test "Deposit-contract-loop-error-version" $ do-          Just c <- solcRuntime "Deposit"-            [i|-            contract Deposit {-              function deposit(uint8 deposit_count) external pure {-                require(deposit_count < 2**32 - 1);-                ++deposit_count;-                bool found = false;-                for (uint height = 0; height < 32; height++) {-                  if ((deposit_count & 1) == 1) {-                    found = true;-                    break;-                  }-                 deposit_count = deposit_count >> 1;-                 }-                assert(found);-              }-             }-            |]-          (_, [Cex (_, ctr)]) <- withDefaultSolver $ \s -> checkAssert s allPanicCodes c (Just (Sig "deposit(uint8)" [AbiUIntType 8])) [] defaultVeriOpts-          assertEqualM "Must be 255" 255 $ getVar ctr "arg1"-          putStrLnM  $ "expected counterexample found, and it's correct: " <> (show $ getVar ctr "arg1")-        ,-        test "explore function dispatch" $ do-          Just c <- solcRuntime "A"-            [i|-            contract A {-              function f(uint x) public pure returns (uint) {-                return x;-              }-            }-            |]-          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts-          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-        ,-        test "check-asm-byte-in-bounds" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              function foo(uint256 idx, uint256 val) external pure {-                uint256 actual;-                uint256 expected;-                require(idx < 32);-                assembly {-                  actual := byte(idx,val)-                  expected := shr(248, shl(mul(idx, 8), val))-                }-                assert(actual == expected);-              }-            }-            |]-          (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts-          putStrLnM "in bounds byte reads return the expected value"-        ,-        test "check-div-mod-sdiv-smod-by-zero-constant-prop" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              function foo(uint256 e) external pure {-                uint x = 0;-                uint y = 55;-                uint z;-                assembly { z := div(y,x) }-                assert(z == 0);-                assembly { z := div(x,y) }-                assert(z == 0);-                assembly { z := sdiv(y,x) }-                assert(z == 0);-                assembly { z := sdiv(x,y) }-                assert(z == 0);-                assembly { z := mod(y,x) }-                assert(z == 0);-                assembly { z := mod(x,y) }-                assert(z == 0);-                assembly { z := smod(y,x) }-                assert(z == 0);-                assembly { z := smod(x,y) }-                assert(z == 0);-              }-            }-            |]-          (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLnM "div/mod/sdiv/smod by zero works as expected during constant propagation"-        ,-        test "check-asm-byte-oob" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              function foo(uint256 x, uint256 y) external pure {-                uint256 z;-                require(x >= 32);-                assembly { z := byte(x,y) }-                assert(z == 0);-              }-            }-            |]-          (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts-          putStrLnM "oob byte reads always return 0"-        ,-        test "injectivity of keccak (diff sizes)" $ do-          Just c <- solcRuntime "A"-            [i|-            contract A {-              function f(uint128 x, uint256 y) external pure {-                assert(-                    keccak256(abi.encodePacked(x)) !=-                    keccak256(abi.encodePacked(y))-                );-              }-            }-            |]-          Right _ <- reachableUserAsserts c (Just $ Sig "f(uint128,uint256)" [AbiUIntType 128, AbiUIntType 256])-          pure ()-        ,-        test "injectivity of keccak (32 bytes)" $ do-          Just c <- solcRuntime "A"-            [i|-            contract A {-              function f(uint x, uint y) public pure {-                if (keccak256(abi.encodePacked(x)) == keccak256(abi.encodePacked(y))) assert(x == y);-              }-            }-            |]-          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-        ,-        test "injectivity of keccak contrapositive (32 bytes)" $ do-          Just c <- solcRuntime "A"-            [i|-            contract A {-              function f(uint x, uint y) public pure {-                require (x != y);-                assert (keccak256(abi.encodePacked(x)) != keccak256(abi.encodePacked(y)));-              }-            }-            |]-          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-        ,-        test "injectivity of keccak (64 bytes)" $ do-          Just c <- solcRuntime "A"-            [i|-            contract A {-              function f(uint x, uint y, uint w, uint z) public pure {-                assert (keccak256(abi.encodePacked(x,y)) != keccak256(abi.encodePacked(w,z)));-              }-            }-            |]-          (_, [Cex (_, ctr)]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256,uint256,uint256,uint256)" (replicate 4 (AbiUIntType 256)))) [] defaultVeriOpts-          let x = getVar ctr "arg1"-          let y = getVar ctr "arg2"-          let w = getVar ctr "arg3"-          let z = getVar ctr "arg4"-          assertEqualM "x==y for hash collision" x y-          assertEqualM "w==z for hash collision" w z-          putStrLnM "expected counterexample found"-        ,-        test "calldata beyond calldatasize is 0 (symbolic calldata)" $ do-          Just c <- solcRuntime "A"-            [i|-            contract A {-              function f() public pure {-                uint y;-                assembly {-                  let x := calldatasize()-                  y := calldataload(x)-                }-                assert(y == 0);-              }-            }-            |]-          (res, []) <- withBitwuzlaSolver $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts-          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-        ,-        test "calldata beyond calldatasize is 0 (concrete dalldata prefix)" $ do-          Just c <- solcRuntime "A"-            [i|-            contract A {-              function f(uint256 z) public pure {-                uint y;-                assembly {-                  let x := calldatasize()-                  y := calldataload(x)-                }-                assert(y == 0);-              }-            }-            |]-          (res, []) <- withBitwuzlaSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-        ,-        test "calldata symbolic access" $ do-          Just c <- solcRuntime "A"-            [i|-            contract A {-              function f(uint256 z) public pure {-                uint x; uint y;-                assembly {-                  y := calldatasize()-                }-                require(z >= y);-                require(z < 2**64); // Accesses to larger indices are not supported-                assembly {-                  x := calldataload(z)-                }-                assert(x == 0);-              }-            }-            |]-          (res, []) <- withBitwuzlaSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-        ,-        test "multiple-contracts" $ do-          let code =-                [i|-                  contract C {-                    uint x;-                    A constant a = A(0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B);--                    function call_A() public view {-                      // should fail since x can be anything-                      assert(a.x() == x);-                    }-                  }-                  contract A {-                    uint public x;-                  }-                |]-              aAddr = LitAddr (Addr 0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B)-              cAddr = SymAddr "entrypoint"-          Just c <- solcRuntime "C" code-          Just a <- solcRuntime "A" code-          (_, [Cex (_, cex)]) <- withDefaultSolver $ \s -> do-            calldata <- mkCalldata (Just (Sig "call_A()" [])) []-            vm <- liftIO $ stToIO $ abstractVM calldata c Nothing False-                    <&> set (#state % #callvalue) (Lit 0)-                    <&> over (#env % #contracts)-                       (Map.insert aAddr (initialContract (RuntimeCode (ConcreteRuntimeCode a))))-            verify s (Fetch.noRpcFetcher s) defaultVeriOpts vm (checkAssertions defaultPanicCodes) Nothing--          let storeCex = cex.store-              testCex = case (Map.lookup cAddr storeCex, Map.lookup aAddr storeCex) of-                          (Just sC, Just sA) -> case (Map.lookup 0 sC, Map.lookup 0 sA) of-                              (Just x, Just y) -> x /= y-                              (Just x, Nothing) -> x /= 0-                              _ -> False-                          _ -> False-          assertBoolM "Did not find expected storage cex" testCex-          putStrLnM "expected counterexample found"-        , test "calling-unique-contracts--read-from-storage" $ do-          Just c <- solcRuntime "C"-            [i|-              contract C {-                uint x;-                A a;--                function call_A() public {-                  a = new A();-                  // should fail since x can be anything-                  assert(a.x() == x);-                }-              }-              contract A {-                uint public x;-              }-            |]-          (_, [Cex _]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "call_A()" [])) [] defaultVeriOpts-          putStrLnM "expected counterexample found"-        ,-        test "keccak-concrete-and-sym-agree" $ do-          Just c <- solcRuntime "C"-            [i|-              contract C {-                function kecc(uint x) public pure {-                  if (x == 0) {-                    assert(keccak256(abi.encode(x)) == keccak256(abi.encode(0)));-                  }-                }-              }-            |]-          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "kecc(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-        ,-        test "keccak-concrete-and-sym-agree-nonzero" $ do-          Just c <- solcRuntime "C"-            [i|-              contract C {-                function kecc(uint x) public pure {-                  if (x == 55) {-                    // Note: 3014... is the encode & keccak & uint256 conversion of 55-                    assert(uint256(keccak256(abi.encode(x))) == 30148980456718914367279254941528755963179627010946392082519497346671089299886);-                  }-                }-              }-            |]-          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "kecc(uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-        ,-        test "keccak concrete and sym injectivity" $ do-          Just c <- solcRuntime "A"-            [i|-              contract A {-                function f(uint x) public pure {-                  if (x !=3) assert(keccak256(abi.encode(x)) != keccak256(abi.encode(3)));-                }-              }-            |]-          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"-        ,-        test "safemath-distributivity-yul" $ do-          let yulsafeDistributivity = hex "6355a79a6260003560e01c14156016576015601f565b5b60006000fd60a1565b603d602d604435600435607c565b6039602435600435607c565b605d565b6052604b604435602435605d565b600435607c565b141515605a57fe5b5b565b6000828201821115151560705760006000fd5b82820190505b92915050565b6000818384048302146000841417151560955760006000fd5b82820290505b92915050565b"-          calldata <- mkCalldata (Just (Sig "distributivity(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) []-          vm <- liftIO $ stToIO $ abstractVM calldata yulsafeDistributivity Nothing False-          (_, []) <-  withDefaultSolver $ \s -> verify s (Fetch.noRpcFetcher s) defaultVeriOpts vm (checkAssertions defaultPanicCodes) Nothing-          putStrLnM "Proven"-        ,-        test "safemath-distributivity-sol" $ do-          Just c <- solcRuntime "C"-            [i|-              contract C {-                function distributivity(uint x, uint y, uint z) public {-                  assert(mul(x, add(y, z)) == add(mul(x, y), mul(x, z)));-                }--                function add(uint x, uint y) internal pure returns (uint z) {-                  unchecked {-                    require((z = x + y) >= x, "ds-math-add-overflow");-                    }-                }--                function mul(uint x, uint y) internal pure returns (uint z) {-                  unchecked {-                    require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");-                  }-                }-              }-            |]--          (_, []) <- withSolvers Bitwuzla 1 1 (Just 99999999) $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "distributivity(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-          putStrLnM "Proven"-        ,-        test "storage-cex-1" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              uint x;-              uint y;-              function fun(uint256 a) external{-                require(x != 0);-                require(y != 0);-                assert (x == y);-              }-            }-            |]-          (_, [(Cex (_, cex))]) <- withDefaultSolver $ \s -> checkAssert s [0x01] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          let addr = SymAddr "entrypoint"-              testCex = Map.size cex.store == 1 &&-                        case Map.lookup addr cex.store of-                          Just s -> Map.size s == 2 &&-                                    case (Map.lookup 0 s, Map.lookup 1 s) of-                                      (Just x, Just y) -> x /= y-                                      _ -> False-                          _ -> False-          assertBoolM "Did not find expected storage cex" testCex-          putStrLnM "Expected counterexample found"-        ,-        test "storage-cex-2" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              uint[10] arr1;-              uint[10] arr2;-              function fun(uint256 a) external{-                assert (arr1[0] < arr2[a]);-              }-            }-            |]-          (_, [(Cex (_, cex))]) <- withDefaultSolver $ \s -> checkAssert s [0x01] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          let addr = SymAddr "entrypoint"-              a = getVar cex "arg1"-              testCex = Map.size cex.store == 1 &&-                        case Map.lookup addr cex.store of-                          Just s -> case (Map.lookup 0 s, Map.lookup (10 + a) s) of-                                      (Just x, Just y) -> x >= y-                                      _ -> False-                          Nothing -> False -- arr2 must contain an element, or it'll be 0-          assertBoolM "Did not find expected storage cex" testCex-          putStrLnM "Expected counterexample found"-        ,-        test "storage-cex-concrete" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              uint x;-              uint y;-              function fun(uint256 a) external{-                require (x != 0);-                require (y != 0);-                assert (x != y);-              }-            }-            |]-          let sig = Just (Sig "fun(uint256)" [AbiUIntType 256])-          (_, [Cex (_, cex)]) <- withDefaultSolver $-            \s -> verifyContract s c sig [] defaultVeriOpts Nothing (checkAssertions [0x01])-          let addr = SymAddr "entrypoint"-              testCex = Map.size cex.store == 1 &&-                        case Map.lookup addr cex.store of-                          Just s -> Map.size s == 2 &&-                                    case (Map.lookup 0 s, Map.lookup 1 s) of-                                      (Just x, Just y) -> x == y-                                      _ -> False-                          _ -> False-          assertBoolM "Did not find expected storage cex" testCex-          putStrLnM "Expected counterexample found"-        , test "temp-store-check" $ do-          Just c <- solcRuntime "C"-            [i|-            pragma solidity ^0.8.25;-            contract C {-                mapping(address => bool) sentGifts;-                function stuff(address k) public {-                    require(sentGifts[k] == false);-                    assembly {-                        if tload(0) { revert(0, 0) }-                        tstore(0, 1)-                    }-                    sentGifts[k] = true;-                    assembly {-                        tstore(0, 0)-                    }-                    assert(sentGifts[k]);-                }-            }-            |]-          let sig = (Just (Sig "stuff(address)" [AbiAddressType]))-          (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-          putStrLnM $ "Basic tstore check passed"-  ]-  , testGroup "simple-checks"-    [ test "simple-stores" $ do-      Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          mapping(uint => uint) items;-          function func() public {-            assert(items[5] == 0);-          }-        }-        |]-      let sig = (Sig "func()" [])-      (_, [Cex (_, ctr)]) <- withCVC5Solver $ \s -> checkAssert s defaultPanicCodes c (Just sig) [] defaultVeriOpts-      putStrLnM  $ "expected counterexample found.  ctr: " <> (show ctr)-    , test "simple-fixed-value" $ do-      Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          mapping(uint => uint) items;-          function func(uint a) public {-            assert(a != 1337);-          }-        }-        |]-      let sig = (Sig "func(uint256)" [AbiUIntType 256])-      (_, [Cex (_, ctr)]) <- withCVC5Solver $ \s -> checkAssert s defaultPanicCodes c (Just sig) [] defaultVeriOpts-      putStrLnM  $ "expected counterexample found.  ctr: " <> (show ctr)-    , test "simple-fixed-value2" $ do-      Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          function func(uint a, uint b) public {-            assert(!((a == 1337) && (b == 99)));-          }-        }-        |]-      let sig = (Sig "func(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])-      (_, [Cex (_, ctr)]) <- withCVC5Solver $ \s -> checkAssert s defaultPanicCodes c (Just sig) [] defaultVeriOpts-      putStrLnM  $ "expected counterexample found.  ctr: " <> (show ctr)-    , test "simple-fixed-value3" $ do-      Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          function func(uint a, uint b) public {-            assert(((a != 1337) && (b != 99)));-          }-        }-        |]-      let sig = (Sig "func(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])-      (_, [Cex (_, ctr1), Cex (_, ctr2)]) <- withSolvers Bitwuzla 1 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just sig) [] defaultVeriOpts-      putStrLnM  $ "expected counterexamples found.  ctr1: " <> (show ctr1) <> " ctr2: " <> (show ctr2)-    , test "simple-fixed-value-store1" $ do-      Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          mapping(uint => uint) items;-          function func(uint a) public {-            uint f = items[2];-            assert(a != f);-          }-        }-        |]-      let sig = (Sig "func(uint256)" [AbiUIntType 256, AbiUIntType 256])-      (_, [Cex _]) <- withCVC5Solver $ \s -> checkAssert s defaultPanicCodes c (Just sig) [] defaultVeriOpts-      putStrLnM  "expected counterexamples found"-    , test "simple-fixed-value-store2" $ do-      Just c <- solcRuntime "MyContract"-        [i|-        contract MyContract {-          mapping(uint => uint) items;-          function func(uint a) public {-            items[0] = 1337;-            assert(a != items[0]);-          }-        }-        |]-      let sig = (Sig "func(uint256)" [AbiUIntType 256, AbiUIntType 256])-      (_, [Cex (_, ctr1)]) <- withCVC5Solver $ \s -> checkAssert s defaultPanicCodes c (Just sig) [] defaultVeriOpts-      putStrLnM  $ "expected counterexamples found: " <> show ctr1-  ]-  , testGroup "prop-and-expr-properties"-  [-    test "nubOrd-Prop-PLT" $ do-        let a = [ PLT (Lit 0x0) (ReadWord (ReadWord (Lit 0x0) (AbstractBuf "txdata")) (AbstractBuf "txdata"))-                , PLT (Lit 0x1) (ReadWord (ReadWord (Lit 0x0) (AbstractBuf "txdata")) (AbstractBuf "txdata"))-                , PLT (Lit 0x2) (ReadWord (ReadWord (Lit 0x0) (AbstractBuf "txdata")) (AbstractBuf "txdata"))-                , PLT (Lit 0x0) (ReadWord (ReadWord (Lit 0x0) (AbstractBuf "txdata")) (AbstractBuf "txdata"))]-        let simp = nubOrd a-            simp2 = List.nub a-        assertEqualM "Must be 3-length" 3 (length simp)-        assertEqualM "Must be 3-length" 3 (length simp2)-    , test "nubOrd-Prop-PEq" $ do-        let a = [ PEq (Lit 0x0) (ReadWord (Lit 0x0) (AbstractBuf "txdata"))-                , PEq (Lit 0x0) (ReadWord (Lit 0x1) (AbstractBuf "txdata"))-                , PEq (Lit 0x0) (ReadWord (Lit 0x2) (AbstractBuf "txdata"))-                , PEq (Lit 0x0) (ReadWord (Lit 0x0) (AbstractBuf "txdata"))]-        let simp = nubOrd a-            simp2 = List.nub a-        assertEqualM "Must be 3-length" 3 (length simp)-        assertEqualM "Must be 3-length" 3 (length simp2)-    -- we run these without the simplifier inside `checkSatWithProps`, so-    -- we can test the SMT solver's ability to handle sign extension-    , test "sign-extend-conc-1" $ do-      let p = Expr.sex (Lit 0) (Lit 0xff)-      assertEqualM "stuff" p (Expr.simplify (Sub (Lit 0) (Lit 1)))-      let p2 = Expr.sex (Lit 30) (Lit 0xff)-      assertEqualM "stuff" p2 (Lit 0xff)-      let p3 = Expr.sex (Lit 1) (Lit 0xff)-      assertEqualM "stuff" p3 (Lit 0xff)-      let p4 = Expr.sex (Lit 0) (Lit 0x1)-      assertEqualM "stuff" p4 (Lit 0x1)-      let p5 = Expr.sex (Lit 0) (Lit 0x0)-      assertEqualM "stuff" p5 (Lit 0x0)-    , testNoSimplify "sign-extend-1" $ do-        let p = (PEq (Lit 1) (SLT (Lit 1774544) (SEx (Lit 2) (Lit 1774567))))-        let simp = Expr.simplifyProps [p]-        assertEqualM "Must simplify to PBool True" simp []-        withDefaultSolver $ \s -> do-          res <- checkSatWithProps s [p]-          _ <- case res of-            Cex c -> pure c-            _ -> liftIO $ assertFailure "Must be satisfiable!"-          pure ()-    , testNoSimplify "sign-extend-2" $ do-      let p = (PEq (Lit 1) (SLT (SEx (Lit 2) (Var "arg1")) (Lit 0)))-      withDefaultSolver $ \s -> do-        res <- checkSatWithProps s [p]-        _ <- case res of-          Cex c -> pure c-          _ -> liftIO $ assertFailure "Must be satisfiable!"-        pure()-    , testNoSimplify "sign-extend-3" $ do-      let p = PAnd-                (PEq (Lit 1) (SLT (SEx (Lit 2) (Var "arg1")) (Lit 115792089237316195423570985008687907853269984665640564039457584007913128752664)))-                (PEq (Var "arg1") (SEx (Lit 2) (Var "arg1")))-      withDefaultSolver $ \s -> do-        res <- checkSatWithProps s [p]-        _ <- case res of-          Cex c -> pure c-          _ -> liftIO $ assertFailure "Must be satisfiable!"-        pure()-    , testProperty "sign-extend-vs-smt" $ \(a :: W256, b :: W256) -> propNoSimp $ do-        let p = (PEq (Var "arg1") (SEx (Lit (a `mod` 50)) (Lit b)))-        withDefaultSolver $ \s -> do-          res <- checkSatWithProps s [p]-          cex <- case res of-            Cex c -> pure c-            _ -> liftIO $ assertFailure "Must be satisfiable!"-          let res1 = fromRight (internalError "cannot be") $ subModel cex (Var "arg1")-          res1W <- case res1 of-            Lit x -> pure x-            _ -> internalError "Expected Lit"-          let res2 = Expr.simplifyProps [p]-          res2W <- case res2 of-            [PEq (Lit x) (Var "arg1")] -> pure x-            _ -> internalError "Expected PEq"-          assertEqualM "Must be equivalent concrete values" res1W res2W-  ]-  , testGroup "simplification-working"-  [-    test "PEq-and-PNot-PEq-1" $ do-      let a = [PEq (Lit 0x539) (Var "arg1"),PNeg (PEq (Lit 0x539) (Var "arg1"))]-      assertEqualM "Must simplify to PBool False" (Expr.simplifyProps a) ([PBool False])-    , test "PEq-and-PNot-PEq-2" $ do-      let a = [PEq (Var "arg1") (Lit 0x539),PNeg (PEq (Lit 0x539) (Var "arg1"))]-      assertEqualM "Must simplify to PBool False" (Expr.simplifyProps a) ([PBool False])-    , test "PEq-and-PNot-PEq-3" $ do-      let a = [PEq (Var "arg1") (Lit 0x539),PNeg (PEq (Var "arg1") (Lit 0x539))]-      assertEqualM "Must simplify to PBool False" (Expr.simplifyProps a) ([PBool False])-    , test "prop-simp-bool1" $ do-      let-        a = successGen [PAnd (PBool True) (PBool False)]-        b = Expr.simplify a-      assertEqualM "Must simplify down" (successGen [PBool False]) b-    , test "prop-simp-bool2" $ do-      let-        a = successGen [POr (PBool True) (PBool False)]-        b = Expr.simplify a-      assertEqualM "Must simplify down" (successGen []) b-    , test "prop-simp-LT" $ do-      let-        a = successGen [PLT (Lit 1) (Lit 2)]-        b = Expr.simplify a-      assertEqualM "Must simplify down" (successGen []) b-    , test "prop-simp-GEq" $ do-      let-        a = successGen [PGEq (Lit 1) (Lit 2)]-        b = Expr.simplify a-      assertEqualM "Must simplify down" (successGen [PBool False]) b-    , test "prop-simp-liteq-false" $ do-      let-        a = [PEq (LitByte 10) (LitByte 12)]-        b = Expr.simplifyProps a-      assertEqualM "Must simplify to PBool" ([PBool False]) b-    , test "prop-simp-concbuf-false" $ do-      let-        a = [PEq (ConcreteBuf "a") (ConcreteBuf "ab")]-        b = Expr.simplifyProps a-      assertEqualM "Must simplify to PBool" ([PBool False]) b-    , test "prop-simp-sort-eq-arguments" $ do-      let-        a = [PEq (Var "a") (Var "b"), PEq (Var "b") (Var "a")]-        b = Expr.simplifyProps a-      assertEqualM "Must reorder and nubOrd" (length b) 1-    , test "expr-simp-and-commut-assoc" $ do-      let-        a = And (Lit 4) (And (Lit 5) (Var "a"))-        b = Expr.simplify a-      assertEqualM "Must reorder and perform And" (And (Lit 4) (Var "a")) b-    , test "expr-simp-order-eqbyte" $ do-      let-        a = EqByte (ReadByte (Lit 1) (AbstractBuf "b")) (ReadByte (Lit 1) (AbstractBuf "a"))-        b = Expr.simplify a-      assertEqualM "Must order eqbyte params" b (EqByte (ReadByte (Lit 1) (AbstractBuf "a")) (ReadByte (Lit 1) (AbstractBuf "b")))-    , test "prop-simp-demorgan-pneg-pand" $ do-      let-        a = [PNeg (PAnd (PEq (Lit 4) (Var "a")) (PEq (Lit 5) (Var "b")))]-        b = Expr.simplifyProps a-      assertEqualM "Must apply demorgan" b [POr (PNeg (PEq (Lit 4) (Var "a"))) (PNeg (PEq (Lit 5) (Var "b")))]-    , test "prop-simp-demorgan-pneg-por" $ do-      let-        a = [PNeg (POr (PEq (Lit 4) (Var "a")) (PEq (Lit 5) (Var "b")))]-        b = Expr.simplifyProps a-      assertEqualM "Must apply demorgan" [PNeg (PEq (Lit 4) (Var "a")), PNeg (PEq (Lit 5) (Var "b"))] b-    , test "prop-simp-lit0-or-both-0" $ do-      let-        a = [PEq (Lit 0) (Or (Var "a") (Var "b"))]-        b = Expr.simplifyProps a-      assertEqualM "Must apply demorgan" [PEq (Lit 0) (Var "a"), PEq (Lit 0) (Var "b")] b-    , test "prop-simp-multiple" $ do-      let-        a = successGen [PBool False, PBool True]-        b = Expr.simplify a-      assertEqualM "Must simplify down" (successGen [PBool False]) b-    , test "prop-simp-expr" $ do-      let-        a = successGen [PEq (Add (Lit 1) (Lit 2)) (Sub (Lit 4) (Lit 1))]-        b = Expr.simplify a-      assertEqualM "Must simplify down" (successGen []) b-    , test "prop-simp-impl" $ do-      let-        a = successGen [PImpl (PBool False) (PEq (Var "abc") (Var "bcd"))]-        b = Expr.simplify a-      assertEqualM "Must simplify down" (successGen []) b-    , test "propSimp-no-duplicate1" $ do-      let a = [PAnd (PGEq (Max (Lit 0x44) (BufLength (AbstractBuf "txdata"))) (Lit 0x44)) (PLT (Max (Lit 0x44) (BufLength (AbstractBuf "txdata"))) (Lit 0x10000000000000000)), PAnd (PGEq (Var "arg1") (Lit 0x0)) (PLEq (Var "arg1") (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)),PEq (Lit 0x63) (Var "arg2"),PEq (Lit 0x539) (Var "arg1"),PEq TxValue (Lit 0x0),PEq (IsZero (Eq (Lit 0x63) (Var "arg2"))) (Lit 0x0)]-      let simp = Expr.simplifyProps a-      assertEqualM "must not duplicate" simp (nubOrd simp)-      assertEqualM "We must be able to remove all duplicates" (length $ nubOrd simp) (length $ List.nub simp)-    , test "propSimp-no-duplicate2" $ do-      let a = [PNeg (PBool False),PAnd (PGEq (Max (Lit 0x44) (BufLength (AbstractBuf "txdata"))) (Lit 0x44)) (PLT (Max (Lit 0x44) (BufLength (AbstractBuf "txdata"))) (Lit 0x10000000000000000)),PAnd (PGEq (Var "arg2") (Lit 0x0)) (PLEq (Var "arg2") (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)),PAnd (PGEq (Var "arg1") (Lit 0x0)) (PLEq (Var "arg1") (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)),PEq (Lit 0x539) (Var "arg1"),PNeg (PEq (Lit 0x539) (Var "arg1")),PEq TxValue (Lit 0x0),PLT (BufLength (AbstractBuf "txdata")) (Lit 0x10000000000000000),PEq (IsZero (Eq (Lit 0x539) (Var "arg1"))) (Lit 0x0),PNeg (PEq (IsZero (Eq (Lit 0x539) (Var "arg1"))) (Lit 0x0)),PNeg (PEq (IsZero TxValue) (Lit 0x0))]-      let simp = Expr.simplifyProps a-      assertEqualM "must not duplicate" simp (nubOrd simp)-      assertEqualM "must not duplicate" (length simp) (length $ List.nub simp)-    , test "full-order-prop1" $ do-      let a = [PNeg (PBool False),PAnd (PGEq (Max (Lit 0x44) (BufLength (AbstractBuf "txdata"))) (Lit 0x44)) (PLT (Max (Lit 0x44) (BufLength (AbstractBuf "txdata"))) (Lit 0x10000000000000000)),PAnd (PGEq (Var "arg2") (Lit 0x0)) (PLEq (Var "arg2") (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)),PAnd (PGEq (Var "arg1") (Lit 0x0)) (PLEq (Var "arg1") (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)),PEq (Lit 0x539) (Var "arg1"),PNeg (PEq (Lit 0x539) (Var "arg1")),PEq TxValue (Lit 0x0),PLT (BufLength (AbstractBuf "txdata")) (Lit 0x10000000000000000),PEq (IsZero (Eq (Lit 0x539) (Var "arg1"))) (Lit 0x0),PNeg (PEq (IsZero (Eq (Lit 0x539) (Var "arg1"))) (Lit 0x0)),PNeg (PEq (IsZero TxValue) (Lit 0x0))]-      let simp = Expr.simplifyProps a-      assertEqualM "We must be able to remove all duplicates" (length $ nubOrd simp) (length $ List.nub simp)-    , test "full-order-prop2" $ do-      let a =[PNeg (PBool False),PAnd (PGEq (Max (Lit 0x44) (BufLength (AbstractBuf "txdata"))) (Lit 0x44)) (PLT (Max (Lit 0x44) (BufLength (AbstractBuf "txdata"))) (Lit 0x10000000000000000)),PAnd (PGEq (Var "arg2") (Lit 0x0)) (PLEq (Var "arg2") (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)),PAnd (PGEq (Var "arg1") (Lit 0x0)) (PLEq (Var "arg1") (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)),PEq (Lit 0x63) (Var "arg2"),PEq (Lit 0x539) (Var "arg1"),PEq TxValue (Lit 0x0),PLT (BufLength (AbstractBuf "txdata")) (Lit 0x10000000000000000),PEq (IsZero (Eq (Lit 0x63) (Var "arg2"))) (Lit 0x0),PEq (IsZero (Eq (Lit 0x539) (Var "arg1"))) (Lit 0x0),PNeg (PEq (IsZero TxValue) (Lit 0x0))]-      let simp = Expr.simplifyProps a-      assertEqualM "must not duplicate" simp (nubOrd simp)-      assertEqualM "We must be able to remove all duplicates" (length $ nubOrd simp) (length $ List.nub simp)-  ]-  , testGroup "calling-solvers"-  [ test "no-error-on-large-buf" $ do-      -- These two tests generates a very large buffer that previously would cause an internalError when-      -- printed via "formatCex". We should be able to print it now.-      Just c <- solcRuntime "MyContract" [i|-          contract MyContract {-            function fun(bytes calldata a) external pure {-              if (a.length > 0x800000000000) {-                assert(false);-              }-            }-           } |]-      (_, [Cex cex]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts-      putStrLnM $ "Cex found:" <> T.unpack (formatCex (AbstractBuf "txdata") Nothing (snd cex))-    , test "no-error-on-large-buf-pure-print" $ do-      let bufs = Map.singleton (AbstractBuf "txdata")-                  (EVM.Types.Comp Write {byte = 1, idx = 0x27, next = Base {byte = 0x66, length = 0xffff000000000000000}})-      let mycex = SMTCex {vars = mempty-                         , addrs = mempty-                         , buffers = bufs-                         , store = mempty-                         , blockContext = mempty-                         , txContext = Map.fromList [(TxValue,0x0)]}-      putStrLnM $ "Cex found:" <> T.unpack (formatCex (AbstractBuf "txdata") Nothing mycex)-    , test "correct-model-for-empty-buffer" $ do-      withDefaultSolver $ \s -> do-        let props = [(PEq (BufLength (AbstractBuf "b")) (Lit 0x0))]-        res <- checkSatWithProps s props-        (cex) <- case res of-          Cex c -> pure c-          _ -> liftIO $ assertFailure "Must be satisfiable!"-        let value = fromRight (error "cannot be") $ subModel cex (AbstractBuf "b")-        assertEqualM "Buffer must be empty" (ConcreteBuf "") value-    , test "correct-model-for-non-empty-buffer-of-all-zeroes" $ do-      withDefaultSolver $ \s -> do-        let props = [(PAnd (PEq (ReadByte (Lit 0x0) (AbstractBuf "b")) (LitByte 0x0)) (PEq (BufLength (AbstractBuf "b")) (Lit 0x1)))]-        res <- checkSatWithProps s props-        (cex) <- case res of-          Cex c -> pure c-          _ -> liftIO $ assertFailure "Must be satisfiable!"-        let value = fromRight (error "cannot be") $ subModel cex (AbstractBuf "b")-        assertEqualM "Buffer must have size 1 and contain zero byte" (ConcreteBuf "\0") value-    , test "buffer-shrinking-does-not-loop" $ do-      withDefaultSolver $ \s -> do-        let props = [(PGT (BufLength (AbstractBuf "b")) (Lit 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeb4))]-        res <- checkSatWithProps s props-        let-          sat = case res of-            Cex _ -> True-            _ -> False-        assertBoolM "Must be satisfiable!" sat-    , test "can-get-value-unrelated-to-large-buffer" $ do-      withDefaultSolver $ \s -> do-        let props = [(PEq (Var "a") (Lit 0x1)), (PGT (BufLength (AbstractBuf "b")) (Lit 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeb4))]-        res <- checkSatWithProps s props-        cex :: SMTCex <- case res of-          Cex c -> pure c-          _ -> liftIO $ assertFailure "Must be satisfiable!"-        let value = subModel cex (Var "a")-        assertEqualM "Can get value out of model in the presence of large buffer!" value (Right $ Lit 0x1)-    , test "no-duplicates-with-concrete-keccak" $ do-      let props = [(PGT (Var "a") (Keccak (ConcreteBuf "abcdef"))), (PGT (Var "b") (Keccak (ConcreteBuf "abcdef")))]-      conf <- readConfig-      let SMT2 script _ _ = fromRight (internalError "Must succeed") (assertProps conf props)-      assertBoolM "There were duplicate commands in SMT encoding" $ not (hasDuplicateCommands script)-    , test "no-duplicates-with-read-assumptions" $ do-      let props = [(PGT (ReadWord (Lit 2) (AbstractBuf "test")) (Lit 0)), (PGT (Expr.padByte $ ReadByte (Lit 10) (AbstractBuf "test")) (Expr.padByte $ LitByte 1))]-      conf <- readConfig-      let SMT2 script _ _ = fromRight (internalError "Must succeed") (assertProps conf props)-      assertBoolM "There were duplicate lines in SMT encoding" $ not (hasDuplicateCommands script)-     , test "all-concrete-keccaks-discovered" $ do-      let buf1 = (Keccak (ConcreteBuf "abc"))-          eq = (Eq buf1 (Lit 0x12))-          buf2 = WriteWord eq (Lit 0x0) mempty-          props = [PEq (Keccak buf2) (Lit 0x123)]-          concrete = concreteKeccaks props-      assertEqualM "Must find two keccaks" 2 (length concrete)-    , testCase "store-over-concrete-buffer" $ runEnv (testEnv {config = testEnv.config {simp = False}}) $ do-      let-        as = AbstractStore (SymAddr "test") Nothing-        cs = ConcreteStore $ Map.fromList [(0x1,0x2)]-        e1 = SLoad (Lit 0x1) (SStore (Lit 0x8) (SLoad (Lit 0x40) as) cs)-        eq = PEq e1 (Lit 0x0)-      conf <- readConfig-      let SMT2 _ (CexVars _ _ _ storeReads _ _) _ = fromRight (internalError "Must succeed") (assertProps conf [eq])-      let expected = StorageReads $ Map.singleton (SymAddr "test", Nothing) (Set.singleton (Lit 0x40))-      assertEqualM "Reads must be properly collected" storeReads expected-    , test "all-abstract-reads-detected" $ do-      let mystore = (AbstractStore (SymAddr "test") Nothing)-      let props = [PGT (SLoad (Lit 2) mystore) (SLoad (Lit 0) mystore)]-      conf <- readConfig-      let SMT2 _ cexVars _ = fromRight (internalError "Must succeed") (assertProps conf props)-      let (StorageReads m) = cexVars.storeReads-      case Map.lookup ((SymAddr "test"), Nothing) m of-        Nothing -> assertBoolM "Address missing from storage reads" False-        Just storeReads -> assertBoolM "Did not collect all abstract reads!" $ (Set.size storeReads) == 2-  ]-  , testGroup "equivalence-checking"-    [-      test "eq-simple-diff" $ do-        Just a <- solcRuntime "C"-          [i|-            contract C {-              function stuff() public returns (uint256) {-                return 4;-              }-            }-          |]-        Just b <- solcRuntime "C"-          [i|-            contract C {-              function stuff() public returns (uint256) {-                return 5;-              }-            }-          |]-        withSolvers Bitwuzla 3 1 Nothing $ \s -> do-          calldata <- mkCalldata Nothing []-          eq <- equivalenceCheck s Nothing a b defaultVeriOpts calldata False-          assertBoolM "Must have a difference" (any (isCex . fst) eq.res)-          let cexs = mapMaybe (getCex . fst) eq.res-          assertEqualM "Must have exactly one cex" (length cexs) 1-      -- diverging gas overapproximations are caught-      -- previously, they had the same name (gas_...), so they compared equal-      , test "eq-divergent-overapprox-gas" $ do-        Just a <- solcRuntime "C"-          [i|-            contract C {-              uint x;-              function stuff(uint a) public returns (uint256) {-                unchecked { x = a * 2; }-                return gasleft();-              }-            }-          |]-        Just b <- solcRuntime "C"-          [i|-            contract C {-              uint x;-              function stuff(uint a) public returns (uint256) {-                unchecked { x = a + a; }-                return gasleft();-              }-            }-          |]-        withSolvers Bitwuzla 3 1 Nothing $ \s -> do-          calldata <- mkCalldata Nothing []-          eq <- equivalenceCheck s Nothing a b defaultVeriOpts calldata False-          assertBoolM "Must have a difference" (any (isCex . fst) eq.res)-          let cexs = mapMaybe (getCex . fst) eq.res-          assertEqualM "Must have exactly one cex" (length cexs) 1-      -- diverging gas overapproximations are caught-      -- previously, CALL fresh variables were the same so they compared equal-      , test "eq-divergent-overapprox-call" $ do-        Just a <- solcRuntime "C"-          [i|-            contract C {-              function checkval(address inputAddr, uint256 x, uint256 y) public returns (bool) {-                  bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);-                  (bool success, bytes memory returnData) = inputAddr.staticcall(data);-                  return success;-              }-            }-          |]-        Just b <- solcRuntime "C"-          [i|-            contract C {-              function checkval(address inputAddr, uint256 x, uint256 y) public returns (bool) {-                  bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, x);-                  (bool success, bytes memory returnData) = inputAddr.staticcall(data);-                  return success;-              }-            }-          |]-        withSolvers Bitwuzla 3 1 Nothing $ \s -> do-          calldata <- mkCalldata Nothing []-          eq <- equivalenceCheck s Nothing a b defaultVeriOpts calldata False-          assertBoolM "Must have a difference" (any (isCex . fst) eq.res)-          let cexs = mapMaybe (getCex . fst) eq.res-          assertEqualM "Must have exactly one cex" (length cexs) 1-      -- check bug https://github.com/argotorg/hevm/issues/679-      , test "eq-issue-with-length-cex-bug679" $ do-        let a = fromJust (hexByteString "5f610100526020610100f3")-            b = fromJust (hexByteString "5f356101f40115610100526020610100f3")-        withSolvers Z3 3 1 Nothing $ \s -> do-          calldata <- mkCalldata Nothing []-          eq <- equivalenceCheck s Nothing a b defaultVeriOpts calldata False-          assertBoolM "Must have a difference" (any (isCex . fst) eq.res)-          let cexs :: [SMTCex] = mapMaybe (getCex . fst) eq.res-          cex <- case cexs of-            [cex] -> pure cex-            _     -> liftIO $ assertFailure "Must have exactly one cex"-          let def = fromRight (error "cannot be") $ defaultSymbolicValues $ subModel cex (AbstractBuf "txdata")-          let buf = prettyBuf $ Expr.concKeccakSimpExpr def-          assertBoolM "Must start with specific string" (T.isPrefixOf "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0c" buf)-      , test "eq-yul-simple-cex" $ do-        Right aPrgm <- liftIO $ yul ""-          [i|-          {-            calldatacopy(0, 0, 32)-            switch mload(0)-            case 0 { }-            case 1 { }-            default { invalid() }-          }-          |]-        Right bPrgm <- liftIO $ yul ""-          [i|-          {-            calldatacopy(0, 0, 32)-            switch mload(0)-            case 0 { }-            case 2 { }-            default { invalid() }-          }-          |]-        withSolvers Z3 3 1 Nothing $ \s -> do-          calldata <- mkCalldata Nothing []-          eq <- equivalenceCheck s Nothing aPrgm bPrgm defaultVeriOpts calldata False-          assertBoolM "Must have a difference" (any (isCex . fst) eq.res)-      , test "constructor-same-deployed-diff" $ do-        Just initA <- solidity "C"-          [i|-            contract C {-              uint public immutable NUMBER;-              constructor(uint a) {-                NUMBER = 4;-              }-              function stuff(uint b) public returns (uint256) {-                unchecked{return 2*b+NUMBER;}-              }-            }-          |]-        Just initB <- solidity "C"-          [i|-            contract C {-              uint public immutable NUMBER;-              constructor(uint a) {-                NUMBER = 4;-              }-              function stuff(uint b) public returns (uint256) {-                unchecked {return 4*b+NUMBER;}-              }-            }-          |]-        withSolvers Z3 3 1 Nothing $ \s -> do-          calldata <- mkCalldata Nothing []-          eq <- equivalenceCheck s Nothing initA initB defaultVeriOpts calldata True-          assertBoolM "Must have difference, we return different values" (all (isCex . fst) eq.res)-      , test "constructor-same-deployed-diff2" $ do-        Just initA <- solidity "C"-          [i|-            contract C {-              uint public immutable NUMBER;-              constructor(uint a) {-                NUMBER = 4;-              }-              function stuff(uint b) public returns (uint256) {-                unchecked{return 4*b+NUMBER;}-              }-            }-          |]-        Just initB <- solidity "C"-          [i|-            contract C {-              uint public immutable NUMBER;-              constructor(uint a) {-                NUMBER = 4;-              }-              function stuff(uint b) public returns (uint256) {-                unchecked {return 4*b+NUMBER;}-              }-              function stuff_other(uint b) public returns (uint256) {-                unchecked {return 2*b+NUMBER;}-              }-            }-          |]-        withSolvers Z3 3 1 Nothing $ \s -> do-          calldata <- mkCalldata Nothing []-          eq <- equivalenceCheck s Nothing initA initB defaultVeriOpts calldata True-          assertBoolM "Must have difference, we return different values" (all (isCex . fst) eq.res)-      , test "constructor-same-deployed-diff3" $ do-        Just initA <- solidity "C"-          [i|-            contract C {-              uint public immutable NUMBER;-              constructor(uint a) {-                NUMBER = 4;-              }-              function stuff(uint b) public returns (uint256) {-                unchecked{return 4*b+NUMBER;}-              }-            }-          |]-        Just initB <- solidity "C"-          [i|-            contract C {-              uint public immutable NUMBER;-              constructor(uint a) {-                NUMBER = 4;-              }-            }-          |]-        withSolvers Z3 3 1 Nothing $ \s -> do-          calldata <- mkCalldata Nothing []-          eq <- equivalenceCheck s Nothing initA initB defaultVeriOpts calldata True-          assertBoolM "Must have difference, we return different values" (all (isCex . fst) eq.res)-      -- We set x to be 0 on deployment. Default value is also 0. So they are equivalent-      -- We cannot deal with symbolic code. However, the below will generate symbolic code,-      -- because of the parameter in the constructor that is set to NUMBER in the deployed code.-      -- Hence, this test is ignored.-      , ignoreTest $ test "constructor-diff-deploy" $ do-        Just initA <- solidity "C"-          [i|-            contract C {-              uint public immutable NUMBER;-              constructor(uint a) {-                NUMBER = a+4;-              }-              function stuff(uint b) public returns (uint256) {-                return NUMBER;-              }-            }-          |]-        Just initB <- solidity "C"-          [i|-            contract C {-              uint public immutable NUMBER;-              constructor(uint a) {-                NUMBER = a*2;-              }-              function stuff(uint b) public returns (uint256) {-                return NUMBER;-              }-            }-          |]-        withSolvers Z3 3 1 Nothing $ \s -> do-          calldata <- mkCalldata Nothing []-          eq <- equivalenceCheck s Nothing initA initB defaultVeriOpts calldata True-          assertBoolM "Must have difference" (all (isCex . fst) eq.res)-      -- We set x to be 0 on deployment. Default value is also 0. So they are equivalent-      , test "constructor-implicit" $ do-        Just initA <- solidity "C"-          [i|-            contract C {-              uint immutable x;-              function stuff(uint a) public returns (uint256) {-                unchecked {return 8+a;}-              }-            }-          |]-        Just initB <- solidity "C"-          [i|-            contract C {-              uint immutable x;-              constructor() {-                x = 0;-              }-              function stuff(uint a) public returns (uint256) {-                unchecked {return a+8;}-              }-            }-          |]-        withSolvers Z3 3 1 Nothing $ \s -> do-          calldata <- mkCalldata Nothing []-          eq <- equivalenceCheck s Nothing initA initB defaultVeriOpts calldata True-          putStrLnM $ "Equivalence result: " <> show eq-          assertEqualM "Must have no difference" [] (map fst eq.res)-      -- We set x to be 3 vs 0 (default) on deployment.-      , test "constructor-differing" $ do-        Just initA <- solidity "C"-          [i|-            contract C {-              uint x;-              function stuff(uint a) public returns (uint256) {-                unchecked {return a+x;}-              }-            }-          |]-        Just initB <- solidity "C"-          [i|-            contract C {-              uint x;-              constructor() {-                x = 3;-              }-              function stuff(uint a) public returns (uint256) {-                unchecked {return a+x;}-              }-            }-          |]-        withSolvers Z3 3 1 Nothing $ \s -> do-          calldata <- mkCalldata Nothing []-          eq <- equivalenceCheck s Nothing initA initB defaultVeriOpts calldata True-          let cexes = filter (isCex . fst) eq.res-          assertBoolM "Must have a difference" (not $ null cexes)-      , test "eq-sol-exp-qed" $ do-        Just aPrgm <- solcRuntime "C"-          [i|-            contract C {-              function a(uint8 x) public returns (uint8 b) {-                unchecked {-                  b = x*2;-                }-              }-            }-          |]-        Just bPrgm <- solcRuntime "C"-          [i|-            contract C {-              function a(uint8 x) public returns (uint8 b) {-                unchecked {-                  b = x<<1;-                }-              }-            }-          |]-        withSolvers Z3 3 1 Nothing $ \s -> do-          calldata <- mkCalldata Nothing []-          eq <- equivalenceCheck s Nothing aPrgm bPrgm defaultVeriOpts calldata False-          assertEqualM "Must have no difference" [] (map fst eq.res)-      ,-      test "eq-balance-differs" $ do-        Just aPrgm <- solcRuntime "C"-          [i|-            contract Send {-              constructor(address payable dst) payable {-                selfdestruct(dst);-              }-            }-            contract C {-              function f() public {-                new Send{value:2}(payable(address(0x0)));-              }-            }-          |]-        Just bPrgm <- solcRuntime "C"-          [i|-            contract Send {-              constructor(address payable dst) payable {-                selfdestruct(dst);-              }-            }-            contract C {-              function f() public {-                new Send{value:1}(payable(address(0x0)));-              }-            }-          |]-        withSolvers Z3 3 1 Nothing $ \s -> do-          calldata <- mkCalldata Nothing []-          eq <- equivalenceCheck s Nothing aPrgm bPrgm defaultVeriOpts calldata False-          assertBoolM "Must differ" (all (isCex . fst) eq.res)-      ,-      -- TODO: this fails because we don't check equivalence of deployed contracts-      expectFail $ test "eq-handles-contract-deployment" $ do-        Just aPrgm <- solcRuntime "B"-          [i|-            contract Send {-              constructor(address payable dst) payable {-                selfdestruct(dst);-              }-            }--            contract A {-              address parent;-              constructor(address p) {-                parent = p;-              }-              function evil() public {-                parent.call(abi.encode(B.drain.selector));-              }-            }--            contract B {-              address child;-              function a() public {-                child = address(new A(address(this)));-              }-              function drain() public {-                require(msg.sender == child);-                new Send{value: address(this).balance}(payable(address(0x0)));-              }-            }-          |]-        Just bPrgm <- solcRuntime "D"-          [i|-            contract Send {-              constructor(address payable dst) payable {-                selfdestruct(dst);-              }-            }--            contract C {-              address parent;-              constructor(address p) {-                  parent = p;-              }-            }--            contract D {-              address child;-              function a() public {-                child = address(new C(address(this)));-              }-              function drain() public {-                require(msg.sender == child);-                new Send{value: address(this).balance}(payable(address(0x0)));-              }-            }-          |]-        withSolvers Z3 3 1 Nothing $ \s -> do-          calldata <- mkCalldata Nothing []-          eq <- equivalenceCheck s Nothing aPrgm bPrgm defaultVeriOpts calldata False-          assertBoolM "Must differ" (any (isCex . fst) eq.res)-      ,-      test "eq-unknown-addr" $ do-        Just aPrgm <- solcRuntime "C"-          [i|-            contract C {-              address addr;-              function a(address a, address b) public {-                addr = a;-              }-            }-          |]-        Just bPrgm <- solcRuntime "C"-          [i|-            contract C {-              address addr;-              function a(address a, address b) public {-                addr = b;-              }-            }-          |]-        withSolvers Z3 3 1 Nothing $ \s -> do-          cd <- mkCalldata (Just (Sig "a(address,address)" [AbiAddressType, AbiAddressType])) []-          eq <- equivalenceCheck s Nothing aPrgm bPrgm defaultVeriOpts cd False-          assertEqualM "Must be different" (any (isCex . fst) eq.res) True-      ,-      test "eq-sol-exp-cex" $ do-        Just aPrgm <- solcRuntime "C"-            [i|-              contract C {-                function a(uint8 x) public returns (uint8 b) {-                  unchecked {-                    b = x*2+1;-                  }-                }-              }-            |]-        Just bPrgm <- solcRuntime "C"-          [i|-              contract C {-                function a(uint8 x) public returns (uint8 b) {-                  unchecked {-                    b =  x<<1;-                  }-                }-              }-          |]-        withSolvers Bitwuzla 3 1 Nothing $ \s -> do-          calldata <- mkCalldata Nothing []-          eq <- equivalenceCheck s Nothing aPrgm bPrgm defaultVeriOpts calldata False-          assertEqualM "Must be different" (any (isCex . fst) eq.res) True-      ,-      test "eq-storage-write-to-static-array-uint128" $ do-        Just aPrgm <- solcRuntime "C"-            [i|-              contract C {-                uint128[5] arr;-                function set(uint i, uint128 v) external returns (uint) {-                  arr[i] = v;-                  arr[i] = v;-                  return 0;-                }-              }-            |]-        Just bPrgm <- solcRuntime "C"-          [i|-              contract C {-                uint128[5] arr;-                function set(uint i, uint128 v) external returns (uint) {-                  arr[i] = v;-                  return 0;-                }-              }-          |]-        withSolvers Bitwuzla 1 1 Nothing $ \s -> do-          calldata <- mkCalldata (Just (Sig "set(uint256,uint128)" [AbiUIntType 256, AbiUIntType 128])) []-          eq <- equivalenceCheck s Nothing aPrgm bPrgm defaultVeriOpts calldata False-          assertEqualM "Must have no difference" [] (map fst eq.res)-      ,-      test "eq-storage-write-to-static-array-uint8" $ do-        Just aPrgm <- solcRuntime "C"-            [i|-              contract C {-                uint8[10] arr;-                function set(uint i, uint8 v) external returns (uint) {-                  arr[i] = v;-                  arr[i] = v;-                  return 0;-                }-              }-            |]-        Just bPrgm <- solcRuntime "C"-          [i|-              contract C {-                uint8[10] arr;-                function set(uint i, uint8 v) external returns (uint) {-                  arr[i] = v;-                  return 0;-                }-              }-          |]-        withSolvers Bitwuzla 1 1 Nothing $ \s -> do-          calldata <- mkCalldata (Just (Sig "set(uint256,uint8)" [AbiUIntType 256, AbiUIntType 8])) []-          eq <- equivalenceCheck s Nothing aPrgm bPrgm defaultVeriOpts calldata False-          assertEqualM "Must have no difference" [] (map fst eq.res)-      ,-      test "eq-storage-write-to-static-array-uint32" $ do-        Just aPrgm <- solcRuntime "C"-            [i|-              contract C {-                uint32[5] arr;-                function set(uint i, uint32 v) external returns (uint) {-                  arr[i] = 1;-                  arr[i] = v;-                  return 0;-                }-              }-            |]-        Just bPrgm <- solcRuntime "C"-          [i|-              contract C {-                uint32[5] arr;-                function set(uint i, uint32 v) external returns (uint) {-                  arr[i] = v;-                  return 0;-                }-              }-          |]-        withSolvers Bitwuzla 1 1 Nothing $ \s -> do-          calldata <- mkCalldata (Just (Sig "set(uint256,uint32)" [AbiUIntType 256, AbiUIntType 32])) []-          eq <- equivalenceCheck s Nothing aPrgm bPrgm defaultVeriOpts calldata False-          assertEqualM "Must have no difference" [] (map fst eq.res)-      , test "eq-all-yul-optimization-tests" $ do-        let opts = (defaultVeriOpts :: VeriOpts) { iterConf = defaultIterConf {maxIter = Just 5, askSmtIters = 20, loopHeuristic = Naive }}-            ignoredTests =-                    -- unbounded loop ---                    [ "commonSubexpressionEliminator/branches_for.yul"-                    , "conditionalSimplifier/no_opt_if_break_is_not_last.yul"-                    , "conditionalUnsimplifier/no_opt_if_break_is_not_last.yul"-                    , "expressionSimplifier/inside_for.yul"-                    , "forLoopConditionIntoBody/cond_types.yul"-                    , "forLoopConditionIntoBody/simple.yul"-                    , "fullSimplify/inside_for.yul"-                    , "fullSuite/no_move_loop_orig.yul"-                    , "loopInvariantCodeMotion/multi.yul"-                    , "redundantAssignEliminator/for_deep_simple.yul"-                    , "unusedAssignEliminator/for_deep_noremove.yul"-                    , "unusedAssignEliminator/for_deep_simple.yul"-                    , "ssaTransform/for_def_in_init.yul"-                    , "loopInvariantCodeMotion/simple_state.yul"-                    , "loopInvariantCodeMotion/simple.yul"-                    , "loopInvariantCodeMotion/recursive.yul"-                    , "loopInvariantCodeMotion/no_move_staticall_returndatasize.yul"-                    , "loopInvariantCodeMotion/no_move_state_loop.yul"-                    , "loopInvariantCodeMotion/no_move_state.yul" -- not infinite, but rollaround on a large int-                    , "loopInvariantCodeMotion/no_move_loop.yul"--                    -- infinite recursion-                    , "unusedStoreEliminator/function_side_effects_2.yul"-                    , "unusedStoreEliminator/write_before_recursion.yul"-                    , "fullInliner/multi_fun_callback.yul"-                    , "conditionalUnsimplifier/side_effects_of_functions.yul"-                    , "expressionInliner/double_recursive_calls.yul"-                    , "conditionalSimplifier/side_effects_of_functions.yul"--                    -- Takes too long, would timeout on most test setups.-                    -- We could probably fix these by "bunching together" queries-                    , "reasoningBasedSimplifier/mulmod.yul"-                    , "loadResolver/multi_sload_loop.yul"-                    , "reasoningBasedSimplifier/mulcheck.yul"-                    , "reasoningBasedSimplifier/smod.yul"-                    , "fullSuite/abi_example1.yul"-                    , "yulOptimizerTests/fullInliner/large_function_multi_use.yul"-                    , "loadResolver/merge_known_write_with_distance.yul"-                    , "loadResolver/second_mstore_with_delta.yul"-                    , "rematerialiser/for_continue_2.yul"-                    , "rematerialiser/for_continue_with_assignment_in_post.yul"--                    -- invalid test ---                    -- https://github.com/ethereum/solidity/issues/9500-                    , "commonSubexpressionEliminator/object_access.yul"-                    , "expressionSplitter/object_access.yul"--                    -- stack too deep ---                    , "fullSuite/abi2.yul"-                    , "fullSuite/aztec.yul"-                    , "stackCompressor/inlineInBlock.yul"-                    , "stackCompressor/inlineInFunction.yul"-                    , "stackCompressor/unusedPrunerWithMSize.yul"-                    , "wordSizeTransform/function_call.yul"-                    , "fullInliner/no_inline_into_big_function.yul"-                    , "controlFlowSimplifier/switch_only_default.yul"-                    , "stackLimitEvader" -- all that are in this subdirectory--                    -- typed yul ---                    , "conditionalSimplifier/add_correct_type_wasm.yul"-                    , "conditionalSimplifier/add_correct_type.yul"-                    , "disambiguator/for_statement.yul"-                    , "disambiguator/funtion_call.yul"-                    , "disambiguator/if_statement.yul"-                    , "disambiguator/long_names.yul"-                    , "disambiguator/switch_statement.yul"-                    , "disambiguator/variables_clash.yul"-                    , "disambiguator/variables_inside_functions.yul"-                    , "disambiguator/variables.yul"-                    , "expressionInliner/simple.yul"-                    , "expressionInliner/with_args.yul"-                    , "expressionSplitter/typed.yul"-                    , "fullInliner/multi_return_typed.yul"-                    , "functionGrouper/empty_block.yul"-                    , "functionGrouper/multi_fun_mixed.yul"-                    , "functionGrouper/nested_fun.yul"-                    , "functionGrouper/single_fun.yul"-                    , "functionHoister/empty_block.yul"-                    , "functionHoister/multi_mixed.yul"-                    , "functionHoister/nested.yul"-                    , "functionHoister/single.yul"-                    , "mainFunction/empty_block.yul"-                    , "mainFunction/multi_fun_mixed.yul"-                    , "mainFunction/nested_fun.yul"-                    , "mainFunction/single_fun.yul"-                    , "ssaTransform/typed_for.yul"-                    , "ssaTransform/typed_switch.yul"-                    , "ssaTransform/typed.yul"-                    , "varDeclInitializer/typed.yul"--                    -- New: symbolic index on MSTORE/MLOAD/CopySlice/CallDataCopy/ExtCodeCopy/Revert,-                    --      or exponent is symbolic (requires symbolic gas)-                    --      or SHA3 offset symbolic-                    , "blockFlattener/basic.yul"-                    , "commonSubexpressionEliminator/case2.yul"-                    , "equalStoreEliminator/indirect_inferrence.yul"-                    , "expressionJoiner/reassignment.yul"-                    , "expressionSimplifier/exp_simplifications.yul"-                    , "expressionSimplifier/zero_length_read.yul"-                    , "expressionSimplifier/side_effects_in_for_condition.yul"-                    , "fullSuite/create_and_mask.yul"-                    , "fullSuite/unusedFunctionParameterPruner_return.yul"-                    , "fullSuite/unusedFunctionParameterPruner_simple.yul"-                    , "fullSuite/unusedFunctionParameterPruner.yul"-                    , "loadResolver/double_mload_with_other_reassignment.yul"-                    , "loadResolver/double_mload_with_reassignment.yul"-                    , "loadResolver/double_mload.yul"-                    , "loadResolver/keccak_reuse_basic.yul"-                    , "loadResolver/keccak_reuse_expr_mstore.yul"-                    , "loadResolver/keccak_reuse_msize.yul"-                    , "loadResolver/keccak_reuse_mstore.yul"-                    , "loadResolver/keccak_reuse_reassigned_branch.yul"-                    , "loadResolver/keccak_reuse_reassigned_value.yul"-                    , "loadResolver/keccak_symbolic_memory.yul"-                    , "loadResolver/merge_mload_with_known_distance.yul"-                    , "loadResolver/mload_self.yul"-                    , "loadResolver/keccak_reuse_in_expression.yul"-                    , "loopInvariantCodeMotion/complex_move.yul"-                    , "loopInvariantCodeMotion/move_memory_function.yul"-                    , "loopInvariantCodeMotion/move_state_function.yul"-                    , "loopInvariantCodeMotion/no_move_memory.yul"-                    , "loopInvariantCodeMotion/no_move_storage.yul"-                    , "loopInvariantCodeMotion/not_first.yul"-                    , "ssaAndBack/single_assign_if.yul"-                    , "ssaAndBack/single_assign_switch.yul"-                    , "structuralSimplifier/switch_inline_no_match.yul"-                    , "unusedFunctionParameterPruner/simple.yul"-                    , "unusedStoreEliminator/covering_calldatacopy.yul"-                    , "unusedStoreEliminator/remove_before_revert.yul"-                    , "unusedStoreEliminator/unknown_length2.yul"-                    , "unusedStoreEliminator/unrelated_relative.yul"-                    , "fullSuite/extcodelength.yul"-                    , "unusedStoreEliminator/create_inside_function.yul"-- "trying to reset symbolic storage with writes in create"--                    -- Due to tstorage warnings treated as errors when running solc with --standard-json-                    --   these cannot be currently run. See: https://github.com/ethereum/solidity/issues/15397-                    --   When that fix comes to upstream, we can re-enabled again-                    , "equalStoreEliminator/transient_storage.yul"-                    , "unusedStoreEliminator/tload.yul"-                    , "unusedStoreEliminator/tstore.yul"-                    , "yulOptimizerTests/fullSuite/transient_storage.yul"-                    , "yulOptimizerTests/unusedPruner/transient_storage.yul"--                    -- Bug in solidity, fixed in newer versions:-                    -- https://github.com/ethereum/solidity/issues/15397#event-14116827816-                    , "no_move_transient_storage.yul"-                    ]--        solcRepo <- liftIO $ fromMaybe (internalError "cannot find solidity repo") <$> (lookupEnv "HEVM_SOLIDITY_REPO")-        let testDir = solcRepo <> "/test/libyul/yulOptimizerTests"-        dircontents <- liftIO $ System.Directory.listDirectory testDir-        let-          fullpaths = map ((testDir ++ "/") ++) dircontents-          recursiveList :: [FilePath] -> [FilePath] -> IO [FilePath]-          recursiveList (a:ax) b =  do-              isdir <- doesDirectoryExist a-              case isdir of-                True  -> do-                    fs <- System.Directory.listDirectory a-                    let fs2 = map ((a ++ "/") ++) fs-                    recursiveList (ax++fs2) b-                False -> recursiveList ax (a:b)-          recursiveList [] b = pure b-        files <- liftIO $ recursiveList fullpaths []-        let filesFiltered = filter (\file -> not $ any (`List.isInfixOf` file) ignoredTests) files--        -- Takes one file which follows the Solidity Yul optimizer unit tests format,-        -- extracts both the nonoptimized and the optimized versions, and checks equivalence.-        forM_ filesFiltered (\f-> do-          liftIO $ print f-          origcont <- liftIO $ readFile f-          let-            onlyAfter pattern (a:ax) = if a =~ pattern then (a:ax) else onlyAfter pattern ax-            onlyAfter _ [] = []-            replaceOnce pat repl inp = go inp [] where-              go (a:ax) b = if a =~ pat then let a2 = replaceAll repl $ a *=~ pat in b ++ a2:ax-                                        else go ax (b ++ [a])-              go [] b = b--            -- takes a yul program and ensures memory is symbolic by prepending-            -- `calldatacopy(0,0,1024)`. (calldata is symbolic, but memory starts empty).-            -- This forces the exploration of more branches, and makes the test vectors a-            -- little more thorough.-            symbolicMem (a:ax) = if a =~ [re|"^ *object"|] then-                                      let a2 = replaceAll "a calldatacopy(0,0,1024)" $ a *=~ [re|code {|]-                                      in (a2:ax)-                                    else replaceOnce [re|^ *{|] "{\ncalldatacopy(0,0,1024)" $ onlyAfter [re|^ *{|] (a:ax)-            symbolicMem _ = internalError "Program too short"--            unfiltered = lines origcont-            filteredASym = symbolicMem [ x | x <- unfiltered, (not $ x =~ [re|^//|]) && (not $ x =~ [re|^$|]) ]-            filteredBSym = symbolicMem [ replaceAll "" $ x *=~[re|^//|] | x <- onlyAfter [re|^// step:|] unfiltered, not $ x =~ [re|^$|] ]-          start <- liftIO $ getCurrentTime-          putStrLnM $ "Checking file: " <> f-          conf <- readConfig-          when conf.debug $ liftIO $ do-            putStrLnM "-------------Original Below-----------------"-            mapM_ putStrLn unfiltered-            putStrLnM "------------- Filtered A + Symb below-----------------"-            mapM_ putStrLn filteredASym-            putStrLnM "------------- Filtered B + Symb below-----------------"-            mapM_ putStrLn filteredBSym-            putStrLnM "------------- END -----------------"-          Right aPrgm <- liftIO $ yul "" $ T.pack $ unlines filteredASym-          Right bPrgm <- liftIO $ yul "" $ T.pack $ unlines filteredBSym-          procs <- liftIO $ getNumProcessors-          withSolvers CVC5 (unsafeInto procs) 1 (Just 100) $ \s -> do-            calldata <- mkCalldata Nothing []-            eq <- equivalenceCheck s Nothing aPrgm bPrgm opts calldata False-            let res = map fst eq.res-            end <- liftIO $ getCurrentTime-            case any isCex res of-              False -> liftIO $ do-                print $ "OK. Took " <> (show $ diffUTCTime end start) <> " seconds"-                let timeouts = filter isUnknown res-                let errors = filter isError res-                unless (null timeouts && null errors) $ do-                  putStrLnM $ "But " <> (show $ length timeouts) <> " timeout(s) and " <>  (show $ length errors) <> " error(s) occurred"-                  internalError "Encountered timeout(s) and/or error(s)"-              True -> liftIO $ do-                putStrLnM $ "Not OK: " <> show f <> " Got: " <> show res-                internalError "Was NOT equivalent"-           )-    ]-  ]-  where-    (===>) = assertSolidityComputation--checkEquivProp :: App m => Prop -> Prop -> m Bool-checkEquivProp a b = fmap (fromMaybe True) $ checkEquivBase (\l r -> PNeg (PImpl l r .&& PImpl r l)) a b True--checkEquivPropAndLHS :: App m => Prop -> Prop -> m Bool-checkEquivPropAndLHS orig simp = do-  let lhsConst = Expr.checkLHSConstProp simp-  equiv <- checkEquivProp orig simp-  pure $ lhsConst && equiv--checkEquiv :: (Typeable a, App m) => Expr a -> Expr a -> m Bool-checkEquiv a b = do-  opts <- readConfig-  if a == b then pure True else do-    when (opts.debug) $ liftIO $ putStrLn $ "Checking equivalence of " <> show a <> " and " <> show b-    x <- checkEquivBase (./=) a b True-    when (opts.debug) $ liftIO $ putStrLn $ "UNSAT check, expect True: " <> show x-    y <- checkEquivBase (.==) a b False-    when (opts.debug) $ liftIO $ putStrLn $ "SAT check, expect False: " <> show y-    pure $ (fromMaybe True x) && not (fromMaybe False y)--checkEquivAndLHS :: (Typeable a, App m) => Expr a -> Expr a -> m Bool-checkEquivAndLHS orig simp = do-  opts <- readConfig-  let lhsConst =  Expr.checkLHSConst simp-  when (opts.debug) $ liftIO $ putStrLn $ "LHS const: " <> show lhsConst-  equiv <- checkEquiv orig simp-  pure $ lhsConst && equiv--checkEquivBase :: (Eq a, App m) => (a -> a -> Prop) -> a -> a -> Bool -> m (Maybe Bool)-checkEquivBase mkprop l r expect = do-  config <- readConfig-  let noSimplifyEnv = Env {config = config {simp = False}}-  liftIO $ runEnv noSimplifyEnv $ do-    withSolvers Z3 1 1 (Just 1) $ \solvers -> do-      res <- checkSatWithProps solvers [mkprop l r]-      let ret = case res of-            Qed -> Just True-            Cex {} -> Just False-            Error _ -> Just (not expect)-            Unknown _ -> Nothing-      when (ret == Just (not expect)) $ liftIO $ print res-      pure ret---- | Takes a runtime code and calls it with the provided calldata---- | Takes a creation code and some calldata, runs the creation code, and calls the resulting contract with the provided calldata-runSimpleVM :: App m => ByteString -> ByteString -> m (Maybe ByteString)-runSimpleVM x ins = do-  loadVM x >>= \case-    Nothing -> pure Nothing-    Just vm -> do-     let calldata = (ConcreteBuf ins)-         vm' = set (#state % #calldata) calldata vm-     res <- Stepper.interpret (Fetch.zero 0 Nothing) vm' Stepper.execFully-     case res of-       Right (ConcreteBuf bs) -> pure $ Just bs-       s -> internalError $ show s---- | Takes a creation code and returns a vm with the result of executing the creation code-loadVM :: App m => ByteString -> m (Maybe (VM Concrete))-loadVM x = do-  vm <- liftIO $ stToIO $ vmForEthrunCreation x-  vm1 <- Stepper.interpret (Fetch.zero 0 Nothing) vm Stepper.runFully-  case vm1.result of-    Just (VMSuccess (ConcreteBuf targetCode)) -> do-      let target = vm1.state.contract-      vm2 <- Stepper.interpret (Fetch.zero 0 Nothing) vm1 (prepVm target targetCode)-      writeTrace vm2-      pure $ Just vm2-    _ -> pure Nothing-  where-    prepVm target targetCode = Stepper.evm $ do-      replaceCodeOfSelf (RuntimeCode $ ConcreteRuntimeCode targetCode)-      resetState-      assign (#state % #gas) 0xffffffffffffffff -- kludge-      execState (loadContract target) <$> get >>= put-      get--hex :: ByteString -> ByteString-hex s =-  case BS16.decodeBase16Untyped s of-    Right x -> x-    Left e -> internalError $ T.unpack e--singleContract :: Text -> Text -> IO (Maybe ByteString)-singleContract x s =-  solidity x [i|-    pragma experimental ABIEncoderV2;-    contract ${x} { ${s} }-  |]--defaultDataLocation :: AbiType -> Text-defaultDataLocation t =-  if (case t of-        AbiBytesDynamicType -> True-        AbiStringType -> True-        AbiArrayDynamicType _ -> True-        AbiArrayType _ _ -> True-        _ -> False)-  then "memory"-  else ""--runFunction :: App m => Text -> ByteString -> m (Maybe ByteString)-runFunction c input = do-  x <- liftIO $ singleContract "X" c-  runSimpleVM (fromJust x) input--runStatements :: App m => Text -> [AbiValue] -> AbiType -> m (Maybe ByteString)-runStatements stmts args t = do-  let params =-        T.intercalate ", "-          (map (\(x, c) -> abiTypeSolidity (abiValueType x)-                             <> " " <> defaultDataLocation (abiValueType x)-                             <> " " <> T.pack [c])-            (zip args "abcdefg"))-      s =-        "foo(" <> T.intercalate ","-                    (map (abiTypeSolidity . abiValueType) args) <> ")"--  runFunction [i|-    function foo(${params}) public pure returns (${abiTypeSolidity t} ${defaultDataLocation t} x) {-      ${stmts}-    }-  |] (abiMethod s (AbiTuple $ V.fromList args))--getStaticAbiArgs :: Int -> VM Symbolic -> [Expr EWord]-getStaticAbiArgs n vm =-  let cd = vm.state.calldata-  in decodeStaticArgs 4 n cd---- includes shaving off 4 byte function sig-decodeAbiValues :: [AbiType] -> ByteString -> [AbiValue]-decodeAbiValues types bs =-  let xy = case decodeAbiValue (AbiTupleType $ V.fromList types) (BS.fromStrict (BS.drop 4 bs)) of-        AbiTuple xy' -> xy'-        _ -> internalError "AbiTuple expected"-  in V.toList xy---- abi types that are supported in the symbolic abi encoder-newtype SymbolicAbiType = SymbolicAbiType AbiType-  deriving (Eq, Show)--newtype SymbolicAbiVal = SymbolicAbiVal AbiValue-  deriving (Eq, Show)--instance Arbitrary SymbolicAbiVal where-  arbitrary = do-    SymbolicAbiType ty <- arbitrary-    SymbolicAbiVal <$> genAbiValue ty--instance Arbitrary SymbolicAbiType where-  arbitrary = SymbolicAbiType <$> frequency-    [ (5, (AbiUIntType . (* 8)) <$> choose (1, 32))-    , (5, (AbiIntType . (* 8)) <$> choose (1, 32))-    , (5, pure AbiAddressType)-    , (5, pure AbiBoolType)-    , (5, AbiBytesType <$> choose (1,32))-    , (1, do SymbolicAbiType ty <- scale (`div` 2) arbitrary-             AbiArrayType <$> (choose (1, 30)) <*> pure ty-      )-    ]--newtype Bytes = Bytes ByteString-  deriving Eq--instance Show Bytes where-  showsPrec _ (Bytes x) _ = show (BS.unpack x)--instance Arbitrary Bytes where-  arbitrary = fmap (Bytes . BS.pack) arbitrary--newtype RLPData = RLPData RLP-  deriving (Eq, Show)---- bias towards bytestring to try to avoid infinite recursion-instance Arbitrary RLPData where-  arbitrary = frequency-   [(5, do-           Bytes bytes <- arbitrary-           return $ RLPData $ BS bytes)-   , (1, do-         k <- choose (0,10)-         ls <- vectorOf k arbitrary-         return $ RLPData $ List [r | RLPData r <- ls])-   ]--instance Arbitrary Word128 where-  arbitrary = liftM2 fromHiAndLo arbitrary arbitrary--instance Arbitrary Word160 where-  arbitrary = liftM2 fromHiAndLo arbitrary arbitrary--instance Arbitrary Word256 where-  arbitrary = liftM2 fromHiAndLo arbitrary arbitrary--instance Arbitrary W64 where-  arbitrary = fmap W64 arbitrary--instance Arbitrary W256 where-  arbitrary = fmap W256 arbitrary--instance Arbitrary Addr where-  arbitrary = fmap Addr arbitrary--instance Arbitrary (Expr EAddr) where-  arbitrary = oneof-    [ fmap LitAddr arbitrary-    , fmap SymAddr (genName "addr")-    ]--instance Arbitrary (Expr Storage) where-  arbitrary = sized genStorage--instance Arbitrary (Expr EWord) where-  arbitrary = sized defaultWord--instance Arbitrary (Expr Byte) where-  arbitrary = sized genByte--newtype SymbolicJoinBytes = SymbolicJoinBytes [Expr Byte]-  deriving (Eq, Show)--instance Arbitrary SymbolicJoinBytes where-  arbitrary = liftM SymbolicJoinBytes $ replicateM 32 arbitrary--joinBytesFromList :: [Expr Byte] -> Expr EWord-joinBytesFromList [a0, a1, a2, a3, a4, a5, a6, a7,-                   a8, a9, a10, a11, a12, a13, a14, a15,-                   a16, a17, a18, a19, a20, a21, a22, a23,-                   a24, a25, a26, a27, a28, a29, a30, a31] =-  JoinBytes a0 a1 a2 a3 a4 a5 a6 a7-            a8 a9 a10 a11 a12 a13 a14 a15-            a16 a17 a18 a19 a20 a21 a22 a23-            a24 a25 a26 a27 a28 a29 a30 a31-joinBytesFromList _ = internalError "List must contain exactly 32 elements"--instance Arbitrary (Expr Buf) where-  arbitrary = sized defaultBuf--instance Arbitrary (Expr End) where-  arbitrary = sized genEnd--instance Arbitrary (ContractCode) where-  arbitrary = oneof-    [ fmap UnknownCode arbitrary-    , liftM2 InitCode arbitrary arbitrary-    , fmap RuntimeCode arbitrary-    ]--instance Arbitrary (RuntimeCode) where-  arbitrary = oneof-    [ fmap ConcreteRuntimeCode arbitrary-    , fmap SymbolicRuntimeCode arbitrary-    ]--instance Arbitrary (V.Vector (Expr Byte)) where-  arbitrary = fmap V.fromList (listOf1 arbitrary)--instance Arbitrary (Expr EContract) where-  arbitrary = sized genEContract---- LitOnly-newtype LitOnly a = LitOnly a-  deriving (Show, Eq)--newtype LitWord (sz :: Nat) = LitWord (Expr EWord)-  deriving (Show)--instance (KnownNat sz) => Arbitrary (LitWord sz) where-  arbitrary = LitWord <$> genLit (fromInteger v)-    where-      v = natVal (Proxy @sz)--instance Arbitrary (LitOnly (Expr Byte)) where-  arbitrary = LitOnly . LitByte <$> arbitrary--instance Arbitrary (LitOnly (Expr EWord)) where-  arbitrary = LitOnly . Lit <$> arbitrary--instance Arbitrary (LitOnly (Expr Buf)) where-  arbitrary = LitOnly . ConcreteBuf <$> arbitrary--genEContract :: Int -> Gen (Expr EContract)-genEContract sz = do-  c <- arbitrary-  b <- defaultWord sz-  n <- arbitrary-  s <- genStorage sz-  ts <- genStorage sz-  pure $ C {code=c, storage=s, tStorage=ts, balance=b, nonce=n}---- ZeroDepthWord-newtype ZeroDepthWord = ZeroDepthWord (Expr EWord)-  deriving (Show, Eq)--instance Arbitrary ZeroDepthWord where-  arbitrary = do-    fmap ZeroDepthWord . sized $ genWord 0---- WriteWordBuf-newtype WriteWordBuf = WriteWordBuf (Expr Buf)-  deriving (Show, Eq)--instance Arbitrary WriteWordBuf where-  arbitrary = do-    let mkBuf = oneof-          [ pure $ ConcreteBuf ""       -- empty-          , fmap ConcreteBuf arbitrary  -- concrete-          , sized (genBuf 100)          -- overlapping writes-          , arbitrary                   -- sparse writes-          ]-    fmap WriteWordBuf mkBuf---- GenCopySliceBuf-newtype GenCopySliceBuf = GenCopySliceBuf (Expr Buf)-  deriving (Show, Eq)--instance Arbitrary GenCopySliceBuf where-  arbitrary = do-    let mkBuf = oneof-          [ pure $ ConcreteBuf ""-          , fmap ConcreteBuf arbitrary-          , arbitrary-          ]-    fmap GenCopySliceBuf mkBuf---- GenWriteStorageExpr-newtype GenWriteStorageExpr = GenWriteStorageExpr (Expr EWord, Expr Storage)-  deriving (Show, Eq)--instance Arbitrary GenWriteStorageExpr where-  arbitrary = do-    slot <- arbitrary-    let mkStore = oneof-          [ pure $ ConcreteStore mempty-          , fmap ConcreteStore arbitrary-          , do-              -- generate some write chains where we know that at least one-              -- write matches either the input addr, or both the input-              -- addr and slot-              let addWrites :: Expr Storage -> Int -> Gen (Expr Storage)-                  addWrites b 0 = pure b-                  addWrites b n = liftM3 SStore arbitrary arbitrary (addWrites b (n - 1))-              s <- arbitrary-              addMatch <- fmap (SStore slot) arbitrary-              let withMatch = addMatch s-              newWrites <- oneof [ pure 0, pure 1, fmap (`mod` 5) arbitrary ]-              addWrites withMatch newWrites-          , arbitrary-          ]-    store <- mkStore-    pure $ GenWriteStorageExpr (slot, store)---- GenWriteByteIdx-newtype GenWriteByteIdx = GenWriteByteIdx (Expr EWord)-  deriving (Show, Eq)--instance Arbitrary GenWriteByteIdx where-  arbitrary = do-    -- 1st: can never overflow an Int-    -- 2nd: can overflow an Int-    let mkIdx = frequency [ (10, genLit 1_000_000) , (1, fmap Lit arbitrary) ]-    fmap GenWriteByteIdx mkIdx--newtype LitProp = LitProp Prop-  deriving (Show, Eq)--instance Arbitrary LitProp where-  arbitrary = LitProp <$> sized (genProp True)---newtype StorageExp = StorageExp (Expr EWord)-  deriving (Show, Eq)--instance Arbitrary StorageExp where-  arbitrary = StorageExp <$> (genStorageExp)--genStorageExp :: Gen (Expr EWord)-genStorageExp = do-  fromPos <- genSlot-  storage <- genStorageWrites-  pure $ SLoad fromPos storage--genSlot :: Gen (Expr EWord)-genSlot = frequency [ (1, do-                        buf <- genConcreteBufSlot 64-                        case buf of-                          (ConcreteBuf b) -> do-                            key <- genLit 10-                            pure $ Expr.MappingSlot b key-                          _ -> internalError "impossible"-                        )-                     -- map element-                     ,(2, do-                        l <- genLit 10-                        buf <- genConcreteBufSlot 64-                        pure $ Add (Keccak buf) l)-                    -- Array element-                     ,(2, do-                        l <- genLit 10-                        buf <- genConcreteBufSlot 32-                        pure $ Add (Keccak buf) l)-                     -- member of the Contract-                     ,(2, pure $ Lit 20)-                     -- array element-                     ,(2, do-                        arrayNum :: Int <- arbitrary-                        offs :: W256 <- arbitrary-                        pure $ Lit $ fst (Expr.preImages !! (arrayNum `mod` 3)) + (offs `mod` 3))-                     -- random stuff-                     ,(1, pure $ Lit (maxBound :: W256))-                     ]---- Generates an N-long buffer, all with the same value, at most 8 different ones-genConcreteBufSlot :: Int -> Gen (Expr Buf)-genConcreteBufSlot len = do-  b :: Word8 <- arbitrary-  pure $ ConcreteBuf $ BS.pack ([ 0 | _ <- [0..(len-2)]] ++ [b])--genStorageWrites :: Gen (Expr Storage)-genStorageWrites = do-  toSlot <- genSlot-  val <- genLit (maxBound :: W256)-  store <- frequency [ (3, pure $ AbstractStore (SymAddr "") Nothing)-                     , (2, genStorageWrites)-                     ]-  pure $ SStore toSlot val store--instance Arbitrary Prop where-  arbitrary = sized (genProp False)--genProps :: Bool -> Int -> Gen [Prop]-genProps onlyLits sz2 = listOf $ genProp onlyLits sz2--genProp :: Bool -> Int -> Gen (Prop)-genProp _ 0 = PBool <$> arbitrary-genProp onlyLits sz = oneof-  [ liftM2 PEq subWord subWord-  , liftM2 PLT subWord subWord-  , liftM2 PGT subWord subWord-  , liftM2 PLEq subWord subWord-  , liftM2 PGEq subWord subWord-  , fmap PNeg subProp-  , liftM2 PAnd subProp subProp-  , liftM2 POr subProp subProp-  , liftM2 PImpl subProp subProp-  ]-  where-    subWord = if onlyLits then frequency [(2, Lit <$> arbitrary)-                                         ,(1, pure $ Lit 0)-                                         ,(1, pure $ Lit Expr.maxLit)-                                         ]-                          else genWord 1 (sz `div` 2)-    subProp = genProp onlyLits (sz `div` 2)--genByte :: Int -> Gen (Expr Byte)-genByte 0 = fmap LitByte arbitrary-genByte sz = oneof-  [ liftM2 IndexWord subWord subWord-  , liftM2 ReadByte subWord subBuf-  ]-  where-    subWord = defaultWord (sz `div` 10)-    subBuf = defaultBuf (sz `div` 10)--genLit :: W256 -> Gen (Expr EWord)-genLit bound = do-  w <- arbitrary-  pure $ Lit (w `mod` bound)--genNat :: Gen Int-genNat = fmap unsafeInto (arbitrary :: Gen Natural)--genName :: String -> Gen Text--- In order not to generate SMT reserved words, we prepend with "esc_"-genName ty = fmap (T.pack . (("esc_" <> ty <> "_") <> )) $ listOf1 (oneof . (fmap pure) $ ['a'..'z'] <> ['A'..'Z'])--genEnd :: Int -> Gen (Expr End)-genEnd 0 = oneof-  [ fmap (Failure mempty mempty . UnrecognizedOpcode) arbitrary-  , pure $ Failure mempty mempty IllegalOverflow-  , pure $ Failure mempty mempty SelfDestruction-  ]-genEnd sz = oneof-  [ liftM3 Failure subProp (pure mempty) (fmap Revert subBuf)-  , liftM4 Success subProp (pure mempty) subBuf arbitrary-  -- TODO Partial-  ]-  where-    subBuf = defaultBuf (sz `div` 2)-    subProp = genProps False (sz `div` 2)--genWord :: Int -> Int -> Gen (Expr EWord)-genWord litFreq 0 = frequency-  [ (litFreq, do-      val <- frequency-       [ (10, fmap (`mod` 100) arbitrary)-       , (1, pure 0)-       , (1, pure Expr.maxLit)-       , (1, arbitrary)-       ]-      pure $ Lit val-    )-  , (1, oneof-      [ pure Origin-      , pure Coinbase-      , pure Timestamp-      , pure BlockNumber-      , pure PrevRandao-      , pure GasLimit-      , pure ChainId-      , pure BaseFee-      --, liftM2 SelfBalance arbitrary arbitrary-      --, liftM2 Gas arbitrary arbitrary-      , fmap Lit arbitrary-      , fmap joinBytesFromList $ replicateM 32 arbitrary-      , fmap Var (genName "word")-      ]-    )-  ]-genWord litFreq sz = frequency-  [ (litFreq, do-      val <- frequency-       [ (10, fmap (`mod` 100) arbitrary)-       , (1, arbitrary)-       ]-      pure $ Lit val-    )-  , (1, oneof-    [ liftM2 Add subWord subWord-    , liftM2 Sub subWord subWord-    , liftM2 Mul subWord subWord-    , liftM2 Div subWord subWord-    , liftM2 SDiv subWord subWord-    , liftM2 Mod subWord subWord-    , liftM2 SMod subWord subWord-    --, liftM3 AddMod subWord subWord subWord-    --, liftM3 MulMod subWord subWord subWord -- it works, but it's VERY SLOW-    --, liftM2 Exp subWord litWord-    , liftM2 SEx subWord subWord-    , liftM2 Min subWord subWord-    , liftM2 LT subWord subWord-    , liftM2 GT subWord subWord-    , liftM2 LEq subWord subWord-    , liftM2 GEq subWord subWord-    , liftM2 SLT subWord subWord-    --, liftM2 SGT subWord subWord-    , liftM2 Eq subWord subWord-    , fmap IsZero subWord-    , liftM2 And subWord subWord-    , liftM2 Or subWord subWord-    , liftM2 Xor subWord subWord-    , fmap Not subWord-    , liftM2 SHL subWord subWord-    , liftM2 SHR subWord subWord-    , liftM2 SAR subWord subWord-    , fmap BlockHash subWord-    --, liftM3 Balance arbitrary arbitrary subWord-    --, fmap CodeSize subWord-    --, fmap ExtCodeHash subWord-    , fmap Keccak subBuf-    , fmap SHA256 subBuf-    , liftM2 SLoad subWord subStore-    , liftM2 ReadWord genReadIndex subBuf-    , fmap BufLength subBuf-    , do-      one <- subByte-      two <- subByte-      three <- subByte-      four <- subByte-      five <- subByte-      six <- subByte-      seven <- subByte-      eight <- subByte-      nine <- subByte-      ten <- subByte-      eleven <- subByte-      twelve <- subByte-      thirteen <- subByte-      fourteen <- subByte-      fifteen <- subByte-      sixteen <- subByte-      seventeen <- subByte-      eighteen <- subByte-      nineteen <- subByte-      twenty <- subByte-      twentyone <- subByte-      twentytwo <- subByte-      twentythree <- subByte-      twentyfour <- subByte-      twentyfive <- subByte-      twentysix <- subByte-      twentyseven <- subByte-      twentyeight <- subByte-      twentynine <- subByte-      thirty <- subByte-      thirtyone <- subByte-      thirtytwo <- subByte-      pure $ JoinBytes-        one two three four five six seven eight nine ten-        eleven twelve thirteen fourteen fifteen sixteen-        seventeen eighteen nineteen twenty twentyone-        twentytwo twentythree twentyfour twentyfive-        twentysix twentyseven twentyeight twentynine-        thirty thirtyone thirtytwo-    ])-  ]- where-   subWord = genWord litFreq (sz `div` 5)-   subBuf = defaultBuf (sz `div` 10)-   subStore = genStorage (sz `div` 10)-   subByte = genByte (sz `div` 10)-   genReadIndex = do-    o :: (Expr EWord) <- subWord-    pure $ case o of-      Lit w -> Lit $ w `mod` into (maxBound :: Word64)-      _ -> o--genWordArith :: Int -> Int -> Gen (Expr EWord)-genWordArith litFreq 0 = frequency-  [ (litFreq, fmap Lit arbitrary)-  , (1, oneof [ fmap Lit arbitrary ])-  ]-genWordArith litFreq sz = frequency-  [ (litFreq, fmap Lit arbitrary)-  , (20, frequency-    [ (20, liftM2 Add  subWord subWord)-    , (20, liftM2 Sub  subWord subWord)-    , (20, liftM2 Mul  subWord subWord)-    , (20, liftM2 SEx  subWord subWord)-    , (20, liftM2 Xor  subWord subWord)-    -- these reduce variability-    , (3 , liftM2 Min  subWord subWord)-    , (3 , liftM2 Div  subWord subWord)-    , (3 , liftM2 SDiv subWord subWord)-    , (3 , liftM2 Mod  subWord subWord)-    , (3 , liftM2 SMod subWord subWord)-    , (3 , liftM2 SHL  subWord subWord)-    , (3 , liftM2 SHR  subWord subWord)-    , (3 , liftM2 SAR  subWord subWord)-    , (3 , liftM2 Or   subWord subWord)-    -- comparisons, reducing variability greatly-    , (1 , liftM2 LEq  subWord subWord)-    , (1 , liftM2 GEq  subWord subWord)-    , (1 , liftM2 SLT  subWord subWord)-    --(1, , liftM2 SGT subWord subWord-    , (1 , liftM2 Eq   subWord subWord)-    , (1 , liftM2 And  subWord subWord)-    , (1 , fmap IsZero subWord        )-    -- Expensive below-    --(1,  liftM3 AddMod subWord subWord subWord-    --(1,  liftM3 MulMod subWord subWord subWord-    --(1,  liftM2 Exp subWord litWord-    ])-  ]- where-   subWord = genWordArith (litFreq `div` 2) (sz `div` 2)----- Finds SLoad -> SStore. This should not occur in most scenarios--- as we can simplify them away-badStoresInExpr :: [Expr a] -> Bool-badStoresInExpr exprs = any (getAny . foldExpr match mempty) exprs-  where-      match (SLoad _ (SStore _ _ _)) = Any True-      match _ = Any False--defaultBuf :: Int -> Gen (Expr Buf)-defaultBuf = genBuf (4_000_000)--defaultWord :: Int -> Gen (Expr EWord)-defaultWord = genWord 10--maybeBoundedLit :: W256 -> Gen (Expr EWord)-maybeBoundedLit bound = do-  o <- (arbitrary :: Gen (Expr EWord))-  pure $ case o of-        Lit w -> Lit $ w `mod` bound-        _ -> o--genBuf :: W256 -> Int -> Gen (Expr Buf)-genBuf _ 0 = oneof-  [ fmap AbstractBuf (genName "buf")-  , fmap ConcreteBuf arbitrary-  ]-genBuf bound sz = oneof-  [ liftM3 WriteWord (maybeBoundedLit bound) subWord subBuf-  , liftM3 WriteByte (maybeBoundedLit bound) subByte subBuf-  -- we don't generate copyslice instances where:-  --   - size is abstract-  --   - size > 100 (due to unrolling in SMT.hs)-  --   - literal dstOffsets are > 4,000,000 (due to unrolling in SMT.hs)-  -- n.b. that 4,000,000 is the theoretical maximum memory size given a 30,000,000 block gas limit-  , liftM5 CopySlice genReadIndex (maybeBoundedLit bound) smolLitWord subBuf subBuf-  ]-  where-    -- copySlice gets unrolled in the generated SMT so we can't go too crazy here-    smolLitWord = do-      w <- arbitrary-      pure $ Lit (w `mod` 100)-    subWord = defaultWord (sz `div` 5)-    subByte = genByte (sz `div` 10)-    subBuf = genBuf bound (sz `div` 10)-    genReadIndex = do-      o :: (Expr EWord) <- subWord-      pure $ case o of-        Lit w -> Lit $ w `mod` into (maxBound :: Word64)-        _ -> o--genStorage :: Int -> Gen (Expr Storage)-genStorage 0 = oneof-  [ liftM2 AbstractStore arbitrary (pure Nothing)-  , fmap ConcreteStore $ resize 5 arbitrary-  ]-genStorage sz = liftM3 SStore key val subStore-  where-    subStore = genStorage (sz `div` 10)-    val = defaultWord (sz `div` 5)-    key = genStorageKey--genStorageKey :: Gen (Expr EWord)-genStorageKey = frequency-     -- array slot-    [ (4, liftM2 Expr.ArraySlotWithOffs (genByteStringKey 32) (genLit 5))-    , (4, fmap Expr.ArraySlotZero (genByteStringKey 32))-     -- mapping slot-    , (8, liftM2 Expr.MappingSlot (genByteStringKey 64) (genLit 5))-     -- small slot-    , (4, genLit 20)-    -- unrecognized slot type-    , (1, genLit 5)-    ]--genByteStringKey :: W256 -> Gen (ByteString)-genByteStringKey len = do-  b :: Word8 <- arbitrary-  pure $ BS.pack ([ 0 | _ <- [0..(len-2)]] ++ [b `mod` 5])---- GenWriteStorageLoad-newtype GenWriteStorageLoad = GenWriteStorageLoad (Expr EWord)-  deriving (Show, Eq)--instance Arbitrary GenWriteStorageLoad where-  arbitrary = do-    load <- genStorageLoad 10-    pure $ GenWriteStorageLoad load--    where-      genStorageLoad :: Int -> Gen (Expr EWord)-      genStorageLoad sz = liftM2 SLoad key subStore-        where-          subStore = genStorage (sz `div` 10)-          key = genStorageKey--data Invocation-  = SolidityCall Text [AbiValue]-  deriving Show--assertSolidityComputation :: App m => Invocation -> AbiValue -> m ()-assertSolidityComputation (SolidityCall s args) x =-  do y <- runStatements s args (abiValueType x)-     liftIO $ assertEqual (T.unpack s)-       (fmap Bytes (Just (encodeAbiValue x)))-       (fmap Bytes y)--bothM :: (Monad m) => (a -> m b) -> (a, a) -> m (b, b)-bothM f (a, a') = do-  b  <- f a-  b' <- f a'-  return (b, b')--applyPattern :: String -> TestTree  -> TestTree-applyPattern p = localOption (TestPattern (parseExpr p))--checkBadCheatCode :: Text -> Postcondition-checkBadCheatCode sig _ = \case-  (Failure _ c (Revert _)) -> case mapMaybe findBadCheatCode (concatMap flatten c.traces) of-    (s:_) -> (ConcreteBuf $ into s.unFunctionSelector) ./= (ConcreteBuf $ selector sig)-    _ -> PBool True-  _ -> PBool True-  where-    findBadCheatCode :: Trace -> Maybe FunctionSelector-    findBadCheatCode Trace { tracedata = td } = case td of-      ErrorTrace (BadCheatCode _ s) -> Just s-      _ -> Nothing--allBranchesFail :: App m => ByteString -> Maybe Sig -> m (Either [SMTCex] [Expr End])-allBranchesFail = checkPost p-  where-    p _ = \case-      Success _ _ _ _ -> PBool False-      _ -> PBool True--reachableUserAsserts :: App m => ByteString -> Maybe Sig -> m (Either [SMTCex] [Expr End])-reachableUserAsserts = checkPost (checkAssertions [0x01])--checkPost :: App m => Postcondition -> ByteString -> Maybe Sig -> m (Either [SMTCex] [Expr End])-checkPost post c sig = do-  (e, res) <- withDefaultSolver $ \s -> verifyContract s c sig [] defaultVeriOpts Nothing post-  let cexs = snd <$> mapMaybe getCex res-  case cexs of-    [] -> pure $ Right e-    cs -> pure $ Left cs--successGen :: [Prop] -> Expr End-successGen props = Success props mempty (ConcreteBuf "") mempty---- gets the expected concrete values for symbolic abi testing-expectedConcVals :: Text -> AbiValue -> SMTCex-expectedConcVals nm val = case val of-  AbiUInt {} -> mempty { vars = Map.fromList [(Var nm, mkWord val)] }-  AbiInt {} -> mempty { vars = Map.fromList [(Var nm, mkWord val)] }-  AbiAddress {} -> mempty { addrs = Map.fromList [(SymAddr nm, truncateToAddr (mkWord val))] }-  AbiBool {} -> mempty { vars = Map.fromList [(Var nm, mkWord val)] }-  AbiBytes {} -> mempty { vars = Map.fromList [(Var nm, mkWord val)] }-  AbiArray _ _ vals -> mconcat . V.toList . V.imap (\(T.pack . show -> idx) v -> expectedConcVals (nm <> "-a-" <> idx) v) $ vals-  AbiTuple vals -> mconcat . V.toList . V.imap (\(T.pack . show -> idx) v -> expectedConcVals (nm <> "-t-" <> idx) v) $ vals-  _ -> internalError $ "unsupported Abi type " <> show nm <> " val: " <> show val <> " val type: " <> showAlter val-  where-    mkWord = word . encodeAbiValue--hexStringToByteString :: String -> BS.ByteString-hexStringToByteString hexStr-    | odd (length hexStr) = error "Hex string has an odd length"-    | otherwise = case traverse hexPairToByte (pairs hexStr) of-        Just bytes -> (BS.pack bytes)-        Nothing -> error "Invalid hex string"-  where-    -- Convert a pair of hex characters to a byte-    hexPairToByte :: (Char, Char) -> Maybe Word8-    hexPairToByte (c1, c2) = do-        b1 <- hexCharToDigit c1-        b2 <- hexCharToDigit c2-        return $ fromIntegral (b1 * 16 + b2)--    -- Convert a single hex character to its integer value-    hexCharToDigit :: Char -> Maybe Int-    hexCharToDigit c-        | c >= '0' && c <= '9' = Just (digitToInt c)-        | c >= 'a' && c <= 'f' = Just (digitToInt c)-        | c >= 'A' && c <= 'F' = Just (digitToInt c)-        | otherwise = Nothing--    -- Split a string into pairs of characters-    pairs :: [a] -> [(a, a)]-    pairs [] = []-    pairs (x:y:xs) = (x, y) : pairs xs-    pairs _ = error "Unexpected odd length"+import Control.Monad+import Control.Monad.ST (stToIO)+import Control.Monad.State.Strict+import Control.Monad.IO.Unlift+import Control.Monad.Reader (ReaderT)+import Data.Bits hiding (And, Xor)+import Data.ByteString (ByteString)+import Data.ByteString qualified as BS+import Data.ByteString.Base16 qualified as BS16+import Data.ByteString.Lazy qualified as BSLazy+import Data.Binary.Put (runPut)+import Data.Binary.Get (runGetOrFail)+import Data.Either+import Data.List qualified as List+import Data.Map.Strict qualified as Map+import Data.Maybe+import Data.Set qualified as Set+import Data.String.Here+import Data.Text (Text)+import Data.Text qualified as T+import Data.Tuple.Extra+import Data.Tree (flatten)+import Data.Vector qualified as V+import Test.Tasty+import Test.Tasty.QuickCheck hiding (Failure, Success)+import Test.QuickCheck.Instances.Text()+import Test.QuickCheck.Instances.Natural()+import Test.QuickCheck.Instances.ByteString()+import Test.Tasty.HUnit+import Test.Tasty.Runners hiding (Failure, Success)+import Test.Tasty.ExpectedFailure+import Text.ParserCombinators.ReadP (readP_to_S)+import Witch (unsafeInto, into)++import Optics.Core hiding (pre, re, elements)+import Optics.State++import EVM+import EVM.ABI+import EVM.Assembler+import EVM.ConsoleLog (formatConsoleLog)+import EVM.Exec+import EVM.Expr qualified as Expr+import EVM.Fetch qualified as Fetch+import EVM.Format (hexText)+import EVM.Precompiled+import EVM.RLP+import EVM.SMT hiding (one)+import EVM.Solidity+import EVM.Solvers+import EVM.Stepper qualified as Stepper+import EVM.SymExec+import EVM.Test.FuzzSymExec qualified as FuzzSymExec+import EVM.Types hiding (Env)+import EVM.Effects+import EVM.UnitTest (writeTrace, printWarnings)+import EVM.Expr (maybeLitByteSimp)+import EVM.Keccak (concreteKeccaks)++import EVM.Expr.ExprTests qualified as ExprTests+import EVM.ConcreteExecution.ConcreteExecutionTests qualified as ConcreteExecutionTests+import EVM.Equivalence.EquivalenceTests qualified as EquivalenceTests+import EVM.SymExec.SymExecTests qualified as SymExecTests+import EVM.Test.FoundryTests qualified as FoundryTests++testEnv :: Env+testEnv = Env { config = defaultConfig {+  dumpQueries = False+  , dumpExprs = False+  , dumpEndStates = False+  , debug = False+  , dumpTrace = False+  , decomposeStorage = True+  , verb = 1+  } }++putStrLnM :: (MonadUnliftIO m) => String -> m ()+putStrLnM a = liftIO $ putStrLn a++assertEqualM :: (App m, Eq a, Show a, HasCallStack) => String -> a -> a -> m ()+assertEqualM a b c = liftIO $ assertEqual a b c++assertBoolM+  :: (MonadUnliftIO m, HasCallStack)+  => String -> Bool -> m ()+assertBoolM a b = liftIO $ assertBool a b++exactlyCex :: Int -> [VerifyResult] -> Bool+exactlyCex n results = let numcex = sum $ map (fromEnum . isCex) results+  in numcex == n && length results == n++test :: TestName -> ReaderT Env IO () -> TestTree+test a b = testCase a $ runEnv testEnv b++testNoSimplify :: TestName -> ReaderT Env IO () -> TestTree+testNoSimplify a b = let testEnvNoSimp = Env { config = testEnv.config { simp = False } }+  in testCase a $ runEnv testEnvNoSimp b++prop :: Testable prop => ReaderT Env IO prop -> Property+prop a = ioProperty $ runEnv testEnv a++propNoSimp :: Testable prop => ReaderT Env IO prop -> Property+propNoSimp a = let testEnvNoSimp = Env { config = testEnv.config { simp = False } }+  in ioProperty $ runEnv testEnvNoSimp a++withDefaultSolver :: App m => (SolverGroup -> m a) -> m a+withDefaultSolver = withSolvers Z3 3 Nothing defMemLimit++withCVC5Solver :: App m => (SolverGroup -> m a) -> m a+withCVC5Solver = withSolvers CVC5 3 Nothing defMemLimit+++withBitwuzlaSolver :: App m => (SolverGroup -> m a) -> m a+withBitwuzlaSolver = withSolvers Bitwuzla 3 Nothing defMemLimit+++main :: IO ()+main = defaultMain tests++-- | run a subset of tests in the repl. p is a tasty pattern:+-- https://github.com/UnkindPartition/tasty/tree/ee6fe7136fbcc6312da51d7f1b396e1a2d16b98a#patterns+runSubSet :: String -> IO ()+runSubSet p = defaultMain . applyPattern p $ tests++tests :: TestTree+tests = testGroup "hevm"+  [ FuzzSymExec.tests+  , ExprTests.tests+  , ConcreteExecutionTests.tests+  , SymExecTests.tests+  , EquivalenceTests.tests+  , FoundryTests.tests+  , testGroup "Console log formatting"+    [ testCase "format-string" $ do+        let encoded = ConcreteBuf $ abiMethod "log(string)" (AbiTuple $ V.fromList [AbiString "hello world"])+        assertEqual "console.log(string) format" "console::log(\"hello world\")" (formatConsoleLog encoded)+    , testCase "format-uint" $ do+        let encoded = ConcreteBuf $ abiMethod "log(uint256)" (AbiTuple $ V.fromList [AbiUInt 256 42])+        assertEqual "console.log(uint256) format" "console::log(42)" (formatConsoleLog encoded)+    , testCase "format-string-uint" $ do+        let encoded = ConcreteBuf $ abiMethod "log(string,uint256)" (AbiTuple $ V.fromList [AbiString "count", AbiUInt 256 7])+        assertEqual "console.log(string,uint256) format" "console::log(\"count\", 7)" (formatConsoleLog encoded)+    , testCase "format-bool" $ do+        let encoded = ConcreteBuf $ abiMethod "log(bool)" (AbiTuple $ V.fromList [AbiBool True])+        assertEqual "console.log(bool) format" "console::log(true)" (formatConsoleLog encoded)+    , testCase "format-address" $ do+        let encoded = ConcreteBuf $ abiMethod "log(address)" (AbiTuple $ V.fromList [AbiAddress 0xdeadbeef])+        assertEqual "console.log(address) format" "console::log(0x00000000000000000000000000000000DeaDBeef)" (formatConsoleLog encoded)+    , testCase "format-no-args" $ do+        let encoded = ConcreteBuf $ abiMethod "log()" (AbiTuple $ V.fromList [])+        assertEqual "console.log() format" "console::log()" (formatConsoleLog encoded)+    , testCase "format-unknown-selector" $ do+        let encoded = ConcreteBuf $ BS.pack [0xde, 0xad, 0xbe, 0xef, 0x01, 0x02]+        let result = formatConsoleLog encoded+        assertBool "unknown selector should produce hex fallback" (T.isPrefixOf "console::log(0x" result)+    , testCase "format-short-input" $ do+        let encoded = ConcreteBuf $ BS.pack [0x01, 0x02]+        assertEqual "short input format" "console::log()" (formatConsoleLog encoded)+    ]+  , testGroup "StorageTests"+    [ test "accessStorage uses fetchedStorage" $ do+        let dummyContract =+              (initialContract (RuntimeCode (ConcreteRuntimeCode mempty)))+                { external = True }+        vm :: VM Concrete <- liftIO $ stToIO $ vmForEthrunCreation ""+        -- perform the initial access+        let ?conf = testEnv.config+        vm1 <- liftIO $ stToIO $ execStateT (EVM.accessStorage (LitAddr 0) (Lit 0) (pure . pure ())) vm+        -- it should fetch the contract first+        vm2 <- case vm1.result of+                Just (HandleEffect (Query (PleaseFetchContract _addr _ continue))) ->+                  liftIO $ stToIO $ execStateT (continue dummyContract) vm1+                _ -> internalError "unexpected result"+            -- then it should fetch the slow+        vm3 <- case vm2.result of+                    Just (HandleEffect (Query (PleaseFetchSlot _addr _slot continue))) ->+                      liftIO $ stToIO $ execStateT (continue 1337) vm2+                    _ -> internalError "unexpected result"+            -- perform the same access as for vm1+        vm4 <- liftIO $ stToIO $ execStateT (EVM.accessStorage (LitAddr 0) (Lit 0) (pure . pure ())) vm3++        -- there won't be query now as accessStorage uses fetch cache+        assertBoolM (show vm4.result) (isNothing vm4.result)+    ]+  , testGroup "ABI"+    [ testProperty "Put/get inverse" $ \x ->+        case runGetOrFail (getAbi (abiValueType x)) (runPut (putAbi x)) of+          Right ("", _, x') -> x' == x+          _ -> False+    , test "ABI-negative-small-int" $ do+        let bs = hex "ffffd6" -- -42 as int24+        let padded = BS.replicate (32 - BS.length bs) 0 <> bs -- padded to 32 bytes+        let withSelector = BS.replicate 4 0 <> padded -- added extra 4 bytes, simulating selector+        case decodeAbiValues [AbiIntType 24] withSelector of+          [AbiInt 24 val] -> assertEqualM "Incorrectly decoded int24 value" (-42) val+          _ -> internalError "Error in decoding function"+    , test "ABI-function-roundtrip" $ do+        -- Test that AbiFunction encodes/decodes correctly+        let addr = 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef+        let sel = 0x12345678+        let funcVal = AbiFunction addr sel+        case runGetOrFail (getAbi AbiFunctionType) (runPut (putAbi funcVal)) of+          Right ("", _, decoded) -> assertEqualM "Function roundtrip failed" funcVal decoded+          Left (_, _, err) -> internalError $ "Decoding error: " <> err+          Right (leftover, _, _) -> internalError $ "Leftover bytes: " <> show leftover+    , test "ABI-function-encoding" $ do+        -- Test that AbiFunction encodes to correct 32-byte padded format+        let addr = 0x1234567890abcdef1234567890abcdef12345678+        let sel = 0xaabbccdd+        let funcVal = AbiFunction addr sel+        let encoded = BSLazy.toStrict $ runPut (putAbi funcVal)+        -- Should be 32 bytes: 20 addr + 4 selector + 8 padding+        assertEqualM "Encoded length should be 32" 32 (BS.length encoded)+        -- First 20 bytes should be address+        assertEqualM "Address bytes" (hex "1234567890abcdef1234567890abcdef12345678") (BS.take 20 encoded)+        -- Next 4 bytes should be selector+        assertEqualM "Selector bytes" (hex "aabbccdd") (BS.take 4 $ BS.drop 20 encoded)+        -- Last 8 bytes should be zero padding+        assertEqualM "Padding bytes" (BS.replicate 8 0) (BS.drop 24 encoded)+    , test "ABI-function-parsing" $ do+        -- Test parseAbiValue for function type+        let hexStr = "0x1234567890abcdef1234567890abcdef12345678aabbccdd"+        case readP_to_S (parseAbiValue AbiFunctionType) hexStr of+          [(AbiFunction addr sel, "")] -> do+            assertEqualM "Parsed address" 0x1234567890abcdef1234567890abcdef12345678 addr+            assertEqualM "Parsed selector" 0xaabbccdd sel+          [] -> internalError "Failed to parse function value"+          other -> internalError $ "Unexpected parse result: " <> show other+    , test "ABI-function-parsing-rejects-wrong-length" $ do+        -- 23 bytes (too short)+        let shortHex = "0x1234567890abcdef1234567890abcdef123456aabbcc"+        case readP_to_S (parseAbiValue AbiFunctionType) shortHex of+          [] -> pure ()  -- Expected: parsing should fail+          _ -> internalError "Should reject 23-byte function value"+        -- 25 bytes (too long)+        let longHex = "0x1234567890abcdef1234567890abcdef12345678aabbccddee"+        case readP_to_S (parseAbiValue AbiFunctionType) longHex of+          [(_, "")] -> internalError "Should reject 25-byte function value"+          _ -> pure ()  -- Expected: either fails or has leftover+    , test "ABI-bytes-parsing-validates-length" $ do+        -- bytes4 should require exactly 4 bytes+        let fourBytes = "0xaabbccdd"+        case readP_to_S (parseAbiValue (AbiBytesType 4)) fourBytes of+          [(AbiBytes 4 bs, "")] -> assertEqualM "bytes4 value" (hex "aabbccdd") bs+          _ -> internalError "Failed to parse bytes4"+        -- bytes4 should reject 3 bytes+        let threeBytes = "0xaabbcc"+        case readP_to_S (parseAbiValue (AbiBytesType 4)) threeBytes of+          [] -> pure ()  -- Expected: parsing should fail+          _ -> internalError "Should reject 3-byte value for bytes4"+    , test "ABI-user-defined-enum-type" $ do+        -- User-defined types like enums (e.g. "Order.OrderType") should parse as uint8+        assertEqualM "qualified enum" (Just (AbiUIntType 8)) (parseTypeName mempty "Order.OrderType")+        assertEqualM "simple enum" (Just (AbiUIntType 8)) (parseTypeName mempty "MyEnum")+        -- Should also work with array suffixes+        assertEqualM "enum array" (Just (AbiArrayDynamicType (AbiUIntType 8))) (parseTypeName mempty "Order.OrderType[]")+        -- Standard types should still work+        assertEqualM "uint256" (Just (AbiUIntType 256)) (parseTypeName mempty "uint256")+        assertEqualM "address" (Just AbiAddressType) (parseTypeName mempty "address")+    ]+  , testGroup "Solidity-Expressions"+    [ test "Trivial" $+        SolidityCall "x = 3;" []+          ===> AbiUInt 256 3+    , test "Arithmetic" $ do+        SolidityCall "x = a + 1;"+          [AbiUInt 256 1] ===> AbiUInt 256 2+        SolidityCall "unchecked { x = a - 1; }"+          [AbiUInt 8 0] ===> AbiUInt 8 255+    , test "negative-numbers-nonzero-comp-1" $ do+        Just c <- solcRuntime "C" [i|+            contract C {+              function fun(int256 x) public {+                  // Cheatcode address+                  address vm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;+                  bytes memory data = abi.encodeWithSignature("assertGe(int256,int256)", x, -1);+                  (bool success, ) = vm.staticcall(data);+                  assert(success == true);+              }+            } |]+        let sig = Just $ Sig "fun(int256)" [AbiIntType 256]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertBoolM "The expression must not be partial" $ not (any isPartial e)+        let numCexes = sum $ map (fromEnum . isCex) ret+        let numErrs = sum $ map (fromEnum . isError) ret+        assertEqualM "number of counterexamples" 1 numCexes+        assertEqualM "number of errors" 0 numErrs+    , test "negative-numbers-nonzero-comp-2" $ do+        Just c <- solcRuntime "C" [i|+            contract C {+              function fun(int256 x) public {+                  // Cheatcode address+                  address vm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;+                  bytes memory data = abi.encodeWithSignature("assertGe(int256,int256)", x, 1);+                  (bool success, ) = vm.staticcall(data);+                  assert(success == true);+              }+            } |]+        let sig = Just $ Sig "fun(int256)" [AbiIntType 256]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertBoolM "The expression must not be partial" $ not (any isPartial e)+        let numCexes = sum $ map (fromEnum . isCex) ret+        let numErrs = sum $ map (fromEnum . isError) ret+        assertEqualM "number of counterexamples" 1 numCexes+        assertEqualM "number of errors" 0 numErrs+    , test "negative-numbers-min" $ do+        Just c <- solcRuntime "C" [i|+            contract C {+              function fun(int256 x) public {+                  // Cheatcode address+                  address vm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;+                  bytes memory data = abi.encodeWithSignature("assertLt(int256,int256)", x, type(int256).min);+                  (bool success, ) = vm.staticcall(data);+                  assert(success == true);+              }+            } |]+        let sig = Just $ Sig "fun(int256)" [AbiIntType 256]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertBoolM "The expression must not be partial" $ not (any isPartial e)+        let numCexes = sum $ map (fromEnum . isCex) ret+        let numErrs = sum $ map (fromEnum . isError) ret+        assertEqualM "number of counterexamples" 1 numCexes+        assertEqualM "number of errors" 0 numErrs+    , test "negative-numbers-int128-1" $ do+        Just c <- solcRuntime "C" [i|+            contract C {+              function fun(int128 y) public {+                  int256 x = int256(y);+                  // Cheatcode address+                  address vm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;+                  bytes memory data = abi.encodeWithSignature("assertLt(int256,int256)", x, -1);+                  (bool success, ) = vm.staticcall(data);+                  assert(success == true);+              }+            } |]+        let sig = Just $ Sig "fun(int128)" [AbiIntType 128]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertBoolM "The expression must not be partial" $ not (any isPartial e)+        let numCexes = sum $ map (fromEnum . isCex) ret+        let numErrs = sum $ map (fromEnum . isError) ret+        assertEqualM "number of counterexamples" 1 numCexes+        assertEqualM "number of errors" 0 numErrs+    , test "negative-numbers-zero-comp-simpleassert" $ do+        Just c <- solcRuntime "C" [i|+            contract C {+              function fun(int256 x) public {+                assert(x >= 0);+              }+            } |]+        let sig = Just $ Sig "fun(int256)" [AbiIntType 256]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertBoolM "The expression must not be partial" $ not (any isPartial e)+        let numCexes = sum $ map (fromEnum . isCex) ret+        let numErrs = sum $ map (fromEnum . isError) ret+        assertEqualM "number of counterexamples" 1 numCexes+        assertEqualM "number of errors" 0 numErrs+    , test "signed-int8-range" $ do+        Just c <- solcRuntime "C" [i|+          contract C {+            function fun(int8 x) public {+              int256 y = x;+              assert (y != 1000);+            }+          } |]+        let sig = Just $ Sig "fun(int8)" [AbiIntType 8]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertBoolM "The expression must not be partial" $ not (any isPartial e)+        let numCexes = sum $ map (fromEnum . isCex) ret+        let numErrs = sum $ map (fromEnum . isError) ret+        assertEqualM "number of counterexamples" 0 numCexes+        assertEqualM "number of errors" 0 numErrs+    , test "base-2-exp-uint8" $ do+        Just c <- solcRuntime "C" [i|+          contract C {+            function fun(uint8 x) public {+              unchecked {+                require(x < 10);+                uint256 y = 2**x;+                assert (y <= 512);+              }+            }+          } |]+        let sig = Just $ Sig "fun(uint8)" [AbiUIntType 8]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertBoolM "The expression must not be partial" $ not (any isPartial e)+        let numCexes = sum $ map (fromEnum . isCex) ret+        let numErrs = sum $ map (fromEnum . isError) ret+        assertEqualM "number of counterexamples" 0 numCexes+        assertEqualM "number of errors" 0 numErrs+    , test "base-2-exp-no-rollaround" $ do+        Just c <- solcRuntime "C" [i|+          contract C {+            function fun(uint256 x) public {+              unchecked {+                require(x > 10);+                require(x < 256);+                uint256 y = 2**x;+                assert (y > 512);+              }+            }+          } |]+        let sig = Just $ Sig "fun(uint256)" [AbiUIntType 256]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertBoolM "The expression must not be partial" $ not (any isPartial e)+        let numCexes = sum $ map (fromEnum . isCex) ret+        let numErrs = sum $ map (fromEnum . isError) ret+        assertEqualM "number of counterexamples" 0 numCexes+        assertEqualM "number of errors" 0 numErrs+    , test "base-2-exp-rollaround" $ do+        Just c <- solcRuntime "C" [i|+          contract C {+            function fun(uint256 x) public {+              unchecked {+                require(x == 256);+                uint256 y = 2**x;+                assert (y > 512);+              }+            }+          } |]+        let sig = Just $ Sig "fun(uint256)" [AbiUIntType 256]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertBoolM "The expression must not be partial" $ not (any isPartial e)+        let numCexes = sum $ map (fromEnum . isCex) ret+        let numErrs = sum $ map (fromEnum . isError) ret+        assertEqualM "number of counterexamples" 1 numCexes+        assertEqualM "number of errors" 0 numErrs+    , test "unsigned-int8-range" $ do+        Just c <- solcRuntime "C" [i|+          contract C {+            function fun(uint8 x) public {+              uint256 y = x;+              assert (y != 1000);+            }+          } |]+        let sig = Just $ Sig "fun(uint8)" [AbiUIntType 8]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertBoolM "The expression must not be partial" $ not (any isPartial e)+        let numCexes = sum $ map (fromEnum . isCex) ret+        let numErrs = sum $ map (fromEnum . isError) ret+        assertEqualM "number of counterexamples" 0 numCexes+        assertEqualM "number of errors" 0 numErrs+    , test "negative-numbers-zero-comp" $ do+        Just c <- solcRuntime "C" [i|+            contract C {+              function fun(int256 x) public {+                  // Cheatcode address+                  address vm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;+                  bytes memory data = abi.encodeWithSignature("assertGe(int256,int256)", x, 0);+                  (bool success, ) = vm.staticcall(data);+                  assert(success == true);+              }+            } |]+        let sig = Just $ Sig "fun(int256)" [AbiIntType 256]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertBoolM "The expression must not be partial" $ not (any isPartial e)+        let numCexes = sum $ map (fromEnum . isCex) ret+        let numErrs = sum $ map (fromEnum . isError) ret+        assertEqualM "number of counterexamples" 1 numCexes+        assertEqualM "number of errors" 0 numErrs+    , test "positive-numbers-cex" $ do+        Just c <- solcRuntime "C" [i|+            contract C {+              function fun(uint256 x) public {+                  // Cheatcode address+                  address vm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;+                  bytes memory data = abi.encodeWithSignature("assertGe(uint256,uint256)", x, 1);+                  (bool success, ) = vm.staticcall(data);+                  assert(success == true);+              }+            } |]+        let sig = Just $ Sig "fun(uint256)" [AbiUIntType 256]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertBoolM "The expression must not be partial" $ not (any isPartial e)+        let numCexes = sum $ map (fromEnum . isCex) ret+        let numErrs = sum $ map (fromEnum . isError) ret+        assertEqualM "number of counterexamples" 1 numCexes+        assertEqualM "number of errors" 0 numErrs+    , test "positive-numbers-qed" $ do+        Just c <- solcRuntime "C" [i|+            contract C {+              function fun(uint256 x) public {+                  // Cheatcode address+                  address vm = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;+                  bytes memory data = abi.encodeWithSignature("assertGe(uint256,uint256)", x, 0);+                  (bool success, ) = vm.staticcall(data);+                  assert(success == true);+              }+            } |]+        let sig = Just $ Sig "fun(uint256)" [AbiUIntType 256]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertBoolM "The expression must not be partial" $ not (any isPartial e)+        let numCexes = sum $ map (fromEnum . isCex) ret+        let numErrs = sum $ map (fromEnum . isError) ret+        assertEqualM "number of counterexamples" 0 numCexes+        assertEqualM "number of errors" 0 numErrs++    , test "keccak256()" $+        SolidityCall "x = uint(keccak256(abi.encodePacked(a)));"+          [AbiString ""] ===> AbiUInt 256 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470++    , testProperty "symbolic-abi-enc-vs-solidity" $ \(SymbolicAbiVal y) -> prop $ do+          Just encoded <- runStatements [i| x = abi.encode(a);|] [y] AbiBytesDynamicType+          let solidityEncoded = case decodeAbiValue (AbiTupleType $ V.fromList [AbiBytesDynamicType]) (BS.fromStrict encoded) of+                AbiTuple (V.toList -> [e]) -> e+                _ -> internalError "AbiTuple expected"+          let+              frag = [symAbiArg 64 "y" (AbiTupleType $ V.fromList [abiValueType y])]+              (hevmEncoded, _) = first (Expr.drop 4) $ combineFragments frag (ConcreteBuf "")+              expectedVals = expectedConcVals "y" (AbiTuple . V.fromList $ [y])+              hevmConcretePre = fromRight (error "cannot happen") $ subModel expectedVals hevmEncoded+              hevmConcrete = case Expr.simplify hevmConcretePre of+                               ConcreteBuf b -> b+                               buf -> internalError ("valMap: " <> show expectedVals <> "\ny:" <> show y <> "\n" <> "buf: " <> show buf)+          -- putStrLnM $ "frag: " <> show frag+          -- putStrLnM $ "expectedVals: " <> show expectedVals+          -- putStrLnM $ "frag: " <> show frag+          -- putStrLnM $ "hevmEncoded: " <> show hevmEncoded+          -- putStrLnM $ "solidity encoded: " <> show solidityEncoded+          -- putStrLnM $ "our encoded     : " <> show (AbiBytesDynamic hevmConcrete)+          -- putStrLnM $ "y     : " <> show y+          -- putStrLnM $ "y type: " <> showAlter y+          -- putStrLnM $ "hevmConcretePre: " <> show hevmConcretePre+          assertEqualM "abi encoding mismatch" solidityEncoded (AbiBytesDynamic hevmConcrete)+    , testProperty "symbolic-abi encoding-vs-solidity-2-args" $ \(SymbolicAbiVal x', SymbolicAbiVal y') -> prop $ do+          Just encoded <- runStatements [i| x = abi.encode(a, b);|] [x', y'] AbiBytesDynamicType+          let solidityEncoded = case decodeAbiValue (AbiTupleType $ V.fromList [AbiBytesDynamicType]) (BS.fromStrict encoded) of+                AbiTuple (V.toList -> [e]) -> e+                _ -> internalError "AbiTuple expected"+          let hevmEncoded = encodeAbiValue (AbiTuple $ V.fromList [x',y'])+          assertEqualM "abi encoding mismatch" solidityEncoded (AbiBytesDynamic hevmEncoded)+    , testProperty "abi-encoding-vs-solidity" $ forAll (arbitrary >>= genAbiValue) $+      \y -> prop $ do+          Just encoded <- runStatements [i| x = abi.encode(a);|]+            [y] AbiBytesDynamicType+          let solidityEncoded = case decodeAbiValue (AbiTupleType $ V.fromList [AbiBytesDynamicType]) (BS.fromStrict encoded) of+                AbiTuple (V.toList -> [e]) -> e+                _ -> internalError "AbiTuple expected"+          let hevmEncoded = encodeAbiValue (AbiTuple $ V.fromList [y])+          assertEqualM "abi encoding mismatch" solidityEncoded (AbiBytesDynamic hevmEncoded)++    , testProperty "abi-encoding-vs-solidity-2-args" $ forAll (arbitrary >>= bothM genAbiValue) $+      \(x', y') -> prop $ do+          Just encoded <- runStatements [i| x = abi.encode(a, b);|]+            [x', y'] AbiBytesDynamicType+          let solidityEncoded = case decodeAbiValue (AbiTupleType $ V.fromList [AbiBytesDynamicType]) (BS.fromStrict encoded) of+                AbiTuple (V.toList -> [e]) -> e+                _ -> internalError "AbiTuple expected"+          let hevmEncoded = encodeAbiValue (AbiTuple $ V.fromList [x',y'])+          assertEqualM "abi encoding mismatch" solidityEncoded (AbiBytesDynamic hevmEncoded)++    -- we need a separate test for this because the type of a function is "function() external" in solidity but just "function" in the abi:+    , askOption $ \(QuickCheckTests n) -> testProperty "abi-encoding-vs-solidity-function-pointer" $ withMaxSuccess (min n 20) $ forAll (genAbiValue AbiFunctionType) $+      \y -> prop $ do+          Just encoded <- runFunction [i|+              function foo(function() external a) public pure returns (bytes memory x) {+                x = abi.encode(a);+              }+            |] (abiMethod "foo(function)" (AbiTuple (V.singleton y)))+          let solidityEncoded = case decodeAbiValue (AbiTupleType $ V.fromList [AbiBytesDynamicType]) (BS.fromStrict encoded) of+                AbiTuple (V.toList -> [e]) -> e+                _ -> internalError "AbiTuple expected"+          let hevmEncoded = encodeAbiValue (AbiTuple $ V.fromList [y])+          assertEqualM "abi encoding mismatch" solidityEncoded (AbiBytesDynamic hevmEncoded)+    ]++  , testGroup "Dynamic-bytes-concretization"+    [ test "bytes-length-cex" $ do+        -- A function that asserts bytes.length == 0 should have a counterexample+        Just c <- solcRuntime "C" [i|+            contract C {+              function fun(bytes calldata data) public pure {+                assert(data.length == 0);+              }+            } |]+        let sig = Just $ Sig "fun(bytes)" [AbiBytesDynamicType]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        -- bytes args don't produce a per-path Partial; they attach a program-wide Caveat+        (_, caveats) <- mkCalldata sig []+        assertBoolM "bounded dyn arg must not produce a synthetic partial" $ not (any isPartial e)+        assertBoolM "bounded dyn arg must produce a caveat" $ not (null caveats)+        let numCexes = sum $ map (fromEnum . isCex) ret+        assertEqualM "should find counterexample" 1 numCexes+    , test "bytes-content-cex" $ do+        -- Should find bytes starting with 0xdead+        Just c <- solcRuntime "C" [i|+            contract C {+              function fun(bytes calldata data) public pure {+                if (data.length >= 2) {+                  assert(data[0] != 0xde || data[1] != 0xad);+                }+              }+            } |]+        let sig = Just $ Sig "fun(bytes)" [AbiBytesDynamicType]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        (_, caveats) <- mkCalldata sig []+        assertBoolM "bounded dyn arg must not produce a synthetic partial" $ not (any isPartial e)+        assertBoolM "bounded dyn arg must produce a caveat" $ not (null caveats)+        let numCexes = sum $ map (fromEnum . isCex) ret+        assertEqualM "should find counterexample" 1 numCexes+    , test "bytes-no-cex" $ do+        -- A trivially true assertion should have no counterexample (but still partial)+        Just c <- solcRuntime "C" [i|+            contract C {+              function fun(bytes calldata data) public pure {+                assert(data.length <= data.length);+              }+            } |]+        let sig = Just $ Sig "fun(bytes)" [AbiBytesDynamicType]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        (_, caveats) <- mkCalldata sig []+        assertBoolM "bounded dyn arg must not produce a synthetic partial" $ not (any isPartial e)+        assertBoolM "bounded dyn arg must produce a caveat" $ not (null caveats)+        let numCexes = sum $ map (fromEnum . isCex) ret+        assertEqualM "should have no counterexample" 0 numCexes+    , test "bytes-with-uint-arg" $ do+        -- bytes alongside a uint256 argument+        Just c <- solcRuntime "C" [i|+            contract C {+              function fun(uint256 x, bytes calldata data) public pure {+                if (data.length > 0 && x == 42) {+                  assert(false);+                }+              }+            } |]+        let sig = Just $ Sig "fun(uint256,bytes)" [AbiUIntType 256, AbiBytesDynamicType]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        (_, caveats) <- mkCalldata sig []+        assertBoolM "bounded dyn arg must not produce a synthetic partial" $ not (any isPartial e)+        assertBoolM "bounded dyn arg must produce a caveat" $ not (null caveats)+        let numCexes = sum $ map (fromEnum . isCex) ret+        assertEqualM "should find counterexample" 1 numCexes+    -- Tests that bugs beyond maxDynSize are reported via a caveat, not as false passes+    , test "bytes-beyond-bound-length-check" $ do+        -- Bug only triggers when bytes.length > 100; with default maxDynSize=64, should NOT find cex+        -- but must report a caveat (input domain was bounded)+        Just c <- solcRuntime "C" [i|+            contract C {+              function fun(bytes calldata data) public pure {+                if (data.length > 100) {+                  assert(false);+                }+              }+            } |]+        let sig = Just $ Sig "fun(bytes)" [AbiBytesDynamicType]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        (_, caveats) <- mkCalldata sig []+        assertBoolM "bounded dyn arg must not produce a synthetic partial" $ not (any isPartial e)+        assertBoolM "bounded dyn arg must produce a caveat" $ not (null caveats)+        let numCexes = sum $ map (fromEnum . isCex) ret+        assertEqualM "should not find cex (bug is beyond bound)" 0 numCexes+    , test "bytes-beyond-bound-content-check" $ do+        -- Bug triggers only when byte at index 80 has a specific value; beyond default maxDynSize=64+        Just c <- solcRuntime "C" [i|+            contract C {+              function fun(bytes calldata data) public pure {+                if (data.length > 80 && data[80] == 0xff) {+                  assert(false);+                }+              }+            } |]+        let sig = Just $ Sig "fun(bytes)" [AbiBytesDynamicType]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        (_, caveats) <- mkCalldata sig []+        assertBoolM "bounded dyn arg must not produce a synthetic partial" $ not (any isPartial e)+        assertBoolM "bounded dyn arg must produce a caveat" $ not (null caveats)+        let numCexes = sum $ map (fromEnum . isCex) ret+        assertEqualM "should not find cex (bug is beyond bound)" 0 numCexes+    , test "bytes-beyond-bound-sum-check" $ do+        -- Bug triggers when sum of first 128 bytes overflows; requires > 64 bytes+        Just c <- solcRuntime "C" [i|+            contract C {+              function fun(bytes calldata data) public pure {+                if (data.length >= 128) {+                  uint256 sum = 0;+                  for (uint i = 0; i < 128; i++) {+                    sum += uint8(data[i]);+                  }+                  assert(sum < 1000);+                }+              }+            } |]+        let sig = Just $ Sig "fun(bytes)" [AbiBytesDynamicType]+        (e, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        (_, caveats) <- mkCalldata sig []+        assertBoolM "bounded dyn arg must not produce a synthetic partial" $ not (any isPartial e)+        assertBoolM "bounded dyn arg must produce a caveat" $ not (null caveats)+        let numCexes = sum $ map (fromEnum . isCex) ret+        assertEqualM "should not find cex (bug requires 128 bytes, bound is 64)" 0 numCexes+    ]++  , testGroup "Precompiled contracts"+      [ testGroup "Example (reverse)"+          [ test "success" $+              assertEqualM "example contract reverses"+                (execute 0xdeadbeef "foobar" 6) (Just "raboof")+          , test "failure" $+              assertEqualM "example contract fails on length mismatch"+                (execute 0xdeadbeef "foobar" 5) Nothing+          ]++      , testGroup "ECRECOVER"+          [ test "success" $ do+              let+                r = hex "c84e55cee2032ea541a32bf6749e10c8b9344c92061724c4e751600f886f4732"+                s = hex "1542b6457e91098682138856165381453b3d0acae2470286fd8c8a09914b1b5d"+                v = hex "000000000000000000000000000000000000000000000000000000000000001c"+                h = hex "513954cf30af6638cb8f626bd3f8c39183c26784ce826084d9d267868a18fb31"+                a = hex "0000000000000000000000002d5e56d45c63150d937f2182538a0f18510cb11f"+              assertEqualM "successful recovery"+                (Just a)+                (execute 1 (h <> v <> r <> s) 32)+          , test "fail on made up values" $ do+              let+                r = hex "c84e55cee2032ea541a32bf6749e10c8b9344c92061724c4e751600f886f4731"+                s = hex "1542b6457e91098682138856165381453b3d0acae2470286fd8c8a09914b1b5d"+                v = hex "000000000000000000000000000000000000000000000000000000000000001c"+                h = hex "513954cf30af6638cb8f626bd3f8c39183c26784ce826084d9d267868a18fb31"+              assertEqualM "fail because bit flip"+                Nothing+                (execute 1 (h <> v <> r <> s) 32)+          ]+      ]+  , testGroup "Byte/word manipulations"+    [ testProperty "padLeft length" $ \n (Bytes bs) ->+        BS.length (padLeft n bs) == max n (BS.length bs)+    , testProperty "padLeft identity" $ \(Bytes bs) ->+        padLeft (BS.length bs) bs == bs+    , testProperty "padRight length" $ \n (Bytes bs) ->+        BS.length (padLeft n bs) == max n (BS.length bs)+    , testProperty "padRight identity" $ \(Bytes bs) ->+        padLeft (BS.length bs) bs == bs+    , testProperty "padLeft zeroing" $ \(NonNegative n) (Bytes bs) ->+        let x = BS.take n (padLeft (BS.length bs + n) bs)+            y = BS.replicate n 0+        in x == y+    ]++  , testGroup "Word/Addr encoding"+    [ testProperty "word256Bytes" $ \w ->+        word256Bytes w == slow_word256Bytes w+    , testProperty "word160Bytes" $ \a ->+        word160Bytes a == slow_word160Bytes a+    ]++  , testGroup "Unresolved link detection"+    [ test "holes detected" $ do+        let code' = "608060405234801561001057600080fd5b5060405161040f38038061040f83398181016040528101906100329190610172565b73__$f3cbc3eb14e5bd0705af404abcf6f741ec$__63ab5c1ffe826040518263ffffffff1660e01b81526004016100699190610217565b60206040518083038186803b15801561008157600080fd5b505af4158015610095573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100b99190610145565b50506103c2565b60006100d36100ce84610271565b61024c565b9050828152602081018484840111156100ef576100ee610362565b5b6100fa8482856102ca565b509392505050565b600081519050610111816103ab565b92915050565b600082601f83011261012c5761012b61035d565b5b815161013c8482602086016100c0565b91505092915050565b60006020828403121561015b5761015a61036c565b5b600061016984828501610102565b91505092915050565b6000602082840312156101885761018761036c565b5b600082015167ffffffffffffffff8111156101a6576101a5610367565b5b6101b284828501610117565b91505092915050565b60006101c6826102a2565b6101d081856102ad565b93506101e08185602086016102ca565b6101e981610371565b840191505092915050565b60006102016003836102ad565b915061020c82610382565b602082019050919050565b6000604082019050818103600083015261023181846101bb565b90508181036020830152610244816101f4565b905092915050565b6000610256610267565b905061026282826102fd565b919050565b6000604051905090565b600067ffffffffffffffff82111561028c5761028b61032e565b5b61029582610371565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b60008115159050919050565b60005b838110156102e85780820151818401526020810190506102cd565b838111156102f7576000848401525b50505050565b61030682610371565b810181811067ffffffffffffffff821117156103255761032461032e565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f6261720000000000000000000000000000000000000000000000000000000000600082015250565b6103b4816102be565b81146103bf57600080fd5b50565b603f806103d06000396000f3fe6080604052600080fdfea26469706673582212207d03b26e43dc3d116b0021ddc9817bde3762a3b14315351f11fc4be384fd14a664736f6c63430008060033"+        assertBoolM "linker hole not detected" (containsLinkerHole code'),+      test "no false positives" $ do+        let code' = "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806317bf8bac1461003b578063acffee6b1461005d575b600080fd5b610043610067565b604051808215151515815260200191505060405180910390f35b610065610073565b005b60008060015414905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8a8fd6d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156100da57600080fd5b505afa1580156100ee573d6000803e3d6000fd5b505050506040513d602081101561010457600080fd5b810190808051906020019092919050505060018190555056fea265627a7a723158205d775f914dcb471365a430b5f5b2cfe819e615cbbb5b2f1ccc7da1fd802e43c364736f6c634300050b0032"+        assertBoolM "false positive" (not . containsLinkerHole $ code')+    ]++  , testGroup "metadata stripper"+    [ test "it strips the metadata for solc => 0.6" $ do+        let code' = hexText "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806317bf8bac1461003b578063acffee6b1461005d575b600080fd5b610043610067565b604051808215151515815260200191505060405180910390f35b610065610073565b005b60008060015414905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8a8fd6d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156100da57600080fd5b505afa1580156100ee573d6000803e3d6000fd5b505050506040513d602081101561010457600080fd5b810190808051906020019092919050505060018190555056fea265627a7a723158205d775f914dcb471365a430b5f5b2cfe819e615cbbb5b2f1ccc7da1fd802e43c364736f6c634300050b0032"+            stripped = stripBytecodeMetadata code'+        assertEqualM "failed to strip metadata" (show (ByteStringS stripped)) "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806317bf8bac1461003b578063acffee6b1461005d575b600080fd5b610043610067565b604051808215151515815260200191505060405180910390f35b610065610073565b005b60008060015414905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8a8fd6d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156100da57600080fd5b505afa1580156100ee573d6000803e3d6000fd5b505050506040513d602081101561010457600080fd5b810190808051906020019092919050505060018190555056fe"+    ,+      testCase "it strips the metadata and constructor args" $ do+        let srccode =+              [i|+                contract A {+                  uint y;+                  constructor(uint x) public {+                    y = x;+                  }+                }+                |]++        Just initCode <- solidity "A" srccode+        assertEqual "constructor args screwed up metadata stripping" (stripBytecodeMetadata (initCode <> encodeAbiValue (AbiUInt 256 1))) (stripBytecodeMetadata initCode)+    ]++  , testGroup "RLP encodings"+    [ testProperty "rlp decode is a retraction (bytes)" $ \(Bytes bs) ->+      rlpdecode (rlpencode (BS bs)) == Just (BS bs)+    , testProperty "rlp encode is a partial inverse (bytes)" $ \(Bytes bs) ->+        case rlpdecode bs of+          Just r -> rlpencode r == bs+          Nothing -> True+    ,  testProperty "rlp decode is a retraction (RLP)" $ \(RLPData r) ->+       rlpdecode (rlpencode r) == Just r+    ]+  , testGroup "Symbolic-Constructor-Args"+    -- this produced some hard to debug failures. keeping it around since it seemed to exercise the contract creation code in interesting ways...+    [ test "multiple-symbolic-constructor-calls" $ do+        Just initCode <- solidity "C"+          [i|+            contract A {+                uint public x;+                constructor (uint z)  {}+            }++            contract B {+                constructor (uint i)  {}++            }++            contract C {+                constructor(uint u) {+                  new A(u);+                  new B(u);+                }+            }+          |]+        withSolvers Bitwuzla 1 Nothing defMemLimit $ \s -> do+          let calldata = (WriteWord (Lit 0x0) (Var "u") (ConcreteBuf ""), [])+          initVM <- liftIO $ stToIO $ abstractVM calldata initCode Nothing True+          let iterConf = IterConfig {maxIter=Nothing, askSmtIters=1, loopHeuristic=StackBased }+          paths <- interpret (Fetch.noRpcFetcher s) iterConf initVM runExpr noopPathHandler+          let exprSimp = map Expr.simplify paths+          assertBoolM "unexptected partial execution" (not $ any isPartial exprSimp)+    , test "mixed-concrete-symbolic-args" $ do+        Just c <- solcRuntime "C"+          [i|+            contract B {+                uint public x;+                uint public y;+                constructor (uint i, uint j)  {+                  x = i;+                  y = j;+                }++            }++            contract C {+                function foo(uint i) public {+                  B b = new B(10, i);+                  assert(b.x() == 10);+                  assert(b.y() == i);+                }+            }+          |]+        Right paths <- reachableUserAsserts c (Just $ Sig "foo(uint256)" [AbiUIntType 256])+        assertBoolM "unexptected partial execution" $ Prelude.not (any isPartial paths)+    , test "extcodesize-symbolic" $ do+        Just c <- solcRuntime "C"+          [i|+            contract C {+              function foo(address a, uint x) public {+               require(x > 10);+                uint size;+                assembly {+                  size := extcodesize(a)+                }+                assert(x >= 5);+              }+            }+          |]+        let sig = (Just $ Sig "foo(address,uint256)" [AbiAddressType, AbiUIntType 256])+        (e, res) <- withDefaultSolver $+          \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        liftIO $ printWarnings Nothing mempty e res "the contracts under test"+        assertEqualM "Must be QED" res []+    , test "extcodesize-symbolic2" $ do+        Just c <- solcRuntime "C"+          [i|+            contract C {+              function foo(address a, uint x) public {+                uint size;+                assembly {+                  size := extcodesize(a)+                }+                assert(size > 5);+              }+            }+          |]+        let sig = (Just $ Sig "foo(address,uint256)" [AbiAddressType, AbiUIntType 256])+        (e, res@[Cex _]) <- withDefaultSolver $+          \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        liftIO $ printWarnings Nothing mempty e res "the contracts under test"+    , test "jump-into-symbolic-region" $ do+        let+          -- our initCode just jumps directly to the end+          code = BS.pack . mapMaybe maybeLitByteSimp $ V.toList $ assemble+              [ OpPush (Lit 0x85)+              , OpJump+              , OpPush (Lit 1)+              , OpPush (Lit 1)+              , OpPush (Lit 1)+              , OpJumpdest+              ]+          -- we write a symbolic word to the middle, so the jump above should+          -- fail since the target is not in the concrete region+          initCode = (WriteWord (Lit 0x43) (Var "HI") (ConcreteBuf code), [])++          -- we pass in the above initCode buffer as calldata, and then copy+          -- it into memory before calling Create+          runtimecode = RuntimeCode (SymbolicRuntimeCode $ assemble+              [ OpPush (Lit 0x85)+              , OpPush (Lit 0x0)+              , OpPush (Lit 0x0)+              , OpCalldatacopy+              , OpPush (Lit 0x85)+              , OpPush (Lit 0x0)+              , OpPush (Lit 0x0)+              , OpCreate+              ])+        withDefaultSolver $ \s -> do+          vm <- liftIO $ stToIO $ loadSymVM runtimecode (Lit 0) initCode False+          let iterConf = IterConfig {maxIter=Nothing, askSmtIters=1, loopHeuristic=StackBased }+          paths <- interpret (Fetch.noRpcFetcher s) iterConf vm runExpr noopPathHandler+          let exprSimp = map Expr.simplify paths+          assertBoolM "expected partial execution" (any isPartial exprSimp)+    ]+  , testGroup "max-iterations"+    [ test "concrete-loops-reached" $ do+        Just c <- solcRuntime "C"+            [i|+            contract C {+              function fun() external payable returns (uint) {+                uint count = 0;+                for (uint i = 0; i < 5; i++) count++;+                return count;+              }+            }+            |]+        let sig = Just $ Sig "fun()" []+            opts = (defaultVeriOpts :: VeriOpts) { iterConf = defaultIterConf {maxIter = Just 3 }}+        (e, []) <- withDefaultSolver $+          \s -> checkAssert s defaultPanicCodes c sig [] opts+        assertBoolM "The expression is not partial" $ any isPartial e+    , test "concrete-loops-not-reached" $ do+        Just c <- solcRuntime "C"+            [i|+            contract C {+              function fun() external payable returns (uint) {+                uint count = 0;+                for (uint i = 0; i < 5; i++) count++;+                return count;+              }+            }+            |]++        let sig = Just $ Sig "fun()" []+            opts = (defaultVeriOpts :: VeriOpts) { iterConf = defaultIterConf {maxIter = Just 6 }}+        (e, []) <- withDefaultSolver $+          \s -> checkAssert s defaultPanicCodes c sig [] opts+        assertBoolM "The expression is partial" $ not $ any isPartial e+    , test "symbolic-loops-reached" $ do+        Just c <- solcRuntime "C"+            [i|+            contract C {+              function fun(uint j) external payable returns (uint) {+                uint count = 0;+                for (uint i = 0; i < j; i++) count++;+                return count;+              }+            }+            |]+        let veriOpts = (defaultVeriOpts :: VeriOpts) { iterConf = defaultIterConf { maxIter = Just 5 }}+        (e, []) <- withDefaultSolver $+          \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] veriOpts+        assertBoolM "The expression MUST be partial" $ any (Expr.containsNode isPartial) e+    , test "inconsistent-paths" $ do+        Just c <- solcRuntime "C"+            [i|+            contract C {+              function fun(uint j) external payable returns (uint) {+                require(j <= 3);+                uint count = 0;+                for (uint i = 0; i < j; i++) count++;+                return count;+              }+            }+            |]+        let sig = Just $ Sig "fun(uint256)" [AbiUIntType 256]+            -- we don't ask the solver about the loop condition until we're+            -- already in an inconsistent path (i == 5, j <= 3, i < j), so we+            -- will continue looping here until we hit max iterations+            opts = (defaultVeriOpts :: VeriOpts) { iterConf = defaultIterConf { maxIter = Just 10, askSmtIters = 5 }}+        (e, []) <- withDefaultSolver $+          \s -> checkAssert s defaultPanicCodes c sig [] opts+        assertBoolM "The expression MUST be partial" $ any (Expr.containsNode isPartial) e+    , test "mem-tuple" $ do+        Just c <- solcRuntime "C"+          [i|+            contract C {+              struct Pair {+                uint x;+                uint y;+              }+              function prove_tuple_pass(Pair memory p) public pure {+                uint256 f = p.x;+                uint256 g = p.y;+                unchecked {+                  p.x+=p.y;+                  assert(p.x == (f + g));+                }+              }+            }+          |]+        let opts = defaultVeriOpts+        let sig = Just $ Sig "prove_tuple_pass((uint256,uint256))" [AbiTupleType (V.fromList [AbiUIntType 256, AbiUIntType 256])]+        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] opts+        putStrLnM "Qed, memory tuple is good"+    , test "symbolic-loops-not-reached" $ do+        Just c <- solcRuntime "C"+            [i|+            contract C {+              function fun(uint j) external payable returns (uint) {+                require(j <= 3);+                uint count = 0;+                for (uint i = 0; i < j; i++) count++;+                return count;+              }+            }+            |]+        let sig = Just $ Sig "fun(uint256)" [AbiUIntType 256]+            -- askSmtIters is low enough here to avoid the inconsistent path+            -- conditions, so we never hit maxIters+            opts = (defaultVeriOpts :: VeriOpts) { iterConf = defaultIterConf {maxIter = Just 5, askSmtIters = 1 }}+        (e, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] opts+        assertBoolM "The expression MUST NOT be partial" $ not (any (Expr.containsNode isPartial) e)+    ]+  , testGroup "Symbolic Addresses"+    -- TODO ignore only because Martin has a fix for this-- it should not be using `verify`+    [ test "symbolic-address-create" $ do+        let src = [i|+                  contract A {+                    constructor() payable {}+                  }+                  contract C {+                    function fun(uint256 a) external{+                      require(address(this).balance > a);+                      new A{value:a}();+                    }+                  }+                  |]+        Just a <- solcRuntime "A" src+        Just c <- solcRuntime "C" src+        let sig = Sig "fun(uint256)" [AbiUIntType 256]+        paths <- withDefaultSolver $ \s -> exploreContract s c (Just sig) [] defaultVeriOpts Nothing+        let isSuc (Success {}) = True+            isSuc _ = False+        case filter isSuc paths of+          [Success _ _ _ store] -> do+            let ca = fromJust (Map.lookup (SymAddr "freshSymAddr1") store)+            let code = case ca.code of+                  RuntimeCode (ConcreteRuntimeCode c') -> c'+                  _ -> internalError "expected concrete code"+            assertEqualM "balance mismatch" (Var "arg1") (Expr.simplify ca.balance)+            assertEqualM "code mismatch" (stripBytecodeMetadata a) (stripBytecodeMetadata code)+            assertEqualM "nonce mismatch" (Just 1) ca.nonce+          _ -> assertBoolM "too many/too few success nodes!" False+    , test "symbolic-balance-call" $ do+        let src = [i|+                  contract A {+                    function f() public payable returns (uint) {+                      return msg.value;+                    }+                  }+                  contract C {+                    function fun(uint256 x) external {+                      require(address(this).balance > x);+                      A a = new A();+                      uint res = a.f{value:x}();+                      assert(res == x);+                    }+                  }+                  |]+        Just c <- solcRuntime "C" src+        res <- reachableUserAsserts c Nothing+        assertBoolM "unexpected cex" (isRight res)+    , test "deployed-contract-addresses-cannot-alias1" $ do+        Just c <- solcRuntime "C"+          [i|+            contract A {}+            contract C {+              function f() external {+                A a = new A();+                uint256 addr = uint256(uint160(address(a)));+                uint256 addr2 = uint256(uint160(address(this)));+                assert(addr != addr2);+              }+            }+          |]+        res <- reachableUserAsserts c Nothing+        assertBoolM "should not be able to alias" (isRight res)+    , test "deployed-contract-addresses-cannot-alias2" $ do+        Just c <- solcRuntime "C"+          [i|+            contract A {}+            contract C {+              function f() external {+                A a = new A();+                assert(address(a) != address(this));+              }+            }+          |]+        res <- reachableUserAsserts c Nothing+        assertBoolM "should not be able to alias" (isRight res)+    , test "addresses-in-args-can-alias-anything" $ do+        let addrs :: [Text]+            addrs = ["address(this)", "tx.origin", "block.coinbase", "msg.sender"]+            sig = Just $ Sig "f(address)" [AbiAddressType]+            checkVs vs = [i|+                           contract C {+                             function f(address a) external {+                               if (${vs} == a) assert(false);+                             }+                           }+                         |]++        [self, origin, coinbase, caller] <- forM addrs $ \addr -> do+          Just c <- solcRuntime "C" (checkVs addr)+          Left [cex] <- reachableUserAsserts c sig+          pure cex.addrs++        liftIO $ do+          let check as a = (Map.lookup (SymAddr "arg1") as) @?= (Map.lookup a as)+          check self (SymAddr "entrypoint")+          check origin (SymAddr "origin")+          check coinbase (SymAddr "coinbase")+          check caller (SymAddr "caller")+    , test "addresses-in-args-can-alias-themselves" $ do+        Just c <- solcRuntime "C"+          [i|+            contract C {+              function f(address a, address b) external {+                if (a == b) assert(false);+              }+            }+          |]+        let sig = Just $ Sig "f(address,address)" [AbiAddressType,AbiAddressType]+        Left [cex] <- reachableUserAsserts c sig+        let arg1 = fromJust $ Map.lookup (SymAddr "arg1") cex.addrs+            arg2 = fromJust $ Map.lookup (SymAddr "arg1") cex.addrs+        assertEqualM "should match" arg1 arg2+    -- TODO: fails due to missing aliasing rules+    , expectFail $ test "tx.origin cannot alias deployed contracts" $ do+        Just c <- solcRuntime "C"+          [i|+            contract A {}+            contract C {+              function f() external {+                address a = address(new A());+                if (tx.origin == a) assert(false);+              }+            }+          |]+        cexs <- reachableUserAsserts c Nothing+        assertBoolM "unexpected cex" (isRight cexs)+    , test "tx.origin can alias everything else" $ do+        let addrs = ["address(this)", "block.coinbase", "msg.sender", "arg"] :: [Text]+            sig = Just $ Sig "f(address)" [AbiAddressType]+            checkVs vs = [i|+                           contract C {+                             function f(address arg) external {+                               if (${vs} == tx.origin) assert(false);+                             }+                           }+                         |]++        [self, coinbase, caller, arg] <- forM addrs $ \addr -> do+          Just c <- solcRuntime "C" (checkVs addr)+          Left [cex] <- reachableUserAsserts c sig+          pure cex.addrs++        liftIO $ do+          let check as a = (Map.lookup (SymAddr "origin") as) @?= (Map.lookup a as)+          check self (SymAddr "entrypoint")+          check coinbase (SymAddr "coinbase")+          check caller (SymAddr "caller")+          check arg (SymAddr "arg1")+    , test "coinbase can alias anything" $ do+        let addrs = ["address(this)", "tx.origin", "msg.sender", "a", "arg"] :: [Text]+            sig = Just $ Sig "f(address)" [AbiAddressType]+            checkVs vs = [i|+                           contract A {}+                           contract C {+                             function f(address arg) external {+                               address a = address(new A());+                               if (${vs} == block.coinbase) assert(false);+                             }+                           }+                         |]++        [self, origin, caller, a, arg] <- forM addrs $ \addr -> do+          Just c <- solcRuntime "C" (checkVs addr)+          Left [cex] <- reachableUserAsserts c sig+          pure cex.addrs++        liftIO $ do+          let check as a' = (Map.lookup (SymAddr "coinbase") as) @?= (Map.lookup a' as)+          check self (SymAddr "entrypoint")+          check origin (SymAddr "origin")+          check caller (SymAddr "caller")+          check a (SymAddr "freshSymAddr1")+          check arg (SymAddr "arg1")+    , test "caller can alias anything" $ do+        let addrs = ["address(this)", "tx.origin", "block.coinbase", "a", "arg"] :: [Text]+            sig = Just $ Sig "f(address)" [AbiAddressType]+            checkVs vs = [i|+                           contract A {}+                           contract C {+                             function f(address arg) external {+                               address a = address(new A());+                               if (${vs} == msg.sender) assert(false);+                             }+                           }+                         |]++        [self, origin, coinbase, a, arg] <- forM addrs $ \addr -> do+          Just c <- solcRuntime "C" (checkVs addr)+          Left [cex] <- reachableUserAsserts c sig+          pure cex.addrs++        liftIO $ do+          let check as a' = (Map.lookup (SymAddr "caller") as) @?= (Map.lookup a' as)+          check self (SymAddr "entrypoint")+          check origin (SymAddr "origin")+          check coinbase (SymAddr "coinbase")+          check a (SymAddr "freshSymAddr1")+          check arg (SymAddr "arg1")+    , test "vm.load fails for a potentially aliased address" $ do+        Just c <- solcRuntime "C"+          [i|+            interface Vm {+              function load(address,bytes32) external returns (bytes32);+            }+            contract C {+              function f() external {+                Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);+                vm.load(msg.sender, 0x0);+              }+            }+          |]+        -- NOTE: we have a postcondition here, not just a regular verification+        (_, [Cex _]) <- withDefaultSolver $ \s ->+          verifyContract s c Nothing [] defaultVeriOpts Nothing (checkBadCheatCode "load(address,bytes32)")+        pure ()+    , test "vm.store fails for a potentially aliased address" $ do+        Just c <- solcRuntime "C"+          [i|+            interface Vm {+                function store(address,bytes32,bytes32) external;+            }+            contract C {+              function f() external {+                Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);+                vm.store(msg.sender, 0x0, 0x0);+              }+            }+          |]+        -- NOTE: we have a postcondition here, not just a regular verification+        (_, [Cex _]) <- withDefaultSolver $ \s ->+          verifyContract s c Nothing [] defaultVeriOpts Nothing (checkBadCheatCode "store(address,bytes32,bytes32)")+        pure ()+    -- TODO: make this work properly+    , test "transfering-eth-does-not-dealias" $ do+        Just c <- solcRuntime "C"+          [i|+            // we can't do calls to unknown code yet so we use selfdestruct+            contract Send {+              constructor(address payable dst) payable {+                selfdestruct(dst);+              }+            }+            contract C {+              function f() external {+                uint preSender = msg.sender.balance;+                uint preOrigin = tx.origin.balance;++                new Send{value:10}(payable(msg.sender));+                new Send{value:5}(payable(tx.origin));++                if (msg.sender == tx.origin) {+                  assert(preSender == preOrigin+                      && msg.sender.balance == preOrigin + 15+                      && tx.origin.balance == preSender + 15);+                } else {+                  assert(msg.sender.balance == preSender + 10+                      && tx.origin.balance == preOrigin + 5);+                }+              }+            }+          |]+        Right e <- reachableUserAsserts c Nothing+        -- TODO: this should work one day+        assertBoolM "should be partial" (any isPartial e)+    , test "symbolic-addresses-cannot-be-zero-or-precompiles" $ do+        let addrs = [T.pack . show . Addr $ a | a <- [0x0..0x09]]+            mkC a = fromJust <$> solcRuntime "A"+              [i|+                contract A {+                  function f() external {+                    assert(msg.sender != address(${a}));+                  }+                }+              |]+        codes <- mapM mkC addrs+        results <- mapM (flip reachableUserAsserts (Just (Sig "f()" []))) codes+        let ok = and $ fmap (isRight) results+        assertBoolM "unexpected cex" ok+    , test "addresses-in-context-are-symbolic" $ do+        Just a <- solcRuntime "A"+          [i|+            contract A {+              function f() external {+                assert(msg.sender != address(0x10));+              }+            }+          |]+        Just b <- solcRuntime "B"+          [i|+            contract B {+              function f() external {+                assert(block.coinbase != address(0x11));+              }+            }+          |]+        Just c <- solcRuntime "C"+          [i|+            contract C {+              function f() external {+                assert(tx.origin != address(0x12));+              }+            }+          |]+        Just d <- solcRuntime "D"+          [i|+            contract D {+              function f() external {+                assert(address(this) != address(0x13));+              }+            }+          |]+        [acex,bcex,ccex,dcex] <- forM [a,b,c,d] $ \con -> do+          Left [cex] <- reachableUserAsserts con Nothing+          assertEqualM "wrong number of addresses" 1 (length (Map.keys cex.addrs))+          pure cex++        -- Lowest allowed address is 0x10 due to reserved addresses up to 0x9+        assertEqualM "wrong model for a" (Addr 0x10) (fromJust $ Map.lookup (SymAddr "caller") acex.addrs)+        assertEqualM "wrong model for b" (Addr 0x11) (fromJust $ Map.lookup (SymAddr "coinbase") bcex.addrs)+        assertEqualM "wrong model for c" (Addr 0x12) (fromJust $ Map.lookup (SymAddr "origin") ccex.addrs)+        assertEqualM "wrong model for d" (Addr 0x13) (fromJust $ Map.lookup (SymAddr "entrypoint") dcex.addrs)+    ]+  , testGroup "Symbolic execution"+      [+     test "require-test" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(int256 a) external pure {+              require(a <= 0);+              assert (a <= 0);+              }+             }+            |]+        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256)" [AbiIntType 256])) [] defaultVeriOpts+        putStrLnM "Require works as expected"+     , test "symbolic-block-number" $ do+       Just c <- solcRuntime "C" [i|+           interface Vm {+               function roll(uint) external;+           }+           contract C {+             function myfun(uint x, uint y) public {+               Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);+               vm.roll(x);+               assert(block.number == y);+             }+           } |]+       (e, [Cex _]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+       assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial e)+     , test "symbolic-to-concrete-multi" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            interface Vm {+              function deal(address,uint256) external;+            }+            contract MyContract {+              function fun(uint160 a) external {+                Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);+                uint160 c = 10 + (a % 2);+                address b = address(c);+                vm.deal(b, 10);+              }+             }+            |]+        let sig = Just (Sig "fun(uint160)" [AbiUIntType 160])+        (e, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertBoolM "The expression is not partial" $ Prelude.not (any isPartial e)+     ,+     -- here test+     test "ITE-with-bitwise-AND" $ do+       Just c <- solcRuntime "C"+         [i|+         contract C {+           function f(uint256 x) public pure {+             require(x > 0);+             uint256 a = (x & 8);+             bool w;+             // assembly is needed here, because solidity doesn't allow uint->bool conversion+             assembly {+                 w:=a+             }+             if (!w) assert(false); //we should get a CEX: when x has a 0 at bit 3+           }+         }+         |]+       -- should find a counterexample+       (_, [Cex _]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+       putStrLnM "expected counterexample found"+     ,+     test "ITE-with-bitwise-OR" $ do+       Just c <- solcRuntime "C"+         [i|+         contract C {+           function f(uint256 x) public pure {+             uint256 a = (x | 8);+             bool w;+             // assembly is needed here, because solidity doesn't allow uint->bool conversion+             assembly {+                 w:=a+             }+             assert(w); // due to bitwise OR with positive value, this must always be true+           }+         }+         |]+       (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+       putStrLnM "this should always be true, due to bitwise OR with positive value"+     ,+     test "abstract-returndata-size" $ do+       Just c <- solcRuntime "C"+         [i|+         contract C {+           function f(uint256 x) public pure {+             assembly {+                 return(0, x)+             }+           }+         }+         |]+       paths <- withDefaultSolver $ \s -> getExpr s c (Just (Sig "f(uint256)" [])) [] defaultVeriOpts+       assertBoolM "The expression is partial" $ Prelude.not (any isPartial paths)+    ,+    -- CopySlice check+    -- uses identity precompiled contract (0x4) to copy memory+    -- checks 9af114613075a2cd350633940475f8b6699064de (readByte + CopySlice had src/dest mixed up)+    -- without 9af114613 it dies with: `Exception: UnexpectedSymbolicArg 296 "MSTORE index"`+    --       TODO: check  9e734b9da90e3e0765128b1f20ce1371f3a66085 (bufLength + copySlice was off by 1)+    test "copyslice-check" $ do+      Just c <- solcRuntime "C"+        [i|+        contract C {+          function checkval(uint8 a) public {+            bytes memory data = new bytes(5);+            for(uint i = 0; i < 5; i++) data[i] = bytes1(a);+            bytes memory ret = new bytes(data.length);+            assembly {+                let len := mload(data)+                if iszero(call(0xff, 0x04, 0, add(data, 0x20), len, add(ret,0x20), len)) {+                    invalid()+                }+            }+            for(uint i = 0; i < 5; i++) assert(ret[i] == data[i]);+          }+        }+        |]+      let sig = Just (Sig "checkval(uint8)" [AbiUIntType 8])+      (res, []) <- withDefaultSolver $ \s ->+        checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      putStrLnM $ "successfully explored " <> show (length res) <> " paths"+    , test "staticcall-check-orig" $ do+      Just c <- solcRuntime "C"+        [i|+        contract Target {+            function add(uint256 x, uint256 y) external pure returns (uint256) {+              unchecked {+                return x + y;+              }+            }+        }++        contract C {+            function checkval(uint256 x, uint256 y) public {+                Target t = new Target();+                address realAddr = address(t);+                bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);+                (bool success, bytes memory returnData) = realAddr.staticcall(data);+                assert(success);++                uint result = abi.decode(returnData, (uint256));+                uint expected;+                unchecked {+                  expected = x + y;+                }+                assert(result == expected);+            }+        }+        |]+      let sig = Just (Sig "checkval(uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])+      (res, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+      let numCexes = sum $ map (fromEnum . isCex) ret+      let numErrs = sum $ map (fromEnum . isError) ret+      assertEqualM "number of counterexamples" 0 numCexes+      assertEqualM "number of errors" 0 numErrs+    , test "staticcall-check-orig2" $ do+      Just c <- solcRuntime "C"+        [i|+        contract Target {+            function add(uint256 x, uint256 y) external pure returns (uint256) {+              assert(1 == 0);+            }+        }+        contract C {+            function checkval(uint256 x, uint256 y) public {+                Target t = new Target();+                address realAddr = address(t);+                bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);+                (bool success, bytes memory returnData) = realAddr.staticcall(data);+                assert(success);+            }+        }+        |]+      let sig = Just (Sig "checkval(uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])+      (res, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+      assertBoolM "The expression is NOT partial" $ Prelude.not (any isPartial res)+      let numCexes = sum $ map (fromEnum . isCex) ret+      let numErrs = sum $ map (fromEnum . isError) ret+      assertEqualM "number of counterexamples" 1 numCexes+      assertEqualM "number of errors" 0 numErrs+    , test "copyslice-symbolic-ok" $ do+      Just c <- solcRuntime "C"+        [i|+         contract Target {+           function get(address addr) external view returns (uint256) {+               return 55;+           }+         }+         contract C {+           function retFor(address addr) public returns (uint256) {+               Target mm = new Target();+               uint256 ret = mm.get(addr);+               assert(ret == 4);+               return ret;+           }+         }+        |]+      let sig2 = Just (Sig "retFor(address)" [AbiAddressType])+      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig2 [] defaultVeriOpts+      putStrLnM $ "successfully explored: " <> show (length paths) <> " paths"+      assertBoolM "The expression is NOT error" $ not $ any isError ret+      assertBoolM "The expression is NOT partial" $ not (any isPartial paths)+    , test "no-overapprox-when-present" $ do+      Just c <- solcRuntime "C" [i|+        contract ERC20 {+          function f() public {+          }+        }++        contract C {+          address token;++          function no_overapp() public {+            token = address(new ERC20());+            token.delegatecall(abi.encodeWithSignature("f()"));+          }+        } |]+      let sig2 = Just (Sig "no_overapp()" [])+      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig2 [] defaultVeriOpts+      -- putStrLnM $ "paths: " <> show paths+      putStrLnM $ "successfully explored: " <> show (length paths) <> " paths"+      assertBoolM "The expression is NOT error" $ not $ any isError ret+      assertBoolM "The expression is NOT partial" $ not (any isPartial paths)+      let numCexes = sum $ map (fromEnum . isCex) ret+      assertEqualM "number of counterexamples" 0 numCexes+    -- NOTE: below used to be symbolic copyslice copy error before new copyslice+    --       simplifications in Expr.simplify+    , test "overapproximates-undeployed-contract-symbolic" $ do+      Just c <- solcRuntime "C"+        [i|+         contract Target {+           function get(address addr) external view returns (uint256) {+               return 55;+           }+         }+         contract C {+           Target mm;+           function retFor(address addr) public returns (uint256) {+               // NOTE: this is symbolic execution, and no setUp has been ran+               //       hence, this below calls unknown code! It's trying to load:+               //       (SLoad (Lit 0x0) (AbstractStore (SymAddr "entrypoint") Nothing))+               //       So it overapproximates.+               uint256 ret = mm.get(addr);+               assert(ret == 4);+               return ret;+           }+         }+        |]+      let sig2 = Just (Sig "retFor(address)" [AbiAddressType])+      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig2 [] defaultVeriOpts+      putStrLnM $ "successfully explored: " <> show (length paths) <> " paths"+      assertBoolM "The expression is NOT error" $ not $ any isError ret+      assertBoolM "The expression is NOT partial" $ not (any isPartial paths)+      let numCexes = sum $ map (fromEnum . isCex) ret+      -- There are 2 CEX-es+      -- This is because with one CEX, the return DATA+      -- is empty, and in the other, the return data is non-empty (but symbolic)+      assertEqualM "number of counterexamples" 2 numCexes+    , test "overapproximates-unknown-addr" $ do+      Just c <- solcRuntime "C"+        [i|+         contract Target {+           function get() external view returns (uint256) {+               return 55;+           }+         }+         contract C {+           Target mm;+           function retFor(address addr) public returns (uint256) {+               Target target = Target(addr);+               uint256 ret = target.get();+               assert(ret == 4);+               return ret;+           }+         }+        |]+      let sig2 = Just (Sig "retFor(address)" [AbiAddressType])+      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig2 [] defaultVeriOpts+      putStrLnM $ "successfully explored: " <> show (length paths) <> " paths"+      assertBoolM "The expression is NOT error" $ not $ any isError ret+      let numCexes = sum $ map (fromEnum . isCex) ret+      assertBoolM "The expression is NOT partial" $ not (any isPartial paths)+      -- There are 2 CEX-es+      -- This is because with one CEX, the return DATA+      -- is empty, and in the other, the return data is non-empty (but symbolic)+      assertEqualM "number of counterexamples" 2 numCexes+    , test "overapproximates-fixed-zero-addr" $ do+      Just c <- solcRuntime "C"+        [i|+         contract Target {+           function get() external view returns (uint256) {+               return 55;+           }+         }+         contract C {+           Target mm;+           function retFor() public returns (uint256) {+               Target target = Target(address(0));+               uint256 ret = target.get();+               assert(ret == 4);+               return ret;+           }+         }+        |]+      let sig2 = Just (Sig "retFor()" [])+      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig2 [] defaultVeriOpts+      putStrLnM $ "successfully explored: " <> show (length paths) <> " paths"+      assertBoolM "The expression is NOT error" $ not $ any isError ret+      let numCexes = sum $ map (fromEnum . isCex) ret+      assertBoolM "The expression is NOT partial" $ not (any isPartial paths)+      -- There are 2 CEX-es+      -- This is because with one CEX, the return DATA+      -- is empty, and in the other, the return data is non-empty (but symbolic)+      assertEqualM "number of counterexamples" 2 numCexes+    , test "overapproximates-fixed-wrong-addr" $ do+      Just c <- solcRuntime "C"+        [i|+         contract Target {+           function get() external view returns (uint256) {+               return 55;+           }+         }+         contract C {+           Target mm;+           function retFor() public returns (uint256) {+               Target target = Target(address(0xacab));+               uint256 ret = target.get();+               assert(ret == 4);+               return ret;+           }+         }+        |]+      let sig2 = Just (Sig "retFor()" [])+      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig2 [] defaultVeriOpts+      putStrLnM $ "successfully explored: " <> show (length paths) <> " paths"+      assertBoolM "The expression is NOT error" $ not $ any isError ret+      assertBoolM "The expression is NOT partial" $ not (any isPartial paths)+      let numCexes = sum $ map (fromEnum . isCex) ret+      -- There are 2 CEX-es+      -- This is because with one CEX, the return DATA+      -- is empty, and in the other, the return data is non-empty (but symbolic)+      assertEqualM "number of counterexamples" 2 numCexes+    , test "staticcall-no-overapprox-2" $ do+      Just c <- solcRuntime "C"+        [i|+        contract Target {+            function add(uint256 x, uint256 y) external pure returns (uint256) {+              unchecked {+                return x + y;+              }+            }+        }+        contract C {+            function checkval(uint256 x, uint256 y) public {+                Target t = new Target();+                address realAddr = address(t);+                bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);+                (bool success, bytes memory returnData) = realAddr.staticcall(data);+                assert(success);+                assert(returnData.length == 32);++                // Decode the return value+                uint256 result = abi.decode(returnData, (uint256));++                // Assert that the result is equal to x + y+                unchecked {+                  assert(result == x + y);+                }+            }+        }+        |]+      let sig = Just (Sig "checkval(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])+      (res, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+      assertBoolM "The expression is NOT partial" $ not (any isPartial res)+      assertBoolM "The expression is NOT unknown" $ not $ any isUnknown ret+      assertBoolM "The expression is NOT error" $ not $ any isError ret+      let numCexes = sum $ map (fromEnum . isCex) ret+      let numErrs = sum $ map (fromEnum . isError) ret+      assertEqualM "number of counterexamples" 0 numCexes+      assertEqualM "number of errors" 0 numErrs+    , test "staticcall-check-symbolic1" $ do+      Just c <- solcRuntime "C"+        [i|+        contract C {+            function checkval(address inputAddr, uint256 x, uint256 y) public {+                bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);+                (bool success, bytes memory returnData) = inputAddr.staticcall(data);+                assert(success);+            }+        }+        |]+      let sig = Just (Sig "checkval(address,uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])+      (res, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+      let numCexes = sum $ map (fromEnum . isCex) ret+      let numErrs = sum $ map (fromEnum . isError) ret+      -- There are 2 CEX-es, in contrast to the above (staticcall-check-orig2).+      -- This is because with one CEX, the return DATA+      -- is empty, and in the other, the return data is non-empty (but symbolic)+      assertEqualM "number of counterexamples" 2 numCexes+      assertEqualM "number of errors" 0 numErrs+    -- This checks that calling a symbolic address with staticcall will ALWAYS return 0/1+    -- which is the semantic of the EVM. We insert a  constraint over the return value+    -- even when overapproximation is used, as below.+    , test "staticcall-check-symbolic-yul" $ do+      Just c <- solcRuntime "C"+        [i|+        contract C {+            function checkval(address inputAddr, uint256 x, uint256 y) public {+            uint success;+            assembly {+              // Allocate memory for the call data+              let callData := mload(0x40)++              // Function signature for "add(uint256,uint256)" is "0x771602f7"+              mstore(callData, 0x771602f700000000000000000000000000000000000000000000000000000000)++              // Store the parameters x and y+              mstore(add(callData, 4), x)+              mstore(add(callData, 36), y)++              // Perform the static call+              success := staticcall(+                  gas(),          // Forward all available gas+                  inputAddr,      // Address to call+                  callData,       // Input data location+                  68,             // Input data size (4 bytes for function signature + 32 bytes each for x and y)+                  0,              // Output data location (0 means we don't care about the output)+                  0               // Output data size+              )+              }+              assert(success <= 1);+          }+        }+        |]+      let sig = Just (Sig "checkval(address,uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])+      (res, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+      let numCexes = sum $ map (fromEnum . isCex) ret+      let numErrs = sum $ map (fromEnum . isError) ret+      assertEqualM "number of counterexamples" 0 numCexes -- no counterexamples, because it is always  0/1+      assertEqualM "number of errors" 0 numErrs+    , test "staticcall-check-symbolic2" $ do+      Just c <- solcRuntime "C"+        [i|+        contract C {+            function checkval(address inputAddr, uint256 x, uint256 y) public {+                bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);+                (bool success, bytes memory returnData) = inputAddr.staticcall(data);+                assert(success);++                uint result = abi.decode(returnData, (uint256));+                uint expected;+                unchecked {+                  expected = x + y;+                }+                assert(result == expected);+            }+        }+        |]+      let sig = Just (Sig "checkval(address,uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])+      (res, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+      let numCexes = sum $ map (fromEnum . isCex) ret+      let numErrs = sum $ map (fromEnum . isError) ret+      assertEqualM "number of counterexamples" 2 numCexes+      assertEqualM "number of errors" 1 numErrs+    , testCase "call-symbolic-noreent" $ do+      let conf = testEnv.config {promiseNoReent = True}+      let myTestEnv :: Env = (testEnv :: Env) {config = conf :: Config}+      runEnv myTestEnv $ do+        Just c <- solcRuntime "C"+          [i|+          contract C {+              function checkval(address inputAddr, uint256 x, uint256 y) public {+                  bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);+                  (bool success, bytes memory returnData) = inputAddr.call(data);+                  assert(success);+              }+          }+          |]+        let sig = Just (Sig "checkval(address,uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])+        (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        let numCexes = sum $ map (fromEnum . isCex) ret+        let numErrs = sum $ map (fromEnum . isError) ret+        assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)+      -- There are 2 CEX-es+      -- This is because with one CEX, the return DATA+      -- is empty, and in the other, the return data is non-empty and success is false+        assertEqualM "number of errors" 0 numErrs+        assertEqualM "number of counterexamples" 2 numCexes+    , test "call-symbolic-reent" $ do+      Just c <- solcRuntime "C"+        [i|+        contract C {+            function checkval(address inputAddr, uint256 x, uint256 y) public {+                bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);+                (bool success, bytes memory returnData) = inputAddr.call(data);+                assert(success);+            }+        }+        |]+      let sig = Just (Sig "checkval(address,uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])+      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      assertBoolM "The expression MUST be partial due to CALL to unknown code and no promise" (any isPartial paths)+      let numCexes = sum $ map (fromEnum . isCex) ret+      let numErrs = sum $ map (fromEnum . isError) ret+      assertEqualM "number of errors" 0 numErrs+      assertEqualM "number of counterexamples" 0 numCexes+    , testCase "call-symbolic-noreent-maxbufsize16" $ do+      let conf = testEnv.config {promiseNoReent = True, maxBufSize = 4}+      let myTestEnv :: Env = (testEnv :: Env) {config = conf :: Config}+      runEnv myTestEnv $ do+        Just c <- solcRuntime "C"+          [i|+          contract C {+              function checkval(address inputAddr, uint256 x, uint256 y) public {+                  bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);+                  (bool success, bytes memory returnData) = inputAddr.call(data);+                  assert(returnData.length < 16);+              }+          }+          |]+        let sig = Just (Sig "checkval(address,uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])+        (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        let numCexes = sum $ map (fromEnum . isCex) ret+        let numErrs = sum $ map (fromEnum . isError) ret+        assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)+        assertEqualM "number of errors" 0 numErrs+        assertEqualM "number of counterexamples" 0 numCexes+    , testCase "call-symbolic-noreent-maxbufsize16-fail" $ do+      let conf = testEnv.config {promiseNoReent = True, maxBufSize = 20}+      let myTestEnv :: Env = (testEnv :: Env) {config = conf :: Config}+      runEnv myTestEnv $ do+        Just c <- solcRuntime "C"+          [i|+          contract C {+              function checkval(address inputAddr, uint256 x, uint256 y) public {+                  bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);+                  (bool success, bytes memory returnData) = inputAddr.call(data);+                  assert(returnData.length < 16);+              }+          }+          |]+        let sig = Just (Sig "checkval(address,uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])+        (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        let numCexes = sum $ map (fromEnum . isCex) ret+        let numErrs = sum $ map (fromEnum . isError) ret+        assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)+        assertEqualM "number of errors" 0 numErrs+        assertEqualM "number of counterexamples" 1 numCexes+    , test "call-balance-symb" $ do+      Just c <- solcRuntime "C"+        [i|+        contract C {+            function checkval(address inputAddr) public {+                uint256 balance = inputAddr.balance;+                assert(balance < 10);+            }+        }+        |]+      let sig = Just (Sig "checkval(address)" [AbiAddressType])+      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      let numCexes = sum $ map (fromEnum . isCex) ret+      let numErrs = sum $ map (fromEnum . isError) ret+      assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)+      assertEqualM "number of errors" 0 numErrs+      assertEqualM "number of counterexamples" 1 numCexes+    , test "call-balance-symb2" $ do+      Just c <- solcRuntime "C"+        [i|+        contract C {+            function checkval() public {+                uint256 balance = address(0xacab).balance;+                assert(balance < 10);+            }+        }+        |]+      let sig = Just (Sig "checkval()" [])+      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      let numCexes = sum $ map (fromEnum . isCex) ret+      let numErrs = sum $ map (fromEnum . isError) ret+      assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)+      assertEqualM "number of errors" 0 numErrs+      assertEqualM "number of counterexamples" 1 numCexes+    , test "call-balance-concrete-pass" $ do+      Just c <- solcRuntime "C"+        [i|+        interface Vm {+          function deal(address,uint256) external;+        }+        contract Target {+        }+        contract C {+            function checkval() public {+                Target t = new Target();+                Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);+                vm.deal(address(t), 5);+                uint256 balance = address(t).balance;+                assert(balance < 10);+            }+        }+        |]+      let sig = Just (Sig "checkval()" [])+      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      let numErrs = sum $ map (fromEnum . isError) ret+      assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)+      assertEqualM "number of errors" 0 numErrs+    , test "call-balance-concrete-fail" $ do+      Just c <- solcRuntime "C"+        [i|+        interface Vm {+          function deal(address,uint256) external;+        }+        contract Target {+        }+        contract C {+            function checkval() public {+                Target t = new Target();+                Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);+                vm.deal(address(t), 5);+                uint256 balance = address(t).balance;+                assert(balance < 5);+            }+        }+        |]+      let sig = Just (Sig "checkval()" [])+      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      let numErrs = sum $ map (fromEnum . isError) ret+      let numCexes = sum $ map (fromEnum . isCex) ret+      assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)+      assertEqualM "number of errors" 0 numErrs+      assertEqualM "number of counterexamples" 1 numCexes+    , test "call-extcodehash-symb1" $ do+      Just c <- solcRuntime "C"+        [i|+        contract C {+            function checkval(address inputAddr) public {+                bytes32 hash = inputAddr.codehash;+                assert(uint(hash) < 10);+            }+        }+        |]+      let sig = Just (Sig "checkval(address)" [AbiAddressType])+      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      let numCexes = sum $ map (fromEnum . isCex) ret+      let numErrs = sum $ map (fromEnum . isError) ret+      assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)+      assertEqualM "number of errors" 0 numErrs+      assertEqualM "number of counterexamples" 1 numCexes+    , test "call-extcodehash-symb2" $ do+      Just c <- solcRuntime "C"+        [i|+        contract C {+            function checkval() public {+                bytes32 hash = address(0xacab).codehash;+                assert(uint(hash) < 10);+            }+        }+        |]+      let sig = Just (Sig "checkval()" [])+      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      let numCexes = sum $ map (fromEnum . isCex) ret+      let numErrs = sum $ map (fromEnum . isError) ret+      assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)+      assertEqualM "number of errors" 0 numErrs+      assertEqualM "number of counterexamples" 1 numCexes+    , test "call-extcodehash-concrete-fail" $ do+      Just c <- solcRuntime "C"+        [i|+        contract Target {+        }+        contract C {+            function checkval() public {+                Target t = new Target();+                bytes32 hash = address(t).codehash;+                assert(uint(hash) == 8);+            }+        }+        |]+      let sig = Just (Sig "checkval()" [])+      (paths, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      let numErrs = sum $ map (fromEnum . isError) ret+      let numCexes = sum $ map (fromEnum . isCex) ret+      assertBoolM "The expression MUST NOT be partial" $ Prelude.not (any isPartial paths)+      assertEqualM "number of errors" 0 numErrs+      assertEqualM "number of counterexamples" 1 numCexes+    , test "jump-symbolic" $ do+      Just c <- solcRuntime "C"+        [i|+        // Target contract with a view function+        contract Target {+        }++        // Caller contract using staticcall+        contract C {+            function checkval(address inputAddr, uint256 x, uint256 y) public {+                Target t = new Target();+                address realAddr = address(t);++                bytes memory data = abi.encodeWithSignature("add(uint256,uint256)", x, y);+                (bool success, bytes memory returnData) = inputAddr.staticcall(data);+                assert(success == true);+            }+        }+        |]+      let sig = Just (Sig "checkval(address,uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256])+      (res, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+      let numCexes = sum $ map (fromEnum . isCex) ret+      let numErrs = sum $ map (fromEnum . isError) ret+      assertEqualM "number of counterexamples" numCexes 2+      assertEqualM "number of symbolic copy errors" numErrs 0+     ,+     test "opcode-mul-assoc" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(int256 a, int256 b, int256 c) external pure {+              int256 tmp1;+              int256 out1;+              int256 tmp2;+              int256 out2;+              assembly {+                tmp1 := mul(a, b)+                out1 := mul(tmp1,c)+                tmp2 := mul(b, c)+                out2 := mul(a, tmp2)+              }+              assert (out1 == out2);+              }+             }+            |]+        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256,int256)" [AbiIntType 256, AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts+        putStrLnM "MUL is associative"+     ,+     -- TODO look at tests here for SAR: https://github.com/dapphub/dapptools/blob/01ef8ea418c3fe49089a44d56013d8fcc34a1ec2/src/dapp-tests/pass/constantinople.sol#L250+     test "opcode-sar-neg" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(int256 shift_by, int256 val) external pure returns (int256 out) {+              require(shift_by >= 0);+              require(val <= 0);+              assembly {+                out := sar(shift_by,val)+              }+              assert (out <= 0);+              }+             }+            |]+        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256)" [AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts+        putStrLnM "SAR works as expected"+     ,+     test "opcode-sar-pos" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(int256 shift_by, int256 val) external pure returns (int256 out) {+              require(shift_by >= 0);+              require(val >= 0);+              assembly {+                out := sar(shift_by,val)+              }+              assert (out >= 0);+              }+             }+            |]+        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256)" [AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts+        putStrLnM "SAR works as expected"+     ,+     test "opcode-sar-fixedval-pos" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(int256 shift_by, int256 val) external pure returns (int256 out) {+              require(shift_by == 1);+              require(val == 64);+              assembly {+                out := sar(shift_by,val)+              }+              assert (out == 32);+              }+             }+            |]+        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256)" [AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts+        putStrLnM "SAR works as expected"+     ,+     test "opcode-sar-fixedval-neg" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(int256 shift_by, int256 val) external pure returns (int256 out) {+                require(shift_by == 1);+                require(val == -64);+                assembly {+                  out := sar(shift_by,val)+                }+                assert (out == -32);+              }+             }+            |]+        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256)" [AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts+        putStrLnM "SAR works as expected"+     ,+     test "opcode-div-zero-1" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(uint256 val) external pure {+                uint out;+                assembly {+                  out := div(val, 0)+                }+                assert(out == 0);++              }+            }+            |]+        (_, [])  <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+        putStrLnM "sdiv works as expected"+      ,+     test "opcode-sdiv-zero-1" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(uint256 val) external pure {+                uint out;+                assembly {+                  out := sdiv(val, 0)+                }+                assert(out == 0);++              }+            }+            |]+        (_, [])  <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+        putStrLnM "sdiv works as expected"+      ,+     test "opcode-sdiv-zero-2" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(uint256 val) external pure {+                uint out;+                assembly {+                  out := sdiv(0, val)+                }+                assert(out == 0);++              }+            }+            |]+        (_, [])  <- withCVC5Solver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+        putStrLnM "sdiv works as expected"+      ,+     test "signed-overflow-checks" $ do+        Just c <- solcRuntime "C"+            [i|+            contract C {+              function fun(int256 a) external returns (int256) {+                  return a + a;+              }+            }+            |]+        (_, [Cex (_, _)]) <- withDefaultSolver $ \s -> checkAssert s [0x11] c (Just (Sig "fun(int256)" [AbiIntType 256])) [] defaultVeriOpts+        putStrLnM "expected cex discovered"+      ,+     test "opcode-signextend-neg" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(uint256 val, uint8 b) external pure {+                require(b <= 31);+                require(b >= 0);+                require(val < (1 <<(b*8)));+                require(val & (1 <<(b*8-1)) != 0); // MSbit set, i.e. negative+                uint256 out;+                assembly {+                  out := signextend(b, val)+                }+                if (b == 31) assert(out == val);+                else assert(out > val);+                assert(out & (1<<254) != 0); // MSbit set, i.e. negative+              }+            }+            |]+        (_, [])  <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+        putStrLnM "signextend works as expected"+      ,+     test "opcode-signextend-pos-nochop" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(uint256 val, uint8 b) external pure {+                require(val < (1 <<(b*8)));+                require(val & (1 <<(b*8-1)) == 0); // MSbit not set, i.e. positive+                uint256 out;+                assembly {+                  out := signextend(b, val)+                }+                assert (out == val);+              }+            }+            |]+        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint8)" [AbiUIntType 256, AbiUIntType 8])) [] defaultVeriOpts+        putStrLnM "signextend works as expected"+      ,+      test "opcode-signextend-pos-chopped" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(uint256 val, uint8 b) external pure {+                require(b == 0); // 1-byte+                require(val == 514); // but we set higher bits+                uint256 out;+                assembly {+                  out := signextend(b, val)+                }+                assert (out == 2); // chopped+              }+            }+            |]+        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint8)" [AbiUIntType 256, AbiUIntType 8])) [] defaultVeriOpts+        putStrLnM "signextend works as expected"+      ,+      -- when b is too large, value is unchanged+      test "opcode-signextend-pos-b-toolarge" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(uint256 val, uint8 b) external pure {+                require(b >= 31);+                uint256 out;+                assembly {+                  out := signextend(b, val)+                }+                assert (out == val);+              }+            }+            |]+        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint8)" [AbiUIntType 256, AbiUIntType 8])) [] defaultVeriOpts+        putStrLnM "signextend works as expected"+     ,+     test "opcode-clz" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function clz_test(uint256 x) internal pure returns (uint256 result) {+                  assembly {+                      result := clz(x)+                  }+              }++              function fun() external pure {+                assert(clz_test(0) == 256);+                assert(clz_test(1) == 255);+                assert(clz_test(2) == 254);+                assert(clz_test(2) == 254);+                // 61853446846231190821175268292818646713405929256851257084424727564423478318049+                // is larger than 2**255, so CLZ should be 0+                assert(clz_test(61853446846231190821175268292818646713405929256851257084424727564423478318049) == 0);+                // 1402344919110095602128912437416037586276662158775737295210557361797392888602+                // is larger than 2**249 but smaller than 2**250, so CLZ should be 6+                assert(clz_test(1402344919110095602128912437416037586276662158775737295210557361797392888602) == 6);+              }+             }+            |]+        (_, r) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun()" [])) [] defaultVeriOpts+        assertEqualM "CLZ. expected QED"  [] r+     ,+     test "opcode-clz-negative-test" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function clz_test(uint256 x) internal pure returns (uint256 result) {+                  assembly {+                      result := clz(x)+                  }+              }++              function fun() external pure {+                assert(clz_test(0) == 255); // wrong, should be 256+              }+             }+            |]+        (_, r) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun()" [])) [] defaultVeriOpts+        case r of+          [Cex _] -> assertEqualM "CLZ. expected CEX" () ()+          _ -> liftIO $ assertFailure "CLZ. Expected exactly one Cex"+     ,+     test "opcode-clz-negative-test2" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function clz_test(uint256 x) internal pure returns (uint256 result) {+                  assembly {+                      result := clz(x)+                  }+              }++              function fun() external pure {+                // 22 = 10110 -- i.e. 5 bits+                assert(clz_test(22) == 250); // should be 251: 256 - 5 = 251+              }+             }+            |]+        (_, r) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun()" [])) [] defaultVeriOpts+        case r of+          [Cex _] -> assertEqualM "CLZ. expected CEX" () ()+          _ -> liftIO $ assertFailure "CLZ. Expected exactly one Cex"+     ,+     test "opcode-shl" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(uint256 shift_by, uint256 val) external pure {+              require(val < (1<<16));+              require(shift_by < 16);+              uint256 out;+              assembly {+                out := shl(shift_by,val)+              }+              assert (out >= val);+              }+             }+            |]+        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+        putStrLnM "SHL works as expected"+     ,+     test "opcode-xor-cancel" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(uint256 a, uint256 b) external pure {+              require(a == b);+              uint256 c;+              assembly {+                c := xor(a,b)+              }+              assert (c == 0);+              }+             }+            |]+        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+        putStrLnM "XOR works as expected"+      ,+      test "opcode-xor-reimplement" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(uint256 a, uint256 b) external pure {+              uint256 c;+              assembly {+                c := xor(a,b)+              }+              assert (c == (~(a & b)) & (a | b));+              }+             }+            |]+        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+        putStrLnM "XOR works as expected"+      ,+      test "opcode-add-commutative" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(uint256 a, uint256 b) external pure {+                uint256 res1;+                uint256 res2;+                assembly {+                  res1 := add(a,b)+                  res2 := add(b,a)+                }+                assert (res1 == res2);+              }+            }+            |]+        a <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+        case a of+          (_, [Cex (_, ctr)]) -> do+            let x = getVar ctr "arg1"+            let y = getVar ctr "arg2"+            putStrLnM $ "y:" <> show y+            putStrLnM $ "x:" <> show x+            assertEqualM "Addition is not commutative... that's wrong" False True+          (_, []) -> do+            putStrLnM "adding is commutative"+          _ -> internalError "Unexpected"+      ,+      test "opcode-div-res-zero-on-div-by-zero" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(uint16 a) external pure {+                uint16 b = 0;+                uint16 res;+                assembly {+                  res := div(a,b)+                }+                assert (res == 0);+              }+            }+            |]+        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint16)" [AbiUIntType 16])) [] defaultVeriOpts+        putStrLnM "DIV by zero is zero"+      ,+      -- Somewhat tautological since we are asserting the precondition+      -- on the same form as the actual "requires" clause.+      test "SafeAdd success case" $ do+        Just safeAdd <- solcRuntime "SafeAdd"+          [i|+          contract SafeAdd {+            function add(uint x, uint y) public pure returns (uint z) {+                 require((z = x + y) >= x);+            }+          }+          |]+        let pre preVM = let (x, y) = case getStaticAbiArgs 2 preVM of+                                       [x', y'] -> (x', y')+                                       _ -> internalError "expected 2 args"+                        in (x .<= Expr.add x y)+                        -- TODO check if it's needed+                           .&& preVM.state.callvalue .== Lit 0+            post prestate leaf =+              let (x, y) = case getStaticAbiArgs 2 prestate of+                             [x', y'] -> (x', y')+                             _ -> internalError "expected 2 args"+              in case leaf of+                   Success _ _ b _ -> (ReadWord (Lit 0) b) .== (Add x y)+                   _ -> PBool True+            sig = Just (Sig "add(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])+        (res, []) <- withDefaultSolver $ \s ->+          verifyContract s safeAdd sig [] defaultVeriOpts (Just pre) post+        putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+     ,++      test "x == y => x + y == 2 * y" $ do+        Just safeAdd <- solcRuntime "SafeAdd"+          [i|+          contract SafeAdd {+            function add(uint x, uint y) public pure returns (uint z) {+                 require((z = x + y) >= x);+            }+          }+          |]+        let pre preVM = let (x, y) = case getStaticAbiArgs 2 preVM of+                                       [x', y'] -> (x', y')+                                       _ -> internalError "expected 2 args"+                        in (x .<= Expr.add x y)+                           .&& (x .== y)+                           .&& preVM.state.callvalue .== Lit 0+            post prestate leaf =+              let (_, y) = case getStaticAbiArgs 2 prestate of+                             [x', y'] -> (x', y')+                             _ -> internalError "expected 2 args"+              in case leaf of+                   Success _ _ b _ -> (ReadWord (Lit 0) b) .== (Mul (Lit 2) y)+                   _ -> PBool True+        (res, []) <- withDefaultSolver $ \s ->+          verifyContract s safeAdd (Just (Sig "add(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts (Just pre) post+        putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+      ,+      test "summary storage writes" $ do+        Just c <- solcRuntime "A"+          [i|+          contract A {+            uint x;+            function f(uint256 y) public {+               unchecked {+                 x += y;+                 x += y;+               }+            }+          }+          |]+        let pre vm = Lit 0 .== vm.state.callvalue+            post prestate leaf =+              let y = case getStaticAbiArgs 1 prestate of+                        [y'] -> y'+                        _ -> internalError "expected 1 arg"+                  this = prestate.state.codeContract+                  prestore = (fromJust (Map.lookup this prestate.env.contracts)).storage+                  prex = Expr.readStorage' (Lit 0) prestore+              in case leaf of+                Success _ _ _ postState -> let+                    poststore = (fromJust (Map.lookup this postState)).storage+                  in Expr.add prex (Expr.mul (Lit 2) y) .== (Expr.readStorage' (Lit 0) poststore)+                _ -> PBool True+            sig = Just (Sig "f(uint256)" [AbiUIntType 256])+        (res, []) <- withDefaultSolver $ \s ->+          verifyContract s c sig [] defaultVeriOpts (Just pre) post+        putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+        ,+        -- tests how whiffValue handles Neg via application of the triple IsZero simplification rule+        -- regression test for: https://github.com/dapphub/dapptools/pull/698+        test "Neg" $ do+            let src =+                  [i|+                    object "Neg" {+                      code {+                        // Deploy the contract+                        datacopy(0, dataoffset("runtime"), datasize("runtime"))+                        return(0, datasize("runtime"))+                      }+                      object "runtime" {+                        code {+                          let v := calldataload(4)+                          if iszero(iszero(and(v, not(0xffffffffffffffffffffffffffffffffffffffff)))) {+                            invalid()+                          }+                        }+                      }+                    }+                    |]+            Right c <- liftIO $ yulRuntime "Neg" src+            (res, []) <- withSolvers Z3 4 Nothing defMemLimit $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "hello(address)" [AbiAddressType])) [] defaultVeriOpts+            putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+        ,+        test "catch-storage-collisions-noproblem" $ do+          Just c <- solcRuntime "A"+            [i|+            contract A {+              function f(uint x, uint y) public {+                 if (x != y) {+                   assembly {+                     let newx := sub(sload(x), 1)+                     let newy := add(sload(y), 1)+                     sstore(x,newx)+                     sstore(y,newy)+                   }+                 }+              }+            }+            |]+          let pre vm = (Lit 0) .== vm.state.callvalue+              post prestate poststate =+                let (x,y) = case getStaticAbiArgs 2 prestate of+                        [x',y'] -> (x',y')+                        _ -> error "expected 2 args"+                    this = prestate.state.codeContract+                    prestore = (fromJust (Map.lookup this prestate.env.contracts)).storage+                    prex = Expr.readStorage' x prestore+                    prey = Expr.readStorage' y prestore+                in case poststate of+                     Success _ _ _ postcs -> let+                           poststore = (fromJust (Map.lookup this postcs)).storage+                           postx = Expr.readStorage' x poststore+                           posty = Expr.readStorage' y poststore+                       in Expr.add prex prey .== Expr.add postx posty+                     _ -> PBool True+              sig = Just (Sig "f(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])+          (_, []) <- withDefaultSolver $ \s ->+            verifyContract s c sig [] defaultVeriOpts (Just pre) post+          putStrLnM "Correct, this can never fail"+        ,+        -- Inspired by these `msg.sender == to` token bugs+        -- which break linearity of totalSupply.+        test "catch-storage-collisions-good" $ do+          Just c <- solcRuntime "A"+            [i|+            contract A {+              function f(uint x, uint y) public {+                 assembly {+                   let newx := sub(sload(x), 1)+                   let newy := add(sload(y), 1)+                   sstore(x,newx)+                   sstore(y,newy)+                 }+              }+            }+            |]+          let pre vm = (Lit 0) .== vm.state.callvalue+              post prestate leaf =+                let (x,y) = case getStaticAbiArgs 2 prestate of+                        [x',y'] -> (x',y')+                        _ -> error "expected 2 args"+                    this = prestate.state.codeContract+                    prestore = (fromJust (Map.lookup this prestate.env.contracts)).storage+                    prex = Expr.readStorage' x prestore+                    prey = Expr.readStorage' y prestore+                in case leaf of+                     Success _ _ _ poststate -> let+                           poststore = (fromJust (Map.lookup this poststate)).storage+                           postx = Expr.readStorage' x poststore+                           posty = Expr.readStorage' y poststore+                       in Expr.add prex prey .== Expr.add postx posty+                     _ -> PBool True+              sig = Just (Sig "f(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])+          (_, [Cex (_, ctr)]) <- withDefaultSolver $ \s ->+            verifyContract s c sig [] defaultVeriOpts (Just pre) post+          let x = getVar ctr "arg1"+          let y = getVar ctr "arg2"+          putStrLnM $ "y:" <> show y+          putStrLnM $ "x:" <> show x+          assertEqualM "Catch storage collisions" x y+          putStrLnM "expected counterexample found"+        ,+        test "simple-assert" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              function foo() external pure {+                assert(false);+              }+             }+            |]+          (_, [Cex (Failure _ _ (Revert msg), _)]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo()" [])) [] defaultVeriOpts+          assertEqualM "incorrect revert msg" msg (ConcreteBuf $ panicMsg 0x01)+        ,+        test "simple-assert-2" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              function foo(uint256 x) external pure {+                assert(x != 10);+              }+             }+            |]+          (_, [(Cex (_, ctr))]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          assertEqualM "Must be 10" 10 $ getVar ctr "arg1"+          putStrLnM "Got 10 Cex, as expected"+        ,+        test "assert-fail-equal" $ do+          Just c <- solcRuntime "AssertFailEqual"+            [i|+            contract AssertFailEqual {+              function fun(uint256 deposit_count) external pure {+                assert(deposit_count == 0);+                assert(deposit_count == 11);+              }+             }+            |]+          (_, [Cex (_, a), Cex (_, b)]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          let ints = map (flip getVar "arg1") [a,b]+          assertBoolM "0 must be one of the Cex-es" $ isJust $ List.elemIndex 0 ints+          putStrLnM "expected 2 counterexamples found, one Cex is the 0 value"+        ,+        test "assert-fail-notequal" $ do+          Just c <- solcRuntime "AssertFailNotEqual"+            [i|+            contract AssertFailNotEqual {+              function fun(uint256 deposit_count) external pure {+                assert(deposit_count != 0);+                assert(deposit_count != 11);+              }+             }+            |]+          (_, [Cex (_, a), Cex (_, b)]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          let x = getVar a "arg1"+          let y = getVar b "arg1"+          assertBoolM "At least one has to be 0, to go through the first assert" (x == 0 || y == 0)+          putStrLnM "expected 2 counterexamples found."+        ,+        test "assert-fail-twoargs" $ do+          Just c <- solcRuntime "AssertFailTwoParams"+            [i|+            contract AssertFailTwoParams {+              function fun(uint256 deposit_count1, uint256 deposit_count2) external pure {+                assert(deposit_count1 != 0);+                assert(deposit_count2 != 11);+              }+             }+            |]+          (_, [Cex _, Cex _]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+          putStrLnM "expected 2 counterexamples found"+        ,+        test "assert-2nd-arg" $ do+          Just c <- solcRuntime "AssertFailTwoParams"+            [i|+            contract AssertFailTwoParams {+              function fun(uint256 deposit_count1, uint256 deposit_count2) external pure {+                assert(deposit_count2 != 666);+              }+             }+            |]+          (_, [Cex (_, ctr)]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+          assertEqualM "Must be 666" 666 $ getVar ctr "arg2"+          putStrLnM "Found arg2 Ctx to be 666"+        ,+        -- LSB is zeroed out, byte(31,x) takes LSB, so y==0 always holds+        test "check-lsb-msb1" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              function foo(uint256 x) external pure {+                x &= 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00;+                uint8 y;+                assembly { y := byte(31,x) }+                assert(y == 0);+              }+            }+            |]+          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+        ,+        -- We zero out everything but the LSB byte. However, byte(31,x) takes the LSB byte+        -- so there is a counterexamle, where LSB of x is not zero+        test "check-lsb-msb2" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              function foo(uint256 x) external pure {+                x &= 0x00000000000000000000000000000000000000000000000000000000000000ff;+                uint8 y;+                assembly { y := byte(31,x) }+                assert(y == 0);+              }+            }+            |]+          (_, [Cex (_, ctr)]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          assertBoolM "last byte must be non-zero" $ ((Data.Bits..&.) (getVar ctr "arg1") 0xff) > 0+          putStrLnM "Expected counterexample found"+        ,+        -- We zero out everything but the 2nd LSB byte. However, byte(31,x) takes the 2nd LSB byte+        -- so there is a counterexamle, where 2nd LSB of x is not zero+        test "check-lsb-msb3 -- 2nd byte" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              function foo(uint256 x) external pure {+                x &= 0x000000000000000000000000000000000000000000000000000000000000ff00;+                uint8 y;+                assembly { y := byte(30,x) }+                assert(y == 0);+              }+            }+            |]+          (_, [Cex (_, ctr)]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          assertBoolM "second to last byte must be non-zero" $ ((Data.Bits..&.) (getVar ctr "arg1") 0xff00) > 0+          putStrLnM "Expected counterexample found"+        ,+        -- Reverse of test above+        test "check-lsb-msb4 2nd byte rev" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              function foo(uint256 x) external pure {+                x &= 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff;+                uint8 y;+                assembly {+                    y := byte(30,x)+                }+                assert(y == 0);+              }+            }+            |]+          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+        ,+        -- Bitwise OR operation test+        test "opcode-bitwise-or-full-1s" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              function foo(uint256 x) external pure {+                uint256 y;+                uint256 z = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;+                assembly { y := or(x, z) }+                assert(y == 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);+              }+            }+            |]+          (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLnM "When OR-ing with full 1's we should get back full 1's"+        ,+        -- Bitwise OR operation test+        test "opcode-bitwise-or-byte-of-1s" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              function foo(uint256 x) external pure {+                uint256 y;+                uint256 z = 0x000000000000000000000000000000000000000000000000000000000000ff00;+                assembly { y := or(x, z) }+                assert((y & 0x000000000000000000000000000000000000000000000000000000000000ff00) ==+                  0x000000000000000000000000000000000000000000000000000000000000ff00);+              }+            }+            |]+          (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLnM "When OR-ing with a byte of 1's, we should get 1's back there"+        ,+        test "Deposit contract loop (z3)" $ do+          Just c <- solcRuntime "Deposit"+            [i|+            contract Deposit {+              function deposit(uint256 deposit_count) external pure {+                require(deposit_count < 2**32 - 1);+                ++deposit_count;+                bool found = false;+                for (uint height = 0; height < 32; height++) {+                  if ((deposit_count & 1) == 1) {+                    found = true;+                    break;+                  }+                 deposit_count = deposit_count >> 1;+                 }+                assert(found);+              }+             }+            |]+          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "deposit(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+        ,+        test "Deposit-contract-loop-error-version" $ do+          Just c <- solcRuntime "Deposit"+            [i|+            contract Deposit {+              function deposit(uint8 deposit_count) external pure {+                require(deposit_count < 2**32 - 1);+                ++deposit_count;+                bool found = false;+                for (uint height = 0; height < 32; height++) {+                  if ((deposit_count & 1) == 1) {+                    found = true;+                    break;+                  }+                 deposit_count = deposit_count >> 1;+                 }+                assert(found);+              }+             }+            |]+          (_, [Cex (_, ctr)]) <- withDefaultSolver $ \s -> checkAssert s allPanicCodes c (Just (Sig "deposit(uint8)" [AbiUIntType 8])) [] defaultVeriOpts+          assertEqualM "Must be 255" 255 $ getVar ctr "arg1"+          putStrLnM  $ "expected counterexample found, and it's correct: " <> (show $ getVar ctr "arg1")+        ,+        test "explore function dispatch" $ do+          Just c <- solcRuntime "A"+            [i|+            contract A {+              function f(uint x) public pure returns (uint) {+                return x;+              }+            }+            |]+          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+        ,+        test "check-asm-byte-in-bounds" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              function foo(uint256 idx, uint256 val) external pure {+                uint256 actual;+                uint256 expected;+                require(idx < 32);+                assembly {+                  actual := byte(idx,val)+                  expected := shr(248, shl(mul(idx, 8), val))+                }+                assert(actual == expected);+              }+            }+            |]+          (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+          putStrLnM "in bounds byte reads return the expected value"+        ,+        test "check-div-mod-sdiv-smod-by-zero-constant-prop" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              function foo(uint256 e) external pure {+                uint x = 0;+                uint y = 55;+                uint z;+                assembly { z := div(y,x) }+                assert(z == 0);+                assembly { z := div(x,y) }+                assert(z == 0);+                assembly { z := sdiv(y,x) }+                assert(z == 0);+                assembly { z := sdiv(x,y) }+                assert(z == 0);+                assembly { z := mod(y,x) }+                assert(z == 0);+                assembly { z := mod(x,y) }+                assert(z == 0);+                assembly { z := smod(y,x) }+                assert(z == 0);+                assembly { z := smod(x,y) }+                assert(z == 0);+              }+            }+            |]+          (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLnM "div/mod/sdiv/smod by zero works as expected during constant propagation"+        ,+        test "check-asm-byte-oob" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              function foo(uint256 x, uint256 y) external pure {+                uint256 z;+                require(x >= 32);+                assembly { z := byte(x,y) }+                assert(z == 0);+              }+            }+            |]+          (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+          putStrLnM "oob byte reads always return 0"+        ,+        test "injectivity of keccak (diff sizes)" $ do+          Just c <- solcRuntime "A"+            [i|+            contract A {+              function f(uint128 x, uint256 y) external pure {+                assert(+                    keccak256(abi.encodePacked(x)) !=+                    keccak256(abi.encodePacked(y))+                );+              }+            }+            |]+          Right _ <- reachableUserAsserts c (Just $ Sig "f(uint128,uint256)" [AbiUIntType 128, AbiUIntType 256])+          pure ()+        ,+        test "injectivity of keccak (32 bytes)" $ do+          Just c <- solcRuntime "A"+            [i|+            contract A {+              function f(uint x, uint y) public pure {+                if (keccak256(abi.encodePacked(x)) == keccak256(abi.encodePacked(y))) assert(x == y);+              }+            }+            |]+          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+        ,+        test "injectivity of keccak contrapositive (32 bytes)" $ do+          Just c <- solcRuntime "A"+            [i|+            contract A {+              function f(uint x, uint y) public pure {+                require (x != y);+                assert (keccak256(abi.encodePacked(x)) != keccak256(abi.encodePacked(y)));+              }+            }+            |]+          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+        ,+        test "injectivity of keccak (64 bytes)" $ do+          Just c <- solcRuntime "A"+            [i|+            contract A {+              function f(uint x, uint y, uint w, uint z) public pure {+                assert (keccak256(abi.encodePacked(x,y)) != keccak256(abi.encodePacked(w,z)));+              }+            }+            |]+          (_, [Cex (_, ctr)]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256,uint256,uint256,uint256)" (replicate 4 (AbiUIntType 256)))) [] defaultVeriOpts+          let x = getVar ctr "arg1"+          let y = getVar ctr "arg2"+          let w = getVar ctr "arg3"+          let z = getVar ctr "arg4"+          assertEqualM "x==y for hash collision" x y+          assertEqualM "w==z for hash collision" w z+          putStrLnM "expected counterexample found"+        ,+        test "calldata beyond calldatasize is 0 (symbolic calldata)" $ do+          Just c <- solcRuntime "A"+            [i|+            contract A {+              function f() public pure {+                uint y;+                assembly {+                  let x := calldatasize()+                  y := calldataload(x)+                }+                assert(y == 0);+              }+            }+            |]+          (res, []) <- withBitwuzlaSolver $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+        ,+        test "calldata beyond calldatasize is 0 (concrete dalldata prefix)" $ do+          Just c <- solcRuntime "A"+            [i|+            contract A {+              function f(uint256 z) public pure {+                uint y;+                assembly {+                  let x := calldatasize()+                  y := calldataload(x)+                }+                assert(y == 0);+              }+            }+            |]+          (res, []) <- withBitwuzlaSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+        ,+        test "calldata symbolic access" $ do+          Just c <- solcRuntime "A"+            [i|+            contract A {+              function f(uint256 z) public pure {+                uint x; uint y;+                assembly {+                  y := calldatasize()+                }+                require(z >= y);+                require(z < 2**64); // Accesses to larger indices are not supported+                assembly {+                  x := calldataload(z)+                }+                assert(x == 0);+              }+            }+            |]+          (res, []) <- withBitwuzlaSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+        ,+        test "multiple-contracts" $ do+          let code =+                [i|+                  contract C {+                    uint x;+                    A constant a = A(0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B);++                    function call_A() public view {+                      // should fail since x can be anything+                      assert(a.x() == x);+                    }+                  }+                  contract A {+                    uint public x;+                  }+                |]+              aAddr = LitAddr (Addr 0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B)+              cAddr = SymAddr "entrypoint"+          Just c <- solcRuntime "C" code+          Just a <- solcRuntime "A" code+          (_, [Cex (_, cex)]) <- withDefaultSolver $ \s -> do+            (calldata, _) <- mkCalldata (Just (Sig "call_A()" [])) []+            vm <- liftIO $ stToIO $ abstractVM calldata c Nothing False+                    <&> set (#state % #callvalue) (Lit 0)+                    <&> over (#env % #contracts)+                       (Map.insert aAddr (initialContract (RuntimeCode (ConcreteRuntimeCode a))))+            verify s (Fetch.noRpcFetcher s) defaultVeriOpts vm (checkAssertions defaultPanicCodes) Nothing++          let storeCex = cex.store+              testCex = case (Map.lookup cAddr storeCex, Map.lookup aAddr storeCex) of+                          (Just sC, Just sA) -> case (Map.lookup 0 sC, Map.lookup 0 sA) of+                              (Just x, Just y) -> x /= y+                              (Just x, Nothing) -> x /= 0+                              _ -> False+                          _ -> False+          assertBoolM "Did not find expected storage cex" testCex+          putStrLnM "expected counterexample found"+        , test "calling-unique-contracts--read-from-storage" $ do+          Just c <- solcRuntime "C"+            [i|+              contract C {+                uint x;+                A a;++                function call_A() public {+                  a = new A();+                  // should fail since x can be anything+                  assert(a.x() == x);+                }+              }+              contract A {+                uint public x;+              }+            |]+          (_, [Cex _]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "call_A()" [])) [] defaultVeriOpts+          putStrLnM "expected counterexample found"+        ,+        test "keccak-concrete-and-sym-agree" $ do+          Just c <- solcRuntime "C"+            [i|+              contract C {+                function kecc(uint x) public pure {+                  if (x == 0) {+                    assert(keccak256(abi.encode(x)) == keccak256(abi.encode(0)));+                  }+                }+              }+            |]+          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "kecc(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+        ,+        test "keccak-concrete-and-sym-agree-nonzero" $ do+          Just c <- solcRuntime "C"+            [i|+              contract C {+                function kecc(uint x) public pure {+                  if (x == 55) {+                    // Note: 3014... is the encode & keccak & uint256 conversion of 55+                    assert(uint256(keccak256(abi.encode(x))) == 30148980456718914367279254941528755963179627010946392082519497346671089299886);+                  }+                }+              }+            |]+          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "kecc(uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+        ,+        test "keccak concrete and sym injectivity" $ do+          Just c <- solcRuntime "A"+            [i|+              contract A {+                function f(uint x) public pure {+                  if (x !=3) assert(keccak256(abi.encode(x)) != keccak256(abi.encode(3)));+                }+              }+            |]+          (res, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLnM $ "successfully explored: " <> show (length res) <> " paths"+        ,+        test "safemath-distributivity-yul" $ do+          let yulsafeDistributivity = hex "6355a79a6260003560e01c14156016576015601f565b5b60006000fd60a1565b603d602d604435600435607c565b6039602435600435607c565b605d565b6052604b604435602435605d565b600435607c565b141515605a57fe5b5b565b6000828201821115151560705760006000fd5b82820190505b92915050565b6000818384048302146000841417151560955760006000fd5b82820290505b92915050565b"+          (calldata, _) <- mkCalldata (Just (Sig "distributivity(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) []+          vm <- liftIO $ stToIO $ abstractVM calldata yulsafeDistributivity Nothing False+          (_, []) <-  withDefaultSolver $ \s -> verify s (Fetch.noRpcFetcher s) defaultVeriOpts vm (checkAssertions defaultPanicCodes) Nothing+          putStrLnM "Proven"+        ,+        test "safemath-distributivity-sol" $ do+          Just c <- solcRuntime "C"+            [i|+              contract C {+                function distributivity(uint x, uint y, uint z) public {+                  assert(mul(x, add(y, z)) == add(mul(x, y), mul(x, z)));+                }++                function add(uint x, uint y) internal pure returns (uint z) {+                  unchecked {+                    require((z = x + y) >= x, "ds-math-add-overflow");+                    }+                }++                function mul(uint x, uint y) internal pure returns (uint z) {+                  unchecked {+                    require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");+                  }+                }+              }+            |]++          (_, []) <- withSolvers Bitwuzla 1 (Just 99999999) defMemLimit $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "distributivity(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+          putStrLnM "Proven"+        ,+        test "storage-cex-1" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              uint x;+              uint y;+              function fun(uint256 a) external{+                require(x != 0);+                require(y != 0);+                assert (x == y);+              }+            }+            |]+          (_, [(Cex (_, cex))]) <- withDefaultSolver $ \s -> checkAssert s [0x01] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          let addr = SymAddr "entrypoint"+              testCex = Map.size cex.store == 1 &&+                        case Map.lookup addr cex.store of+                          Just s -> Map.size s == 2 &&+                                    case (Map.lookup 0 s, Map.lookup 1 s) of+                                      (Just x, Just y) -> x /= y+                                      _ -> False+                          _ -> False+          assertBoolM "Did not find expected storage cex" testCex+          putStrLnM "Expected counterexample found"+        ,+        test "storage-cex-2" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              uint[10] arr1;+              uint[10] arr2;+              function fun(uint256 a) external{+                assert (arr1[0] < arr2[a]);+              }+            }+            |]+          (_, [(Cex (_, cex))]) <- withDefaultSolver $ \s -> checkAssert s [0x01] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          let addr = SymAddr "entrypoint"+              a = getVar cex "arg1"+              testCex = Map.size cex.store == 1 &&+                        case Map.lookup addr cex.store of+                          Just s -> case (Map.lookup 0 s, Map.lookup (10 + a) s) of+                                      (Just x, Just y) -> x >= y+                                      _ -> False+                          Nothing -> False -- arr2 must contain an element, or it'll be 0+          assertBoolM "Did not find expected storage cex" testCex+          putStrLnM "Expected counterexample found"+        ,+        test "storage-cex-concrete" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              uint x;+              uint y;+              function fun(uint256 a) external{+                require (x != 0);+                require (y != 0);+                assert (x != y);+              }+            }+            |]+          let sig = Just (Sig "fun(uint256)" [AbiUIntType 256])+          (_, [Cex (_, cex)]) <- withDefaultSolver $+            \s -> verifyContract s c sig [] defaultVeriOpts Nothing (checkAssertions [0x01])+          let addr = SymAddr "entrypoint"+              testCex = Map.size cex.store == 1 &&+                        case Map.lookup addr cex.store of+                          Just s -> Map.size s == 2 &&+                                    case (Map.lookup 0 s, Map.lookup 1 s) of+                                      (Just x, Just y) -> x == y+                                      _ -> False+                          _ -> False+          assertBoolM "Did not find expected storage cex" testCex+          putStrLnM "Expected counterexample found"+        , test "temp-store-check" $ do+          Just c <- solcRuntime "C"+            [i|+            pragma solidity ^0.8.25;+            contract C {+                mapping(address => bool) sentGifts;+                function stuff(address k) public {+                    require(sentGifts[k] == false);+                    assembly {+                        if tload(0) { revert(0, 0) }+                        tstore(0, 1)+                    }+                    sentGifts[k] = true;+                    assembly {+                        tstore(0, 0)+                    }+                    assert(sentGifts[k]);+                }+            }+            |]+          let sig = (Just (Sig "stuff(address)" [AbiAddressType]))+          (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+          putStrLnM $ "Basic tstore check passed"+  ]+  , testGroup "state-merging"+    -- Tests for ITE-based state merging during symbolic execution+    -- State merging combines multiple execution paths into a single path with ITE expressions+    [ testCase "merge-simple-branches" $ do+        -- Simple branching pattern that should be merged+        Just c <- solcRuntime "C"+          [i|+          contract C {+            function f(uint256 x) public pure {+              unchecked {+              uint256 result = 1;+              if (x & 0x1 != 0) result = result * 2;+              if (x & 0x2 != 0) result = result * 3;+              assert(result > 0);+              }+            }+          }+          |]+        let sig = Just (Sig "f(uint256)" [AbiUIntType 256])+        noMerege <- runEnv testEnv { config = testEnv.config {mergeMaxBudget = 0}} $ do+          e <- withDefaultSolver $ \s -> getExpr s c sig [] defaultVeriOpts+          pure $ length e+        merge <- runEnv testEnv { config = testEnv.config {mergeMaxBudget = 1000}} $ do+          e <- withDefaultSolver $ \s -> getExpr s c sig [] defaultVeriOpts+          pure $ length e+        assertBoolM "Merging should reduce number of paths" (merge < noMerege)+     -- Checked arithmetic reverts in one of the branches, which is not supported by+     -- the current state merging implementation+     , expectFail $ testCase "merge-simple-branches-revert" $ do+        Just c <- solcRuntime "C"+          [i|+          contract C {+            function f(uint256 x) public pure {+              uint256 result = 1;+              if (x & 0x1 != 0) result = result * 2;+              if (x & 0x2 != 0) result = result * 3;+              assert(result > 0);+            }+          }+          |]+        let sig = Just (Sig "f(uint256)" [AbiUIntType 256])+        noMerege <- runEnv testEnv { config = testEnv.config {mergeMaxBudget = 0}} $ do+          e <- withDefaultSolver $ \s -> getExpr s c sig [] defaultVeriOpts+          pure $ length e+        merge <- runEnv testEnv { config = testEnv.config {mergeMaxBudget = 1000}} $ do+          e <- withDefaultSolver $ \s -> getExpr s c sig [] defaultVeriOpts+          pure $ length e+        assertBoolM "Merging should reduce number of paths" (merge < noMerege)+    , test "merge-finds-counterexample" $ do+        -- Merged paths should still find counterexamples+        Just c <- solcRuntime "C"+          [i|+          contract C {+            function f(uint256 x) public pure {+              uint256 result = 1;+              if (x & 0x1 != 0) result = result * 2;+              if (x & 0x2 != 0) result = result * 3;+              // Bug: result == 6 when both bits are set+              assert(result != 6);+            }+          }+          |]+        let sig = Just (Sig "f(uint256)" [AbiUIntType 256])+        (_, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertBoolM "One counterexample should be found even with merging" (exactlyCex 1 ret)+    , test "merge-many-branches-unchecked" $ do+        -- Multiple branches with unchecked arithmetic+        -- 4 branches = 2^4 = 16 paths without merging+        -- With unchecked + merging, should be minimal paths+        Just c <- solcRuntime "C"+          [i|+          contract C {+            function f(uint256 x) public pure {+              uint256 result = 1;+              unchecked {+                if (x & 0x1 != 0) result = result * 2;+                if (x & 0x2 != 0) result = result * 3;+                if (x & 0x4 != 0) result = result * 5;+                if (x & 0x8 != 0) result = result * 7;+              }+              assert(result > 0);+            }+          }+          |]+        let sig = Just (Sig "f(uint256)" [AbiUIntType 256])+        paths <- withDefaultSolver $ \s -> getExpr s c sig [] defaultVeriOpts+        let numPaths = length paths+        -- Without merging: 16 paths. With unchecked + merging: should be <= 4+        liftIO $ assertBool ("Expected at most 4 paths with unchecked merging, got " ++ show numPaths) (numPaths <= 4)+    , test "merge-with-unchecked-arithmetic" $ do+        Just c <- solcRuntime "C"+          [i|+          contract C {+            function f(uint256 tick) public pure returns (uint256 ratio) {+              ratio = 0x100000000000000000000000000000000;+              unchecked {+                if (tick & 0x1 != 0) ratio = 0xfffcb933bd6fad37aa2d162d1a594001;+                if (tick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;+                if (tick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;+                if (tick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;+              }+              assert(ratio > 0);+            }+          }+          |]+        let sig = Just (Sig "f(uint256)" [AbiUIntType 256])+        (paths, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        let numPaths = length paths+        -- Without merging: 16 paths. With unchecked + merging: should be <= 4+        liftIO $ assertBool ("Expected at most 4 paths with unchecked merging, got " ++ show numPaths) (numPaths <= 4)+    , test "merge-counterexample-three-branches" $ do+        -- Find counterexample with 3 merged branches+        Just c <- solcRuntime "C"+          [i|+          contract C {+            function f(uint256 x) public pure {+              uint256 result = 1;+              if (x & 0x1 != 0) result = result * 100;+              if (x & 0x2 != 0) result = result * 100;+              if (x & 0x4 != 0) result = result * 100;+              // Bug: result = 1000000 when all 3 bits are set+              assert(result < 1000000);+            }+          }+          |]+        let sig = Just (Sig "f(uint256)" [AbiUIntType 256])+        (_, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertBoolM "Expected a counterexample with merged branches" (exactlyCex 1 ret)+    , test "no-false-positive-nested-branches" $ do+        -- Verify that nested branches don't cause false positives+        -- This tests soundness: if both paths of a nested branch don't converge+        -- to the same point, merging should be disabled to avoid invalid states+        Just c <- solcRuntime "C"+          [i|+          contract C {+            function f(uint256 x, uint256 y) public pure {+              uint256 result = 1;+              // Nested branches that depend on different inputs+              if (x & 0x1 != 0) {+                if (y & 0x1 != 0) {+                  result = result * 2;+                } else {+                  result = result * 3;+                }+              }+              // result is either 1, 2, or 3+              assert(result == 1 || result == 2 || result == 3);+            }+          }+          |]+        let sig = Just (Sig "f(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])+        (_, []) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        putStrLnM "Nested branches handled correctly without false positives"+    , test "merge-simplify-zero-mul-in-loop" $ do+        -- Regression test: merging inside a loop where one branch multiplies by zero.+        -- Without simplification of merged ITE expressions, Mul (Lit 0) (ITE ...)+        -- accumulates unsimplified across loop iterations, causing unbounded memory growth.+        -- This is the pattern from ABDKMath64x64.pow(0, x).+        Just c <- solcRuntime "C"+          [i|+          contract C {+            function f(uint256 x) public pure {+              uint256 base = 0;+              uint256 result = 0x100000000;+              unchecked {+                // Unrolled loop: 8 sequential conditional multiplications by zero.+                // Each creates an ITE where true-branch is Mul(result, 0) and false+                // leaves result unchanged. Without simplifying Mul(Lit 0, ITE(...))+                // to Lit 0 at merge time, the expression tree grows unboundedly.+                if (x & 0x1 != 0) result = result * base;+                base = base * base;+                if (x & 0x2 != 0) result = result * base;+                base = base * base;+                if (x & 0x4 != 0) result = result * base;+                base = base * base;+                if (x & 0x8 != 0) result = result * base;+                base = base * base;+                if (x & 0x10 != 0) result = result * base;+                base = base * base;+                if (x & 0x20 != 0) result = result * base;+                base = base * base;+                if (x & 0x40 != 0) result = result * base;+                base = base * base;+                if (x & 0x80 != 0) result = result * base;+              }+              // If any bit is set, result becomes 0; if x&0xff==0, result stays 0x100000000+              // Either way result <= 0x100000000+              assert(result <= 0x100000000);+            }+          }+          |]+        let sig = Just (Sig "f(uint256)" [AbiUIntType 256])+        (_, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertEqualM "Zero-mul loop merging works without expression blowup" [] ret+    , test "no-merge-with-memory-write" $ do+        -- Branches with memory writes should not cause issues+        Just c <- solcRuntime "C"+          [i|+          contract C {+            function f(uint256 x) public pure {+              uint256[] memory arr = new uint256[](2);+              arr[0] = 1;+              if (x & 0x1 != 0) {+                arr[1] = 2;+              }+              assert(arr[0] == 1);+            }+          }+          |]+        let sig = Just (Sig "f(uint256)" [AbiUIntType 256])+        (_, ret) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+        assertEqualM "Merging should not cause issues with memory writes" [] ret+    ]+  , testGroup "SMT-encoding"+  [ testCase "encodeConcreteStore-overwrite" $+    assertEqual ""+      (pure "(store (store ((as const Storage) #x0000000000000000000000000000000000000000000000000000000000000000) (_ bv1 256) (_ bv2 256)) (_ bv3 256) (_ bv4 256))")+      (EVM.SMT.encodeConcreteStore $ Map.fromList [(W256 1, W256 2), (W256 3, W256 4)])+  ]+  , testGroup "calling-solvers"+  [ test "no-error-on-large-buf" $ do+      -- These two tests generates a very large buffer that previously would cause an internalError when+      -- printed via "formatCex". We should be able to print it now.+      Just c <- solcRuntime "MyContract" [i|+          contract MyContract {+            function fun(bytes calldata a) external pure {+              if (a.length > 0x800000000000) {+                assert(false);+              }+            }+           } |]+      (_, [Cex cex]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+      putStrLnM $ "Cex found:" <> T.unpack (formatCex (AbstractBuf "txdata") Nothing (snd cex))+    , test "no-error-on-large-buf-pure-print" $ do+      let bufs = Map.singleton (AbstractBuf "txdata")+                  (EVM.Types.Comp Write {byte = 1, idx = 0x27, next = Base {byte = 0x66, length = 0xffff000000000000000}})+      let mycex = SMTCex {vars = mempty+                         , addrs = mempty+                         , buffers = bufs+                         , store = mempty+                         , blockContext = mempty+                         , txContext = Map.fromList [(TxValue,0x0)]}+      putStrLnM $ "Cex found:" <> T.unpack (formatCex (AbstractBuf "txdata") Nothing mycex)+    , test "correct-model-for-empty-buffer" $ do+      withDefaultSolver $ \s -> do+        let props = [(PEq (BufLength (AbstractBuf "b")) (Lit 0x0))]+        res <- checkSatWithProps s props+        (cex) <- case res of+          Cex c -> pure c+          _ -> liftIO $ assertFailure "Must be satisfiable!"+        let value = fromRight (error "cannot be") $ subModel cex (AbstractBuf "b")+        assertEqualM "Buffer must be empty" (ConcreteBuf "") value+    , test "correct-model-for-non-empty-buffer-of-all-zeroes" $ do+      withDefaultSolver $ \s -> do+        let props = [(PAnd (PEq (ReadByte (Lit 0x0) (AbstractBuf "b")) (LitByte 0x0)) (PEq (BufLength (AbstractBuf "b")) (Lit 0x1)))]+        res <- checkSatWithProps s props+        (cex) <- case res of+          Cex c -> pure c+          _ -> liftIO $ assertFailure "Must be satisfiable!"+        let value = fromRight (error "cannot be") $ subModel cex (AbstractBuf "b")+        assertEqualM "Buffer must have size 1 and contain zero byte" (ConcreteBuf "\0") value+    , test "buffer-shrinking-does-not-loop" $ do+      withDefaultSolver $ \s -> do+        let props = [(PGT (BufLength (AbstractBuf "b")) (Lit 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeb4))]+        res <- checkSatWithProps s props+        let+          sat = case res of+            Cex _ -> True+            _ -> False+        assertBoolM "Must be satisfiable!" sat+    , test "can-get-value-unrelated-to-large-buffer" $ do+      withDefaultSolver $ \s -> do+        let props = [(PEq (Var "a") (Lit 0x1)), (PGT (BufLength (AbstractBuf "b")) (Lit 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeb4))]+        res <- checkSatWithProps s props+        cex :: SMTCex <- case res of+          Cex c -> pure c+          _ -> liftIO $ assertFailure "Must be satisfiable!"+        let value = subModel cex (Var "a")+        assertEqualM "Can get value out of model in the presence of large buffer!" value (Right $ Lit 0x1)+    , test "no-duplicates-with-concrete-keccak" $ do+      let props = [(PGT (Var "a") (Keccak (ConcreteBuf "abcdef"))), (PGT (Var "b") (Keccak (ConcreteBuf "abcdef")))]+      conf <- readConfig+      let SMT2 script _ _ = fromRight (internalError "Must succeed") (assertProps conf props)+      assertBoolM "There were duplicate commands in SMT encoding" $ not (hasDuplicateCommands script)+    , test "no-duplicates-with-read-assumptions" $ do+      let props = [(PGT (ReadWord (Lit 2) (AbstractBuf "test")) (Lit 0)), (PGT (Expr.padByte $ ReadByte (Lit 10) (AbstractBuf "test")) (Expr.padByte $ LitByte 1))]+      conf <- readConfig+      let SMT2 script _ _ = fromRight (internalError "Must succeed") (assertProps conf props)+      assertBoolM "There were duplicate lines in SMT encoding" $ not (hasDuplicateCommands script)+     , test "all-concrete-keccaks-discovered" $ do+      let buf1 = (Keccak (ConcreteBuf "abc"))+          eq = (Eq buf1 (Lit 0x12))+          buf2 = WriteWord eq (Lit 0x0) mempty+          props = [PEq (Keccak buf2) (Lit 0x123)]+          concrete = concreteKeccaks props+      assertEqualM "Must find two keccaks" 2 (length concrete)+    , testCase "store-over-concrete-buffer" $ runEnv (testEnv {config = testEnv.config {simp = False}}) $ do+      let+        as = AbstractStore (SymAddr "test") Nothing+        cs = ConcreteStore $ Map.fromList [(0x1,0x2)]+        e1 = SLoad (Lit 0x1) (SStore (Lit 0x8) (SLoad (Lit 0x40) as) cs)+        eq = PEq e1 (Lit 0x0)+      conf <- readConfig+      let SMT2 _ (CexVars _ _ _ storeReads _ _) _ = fromRight (internalError "Must succeed") (assertProps conf [eq])+      let expected = StorageReads $ Map.singleton (SymAddr "test", Nothing) (Set.singleton (Lit 0x40))+      assertEqualM "Reads must be properly collected" storeReads expected+    , test "all-abstract-reads-detected" $ do+      let mystore = (AbstractStore (SymAddr "test") Nothing)+      let props = [PGT (SLoad (Lit 2) mystore) (SLoad (Lit 0) mystore)]+      conf <- readConfig+      let SMT2 _ cexVars _ = fromRight (internalError "Must succeed") (assertProps conf props)+      let (StorageReads m) = cexVars.storeReads+      case Map.lookup ((SymAddr "test"), Nothing) m of+        Nothing -> assertBoolM "Address missing from storage reads" False+        Just storeReads -> assertBoolM "Did not collect all abstract reads!" $ (Set.size storeReads) == 2+  ]+  ]+  where+    (===>) = assertSolidityComputation++-- | Takes a runtime code and calls it with the provided calldata++-- | Takes a creation code and some calldata, runs the creation code, and calls the resulting contract with the provided calldata+runSimpleVM :: App m => ByteString -> ByteString -> m (Maybe ByteString)+runSimpleVM x ins = do+  loadVM x >>= \case+    Nothing -> pure Nothing+    Just vm -> do+     let calldata = (ConcreteBuf ins)+         vm' = set (#state % #calldata) calldata vm+     res <- Stepper.interpret (Fetch.zero 0 Nothing 1024) vm' Stepper.execFully+     case res of+       Right (ConcreteBuf bs) -> pure $ Just bs+       s -> internalError $ show s++-- | Takes a creation code and returns a vm with the result of executing the creation code+loadVM :: App m => ByteString -> m (Maybe (VM Concrete))+loadVM x = do+  vm <- liftIO $ stToIO $ vmForEthrunCreation x+  vm1 <- Stepper.interpret (Fetch.zero 0 Nothing 1024) vm Stepper.runFully+  case vm1.result of+    Just (VMSuccess (ConcreteBuf targetCode)) -> do+      let target = vm1.state.contract+      vm2 <- Stepper.interpret (Fetch.zero 0 Nothing 1024) vm1 (prepVm target targetCode)+      writeTrace vm2+      pure $ Just vm2+    _ -> pure Nothing+  where+    prepVm target targetCode = Stepper.evm $ do+      replaceCodeOfSelf (RuntimeCode $ ConcreteRuntimeCode targetCode)+      resetState+      assign (#state % #gas) 0xffffffffffffffff -- kludge+      execState (loadContract target) <$> get >>= put+      get++hex :: ByteString -> ByteString+hex s =+  case BS16.decodeBase16Untyped s of+    Right x -> x+    Left e -> internalError $ T.unpack e++singleContract :: Text -> Text -> IO (Maybe ByteString)+singleContract x s =+  solidity x [i|+    pragma experimental ABIEncoderV2;+    contract ${x} { ${s} }+  |]++defaultDataLocation :: AbiType -> Text+defaultDataLocation t =+  if (case t of+        AbiBytesDynamicType -> True+        AbiStringType -> True+        AbiArrayDynamicType _ -> True+        AbiArrayType _ _ -> True+        _ -> False)+  then "memory"+  else ""++runFunction :: App m => Text -> ByteString -> m (Maybe ByteString)+runFunction c input = do+  x <- liftIO $ singleContract "X" c+  runSimpleVM (fromJust x) input++runStatements :: App m => Text -> [AbiValue] -> AbiType -> m (Maybe ByteString)+runStatements stmts args t = do+  let params =+        T.intercalate ", "+          (map (\(x, c) -> abiTypeSolidity (abiValueType x)+                             <> " " <> defaultDataLocation (abiValueType x)+                             <> " " <> T.pack [c])+            (zip args "abcdefg"))+      s =+        "foo(" <> T.intercalate ","+                    (map (abiTypeSolidity . abiValueType) args) <> ")"++  runFunction [i|+    function foo(${params}) public pure returns (${abiTypeSolidity t} ${defaultDataLocation t} x) {+      ${stmts}+    }+  |] (abiMethod s (AbiTuple $ V.fromList args))++getStaticAbiArgs :: Int -> VM Symbolic -> [Expr EWord]+getStaticAbiArgs n vm =+  let cd = vm.state.calldata+  in decodeStaticArgs 4 n cd++-- includes shaving off 4 byte function sig+decodeAbiValues :: [AbiType] -> ByteString -> [AbiValue]+decodeAbiValues types bs =+  let xy = case decodeAbiValue (AbiTupleType $ V.fromList types) (BS.fromStrict (BS.drop 4 bs)) of+        AbiTuple xy' -> xy'+        _ -> internalError "AbiTuple expected"+  in V.toList xy++-- abi types that are supported in the symbolic abi encoder+newtype SymbolicAbiType = SymbolicAbiType AbiType+  deriving (Eq, Show)++newtype SymbolicAbiVal = SymbolicAbiVal AbiValue+  deriving (Eq, Show)++instance Arbitrary SymbolicAbiVal where+  arbitrary = do+    SymbolicAbiType ty <- arbitrary+    SymbolicAbiVal <$> genAbiValue ty++instance Arbitrary SymbolicAbiType where+  arbitrary = SymbolicAbiType <$> frequency+    [ (5, (AbiUIntType . (* 8)) <$> choose (1, 32))+    , (5, (AbiIntType . (* 8)) <$> choose (1, 32))+    , (5, pure AbiAddressType)+    , (5, pure AbiBoolType)+    , (5, AbiBytesType <$> choose (1,32))+    , (1, do SymbolicAbiType ty <- scale (`div` 2) arbitrary+             AbiArrayType <$> (choose (1, 30)) <*> pure ty+      )+    ]++newtype Bytes = Bytes ByteString+  deriving Eq++instance Show Bytes where+  showsPrec _ (Bytes x) _ = show (BS.unpack x)++instance Arbitrary Bytes where+  arbitrary = fmap (Bytes . BS.pack) arbitrary++newtype RLPData = RLPData RLP+  deriving (Eq, Show)++-- bias towards bytestring to try to avoid infinite recursion+instance Arbitrary RLPData where+  arbitrary = frequency+   [(5, do+           Bytes bytes <- arbitrary+           return $ RLPData $ BS bytes)+   , (1, do+         k <- choose (0,10)+         ls <- vectorOf k arbitrary+         return $ RLPData $ List [r | RLPData r <- ls])+   ]++genNat :: Gen Int+genNat = fmap unsafeInto (arbitrary :: Gen Natural)++data Invocation+  = SolidityCall Text [AbiValue]+  deriving Show++assertSolidityComputation :: App m => Invocation -> AbiValue -> m ()+assertSolidityComputation (SolidityCall s args) x =+  do y <- runStatements s args (abiValueType x)+     liftIO $ assertEqual (T.unpack s)+       (fmap Bytes (Just (encodeAbiValue x)))+       (fmap Bytes y)++bothM :: (Monad m) => (a -> m b) -> (a, a) -> m (b, b)+bothM f (a, a') = do+  b  <- f a+  b' <- f a'+  return (b, b')++applyPattern :: String -> TestTree  -> TestTree+applyPattern p = localOption (TestPattern (parseExpr p))++checkBadCheatCode :: Text -> Postcondition+checkBadCheatCode sig _ = \case+  (Failure _ c (Revert _)) -> case mapMaybe findBadCheatCode (concatMap flatten c.traces) of+    (s:_) -> (ConcreteBuf $ into s.unFunctionSelector) ./= (ConcreteBuf $ selector sig)+    _ -> PBool True+  _ -> PBool True+  where+    findBadCheatCode :: Trace -> Maybe FunctionSelector+    findBadCheatCode Trace { tracedata = td } = case td of+      ErrorTrace (BadCheatCode _ s) -> Just s+      _ -> Nothing++reachableUserAsserts :: App m => ByteString -> Maybe Sig -> m (Either [SMTCex] [Expr End])+reachableUserAsserts = checkPost (checkAssertions [0x01])++checkPost :: App m => Postcondition -> ByteString -> Maybe Sig -> m (Either [SMTCex] [Expr End])+checkPost post c sig = do+  (e, res) <- withDefaultSolver $ \s -> verifyContract s c sig [] defaultVeriOpts Nothing post+  let cexs = snd <$> mapMaybe getCex res+  case cexs of+    [] -> pure $ Right e+    cs -> pure $ Left cs++-- gets the expected concrete values for symbolic abi testing+expectedConcVals :: Text -> AbiValue -> SMTCex+expectedConcVals nm val = case val of+  AbiUInt {} -> mempty { vars = Map.fromList [(Var nm, mkWord val)] }+  AbiInt {} -> mempty { vars = Map.fromList [(Var nm, mkWord val)] }+  AbiAddress {} -> mempty { addrs = Map.fromList [(SymAddr nm, truncateToAddr (mkWord val))] }+  AbiBool {} -> mempty { vars = Map.fromList [(Var nm, mkWord val)] }+  AbiBytes {} -> mempty { vars = Map.fromList [(Var nm, mkWord val)] }+  AbiArray _ _ vals -> mconcat . V.toList . V.imap (\(T.pack . show -> idx) v -> expectedConcVals (nm <> "-a-" <> idx) v) $ vals+  AbiTuple vals -> mconcat . V.toList . V.imap (\(T.pack . show -> idx) v -> expectedConcVals (nm <> "-t-" <> idx) v) $ vals+  _ -> internalError $ "unsupported Abi type " <> show nm <> " val: " <> show val <> " val type: " <> showAlter val+  where+    mkWord = word . encodeAbiValue+