packages feed

hevm 0.51.3 → 0.52.0

raw patch · 55 files changed

+10119/−12270 lines, 55 filesdep +pretty-hexdep +system-cxx-std-libdep −ansi-wl-pprintdep −tuplePVP ok

version bump matches the API change (PVP)

Dependencies added: pretty-hex, system-cxx-std-lib

Dependencies removed: ansi-wl-pprint, tuple

API changes (from Hackage documentation)

- EVM.Concrete: (^) :: W256 -> W256 -> W256
- EVM.Concrete: readByteOrZero :: Int -> ByteString -> Word8
- EVM.Concrete: wordAt :: Int -> ByteString -> W256
- EVM.Dapp: ConcreteTest :: Text -> Test
- EVM.Dapp: InvariantTest :: Text -> Test
- EVM.Dapp: SymbolicTest :: Text -> Test
- EVM.Dapp: data Test
- EVM.Dapp: extractSig :: Test -> Text
- EVM.Dapp: instance GHC.Show.Show EVM.Dapp.Test
- EVM.Dapp: mkTest :: Text -> Maybe Test
- EVM.Debug: Debug :: Mode
- EVM.Debug: JsonTrace :: Mode
- EVM.Debug: Run :: Mode
- EVM.Debug: data Mode
- EVM.Debug: instance GHC.Classes.Eq EVM.Debug.Mode
- EVM.Debug: instance GHC.Show.Show EVM.Debug.Mode
- EVM.Debug: object :: [(Doc, Doc)] -> Doc
- EVM.Debug: prettyContract :: Contract -> Doc
- EVM.Debug: prettyContracts :: Map Addr Contract -> Doc
- EVM.Debug: srcMapCode :: SourceCache -> SrcMap -> Maybe ByteString
- EVM.Debug: srcMapCodePos :: SourceCache -> SrcMap -> Maybe (FilePath, Int)
- EVM.Dev: analyzeDai :: IO ()
- EVM.Dev: analyzeDeposit :: IO ()
- EVM.Dev: analyzeVat :: IO ()
- EVM.Dev: buildExpr :: SolverGroup -> ByteString -> IO (Expr End)
- EVM.Dev: checkEquiv :: Typeable a => Expr a -> Expr a -> IO ()
- EVM.Dev: dai :: IO ByteString
- EVM.Dev: daiExpr :: IO (Expr End)
- EVM.Dev: doTest :: IO ()
- EVM.Dev: initVm :: ByteString -> VM
- EVM.Dev: reachable' :: Bool -> ByteString -> IO ()
- EVM.Dev: runDappTest :: FilePath -> IO ()
- EVM.Dev: safeAdd :: IO ByteString
- EVM.Dev: showExpr :: ByteString -> IO ()
- EVM.Dev: summaryStore :: IO ByteString
- EVM.Dev: testContract :: IO ByteString
- EVM.Dev: testOpts :: SolverGroup -> FilePath -> FilePath -> IO UnitTestOptions
- EVM.Dev: vat :: IO ByteString
- EVM.Expr: concPrefix :: Expr Buf -> Maybe Integer
- EVM.Facts: BalanceFact :: Addr -> W256 -> Fact
- EVM.Facts: CodeFact :: Addr -> ByteString -> Fact
- EVM.Facts: Data :: ASCII -> Data
- EVM.Facts: File :: Path -> Data -> File
- EVM.Facts: NonceFact :: Addr -> W256 -> Fact
- EVM.Facts: Path :: [ASCII] -> ASCII -> Path
- EVM.Facts: StorageFact :: Addr -> W256 -> W256 -> Fact
- EVM.Facts: [$sel:addr:BalanceFact] :: Fact -> Addr
- EVM.Facts: [$sel:blob:BalanceFact] :: Fact -> ByteString
- EVM.Facts: [$sel:dataASCII:Data] :: Data -> ASCII
- EVM.Facts: [$sel:fileData:File] :: File -> Data
- EVM.Facts: [$sel:filePath:File] :: File -> Path
- EVM.Facts: [$sel:what:BalanceFact] :: Fact -> W256
- EVM.Facts: [$sel:which:BalanceFact] :: Fact -> W256
- EVM.Facts: apply :: VM -> Set Fact -> VM
- EVM.Facts: applyCache :: VM -> Set Fact -> VM
- EVM.Facts: cacheFacts :: Cache -> Set Fact
- EVM.Facts: contractFacts :: Addr -> Contract -> Map W256 (Map W256 W256) -> [Fact]
- EVM.Facts: data Fact
- EVM.Facts: data File
- EVM.Facts: data Path
- EVM.Facts: factToFile :: Fact -> File
- EVM.Facts: fileToFact :: File -> Maybe Fact
- EVM.Facts: instance EVM.Facts.AsASCII Data.ByteString.Internal.Type.ByteString
- EVM.Facts: instance EVM.Facts.AsASCII EVM.Types.Addr
- EVM.Facts: instance EVM.Facts.AsASCII EVM.Types.W256
- EVM.Facts: instance GHC.Classes.Eq EVM.Facts.Data
- EVM.Facts: instance GHC.Classes.Eq EVM.Facts.Fact
- EVM.Facts: instance GHC.Classes.Eq EVM.Facts.File
- EVM.Facts: instance GHC.Classes.Eq EVM.Facts.Path
- EVM.Facts: instance GHC.Classes.Ord EVM.Facts.Data
- EVM.Facts: instance GHC.Classes.Ord EVM.Facts.Fact
- EVM.Facts: instance GHC.Classes.Ord EVM.Facts.File
- EVM.Facts: instance GHC.Classes.Ord EVM.Facts.Path
- EVM.Facts: instance GHC.Show.Show EVM.Facts.Data
- EVM.Facts: instance GHC.Show.Show EVM.Facts.Fact
- EVM.Facts: instance GHC.Show.Show EVM.Facts.File
- EVM.Facts: instance GHC.Show.Show EVM.Facts.Path
- EVM.Facts: newtype Data
- EVM.Facts: vmFacts :: VM -> Set Fact
- EVM.Facts.Git: RepoAt :: String -> RepoAt
- EVM.Facts.Git: instance GHC.Classes.Eq EVM.Facts.Git.RepoAt
- EVM.Facts.Git: instance GHC.Classes.Ord EVM.Facts.Git.RepoAt
- EVM.Facts.Git: instance GHC.Show.Show EVM.Facts.Git.RepoAt
- EVM.Facts.Git: loadFacts :: RepoAt -> IO (Set Fact)
- EVM.Facts.Git: newtype RepoAt
- EVM.Facts.Git: saveFacts :: RepoAt -> Set Fact -> IO ()
- EVM.FeeSchedule: berlin :: Num n => FeeSchedule n
- EVM.FeeSchedule: eip1108 :: EIP n
- EVM.FeeSchedule: eip150 :: EIP n
- EVM.FeeSchedule: eip160 :: EIP n
- EVM.FeeSchedule: eip1884 :: EIP n
- EVM.FeeSchedule: eip2028 :: EIP n
- EVM.FeeSchedule: eip2200 :: EIP n
- EVM.FeeSchedule: eip2929 :: EIP n
- EVM.FeeSchedule: homestead :: Num n => FeeSchedule n
- EVM.FeeSchedule: istanbul :: Num n => FeeSchedule n
- EVM.FeeSchedule: metropolis :: Num n => FeeSchedule n
- EVM.FeeSchedule: type EIP n = Num n => FeeSchedule n -> FeeSchedule n
- EVM.Hexdump: paddedShowHex :: (Show a, Integral a) => Int -> a -> String
- EVM.Hexdump: prettyHex :: Int -> ByteString -> String
- EVM.Hexdump: simpleHex :: ByteString -> String
- EVM.Patricia: DB :: Free (KV k v) a -> DB k v a
- EVM.Patricia: Empty :: Node
- EVM.Patricia: Full :: Seq Ref -> ByteString -> Node
- EVM.Patricia: Get :: k -> (v -> a) -> KV k v a
- EVM.Patricia: Hash :: ByteString -> Ref
- EVM.Patricia: Literal :: Node -> Ref
- EVM.Patricia: Put :: k -> v -> a -> KV k v a
- EVM.Patricia: Shortcut :: Path -> Either Ref ByteString -> Node
- EVM.Patricia: addPrefix :: Path -> Node -> NodeDB Node
- EVM.Patricia: calcRoot :: [(ByteString, ByteString)] -> Maybe ByteString
- EVM.Patricia: data KV k v a
- EVM.Patricia: data Node
- EVM.Patricia: data Ref
- EVM.Patricia: delete :: Node -> Path -> NodeDB Node
- EVM.Patricia: emptyRef :: Ref
- EVM.Patricia: emptyRefs :: Seq Ref
- EVM.Patricia: encodePath :: Path -> Bool -> ByteString
- EVM.Patricia: getNode :: Ref -> NodeDB Node
- EVM.Patricia: getVal :: Path -> Node -> NodeDB ByteString
- EVM.Patricia: insert :: Ref -> ByteString -> ByteString -> NodeDB Ref
- EVM.Patricia: insertDB :: k -> v -> DB k v ()
- EVM.Patricia: insertRef :: Ref -> Path -> ByteString -> NodeDB Ref
- EVM.Patricia: insertValues :: [(ByteString, ByteString)] -> Maybe Ref
- EVM.Patricia: instance GHC.Base.Applicative (EVM.Patricia.DB k v)
- EVM.Patricia: instance GHC.Base.Functor (EVM.Patricia.DB k v)
- EVM.Patricia: instance GHC.Base.Functor (EVM.Patricia.KV k v)
- EVM.Patricia: instance GHC.Base.Monad (EVM.Patricia.DB k v)
- EVM.Patricia: instance GHC.Classes.Eq EVM.Patricia.Node
- EVM.Patricia: instance GHC.Classes.Eq EVM.Patricia.Ref
- EVM.Patricia: instance GHC.Show.Show (EVM.Patricia.NodeDB EVM.Patricia.Node)
- EVM.Patricia: instance GHC.Show.Show EVM.Patricia.Node
- EVM.Patricia: instance GHC.Show.Show EVM.Patricia.Ref
- EVM.Patricia: lookupDB :: k -> DB k v v
- EVM.Patricia: lookupIn :: Ref -> ByteString -> NodeDB ByteString
- EVM.Patricia: lookupPath :: Ref -> Path -> NodeDB ByteString
- EVM.Patricia: newtype DB k v a
- EVM.Patricia: putNode :: Node -> NodeDB Ref
- EVM.Patricia: rlpNode :: Node -> RLP
- EVM.Patricia: rlpRef :: Ref -> RLP
- EVM.Patricia: runDB :: Monad m => (k -> v -> m ()) -> (k -> m v) -> DB k v a -> m a
- EVM.Patricia: runMapDB :: Ord k => DB k v a -> MapDB k v a
- EVM.Patricia: runTrie :: DB ByteString ByteString a -> Trie a
- EVM.Patricia: type MapDB k v a = StateT (Map k v) Maybe a
- EVM.Patricia: type NodeDB = DB ByteString Node
- EVM.Patricia: type Path = [Nibble]
- EVM.Patricia: type Trie = StateT Ref NodeDB
- EVM.Patricia: update :: Node -> Path -> ByteString -> NodeDB Node
- EVM.SMT: interpret2DArray :: Map Symbol Term -> Term -> W256 -> W256 -> W256
- EVM.SMT: parseFrameCtx :: Text -> Expr EWord
- EVM.SMT: referencedBlockContext' :: Prop -> [(Builder, [Prop])]
- EVM.SMT: referencedBlockContextGo :: Expr a -> [(Builder, [Prop])]
- EVM.SMT: referencedBufs' :: Prop -> [Builder]
- EVM.SMT: referencedBufsGo :: Expr a -> [Builder]
- EVM.SMT: referencedFrameContext' :: Prop -> [(Builder, [Prop])]
- EVM.SMT: referencedFrameContextGo :: Expr a -> [(Builder, [Prop])]
- EVM.SMT: referencedVars' :: Prop -> [Builder]
- EVM.SMT: referencedVarsGo :: Expr a -> [Builder]
- EVM.Solidity: solidity' :: Text -> IO (Text, Text)
- EVM.Solidity: yul' :: Text -> IO (Text, Text)
- EVM.Stepper: [Run] :: Action VM
- EVM.Stepper: entering :: Text -> Stepper a -> Stepper a
- EVM.StorageLayout: findContractDefinition :: DappInfo -> SolcContract -> Maybe Value
- EVM.StorageLayout: grokDeclarationType :: Value -> SlotType
- EVM.StorageLayout: grokMappingType :: [Value] -> SlotType
- EVM.StorageLayout: grokValueType :: Value -> AbiType
- EVM.StorageLayout: isStorageVariableDeclaration :: Value -> Bool
- EVM.StorageLayout: nodeIs :: Text -> Value -> Bool
- EVM.StorageLayout: slotTypeForDeclaration :: Value -> SlotType
- EVM.StorageLayout: storageLayout :: DappInfo -> SolcContract -> [Text]
- EVM.StorageLayout: storageVariablesForContract :: Value -> Maybe [Text]
- EVM.SymExec: Sig :: Text -> [AbiType] -> Sig
- EVM.SymExec: data Sig
- EVM.SymExec: evalProp :: Prop -> Prop
- EVM.TTY: AbiPane :: Name
- EVM.TTY: BrowserPane :: Name
- EVM.TTY: BytecodePane :: Name
- EVM.TTY: Continue :: Stepper a -> Continuation a
- EVM.TTY: Pager :: Name
- EVM.TTY: SolidityPane :: Name
- EVM.TTY: StackPane :: Name
- EVM.TTY: Step :: !Int -> StepMode
- EVM.TTY: StepUntil :: Pred VM -> StepMode
- EVM.TTY: Stopped :: a -> Continuation a
- EVM.TTY: TestPickerPane :: Name
- EVM.TTY: TracePane :: Name
- EVM.TTY: UiBrowserState :: List Name (Addr, Contract) -> UiVmState -> UiBrowserState
- EVM.TTY: UiTestPickerState :: List Name (Text, Text) -> DappInfo -> UnitTestOptions -> UiTestPickerState
- EVM.TTY: UiVmState :: VM -> Int -> Map Int (VM, Stepper ()) -> Stepper () -> Bool -> UnitTestOptions -> UiVmState
- EVM.TTY: ViewContracts :: UiBrowserState -> UiState
- EVM.TTY: ViewHelp :: UiVmState -> UiState
- EVM.TTY: ViewPicker :: UiTestPickerState -> UiState
- EVM.TTY: ViewVm :: UiVmState -> UiState
- EVM.TTY: [$sel:contracts:UiBrowserState] :: UiBrowserState -> List Name (Addr, Contract)
- EVM.TTY: [$sel:dapp:UiTestPickerState] :: UiTestPickerState -> DappInfo
- EVM.TTY: [$sel:opts:UiTestPickerState] :: UiTestPickerState -> UnitTestOptions
- EVM.TTY: [$sel:showMemory:UiVmState] :: UiVmState -> Bool
- EVM.TTY: [$sel:snapshots:UiVmState] :: UiVmState -> Map Int (VM, Stepper ())
- EVM.TTY: [$sel:step:UiVmState] :: UiVmState -> Int
- EVM.TTY: [$sel:stepper:UiVmState] :: UiVmState -> Stepper ()
- EVM.TTY: [$sel:testOpts:UiVmState] :: UiVmState -> UnitTestOptions
- EVM.TTY: [$sel:tests:UiTestPickerState] :: UiTestPickerState -> List Name (Text, Text)
- EVM.TTY: [$sel:vm:UiBrowserState] :: UiBrowserState -> UiVmState
- EVM.TTY: [$sel:vm:UiVmState] :: UiVmState -> VM
- EVM.TTY: _ViewContracts :: Prism' UiState UiBrowserState
- EVM.TTY: _ViewHelp :: Prism' UiState UiVmState
- EVM.TTY: _ViewPicker :: Prism' UiState UiTestPickerState
- EVM.TTY: _ViewVm :: Prism' UiState UiVmState
- EVM.TTY: activeAttr :: AttrName
- EVM.TTY: app :: UnitTestOptions -> App UiState () Name
- EVM.TTY: appEvent :: (?fetcher :: Fetcher, ?maxIter :: Maybe Integer) => BrickEvent Name e -> EventM Name UiState ()
- EVM.TTY: backstep :: (?fetcher :: Fetcher, ?maxIter :: Maybe Integer) => UiVmState -> IO UiVmState
- EVM.TTY: backstepUntil :: (?fetcher :: Fetcher, ?maxIter :: Maybe Integer) => (UiVmState -> Pred VM) -> EventM n UiState ()
- EVM.TTY: boldAttr :: AttrName
- EVM.TTY: currentSrcMap :: DappInfo -> VM -> Maybe SrcMap
- EVM.TTY: data Continuation a
- EVM.TTY: data Name
- EVM.TTY: data StepMode
- EVM.TTY: data UiBrowserState
- EVM.TTY: data UiState
- EVM.TTY: data UiTestPickerState
- EVM.TTY: data UiVmState
- EVM.TTY: debuggableTests :: UnitTestOptions -> (Text, [(Test, [AbiType])]) -> [(Text, Text)]
- EVM.TTY: dim :: Widget n -> Widget n
- EVM.TTY: dimAttr :: AttrName
- EVM.TTY: drawBytecodePane :: UiVmState -> UiWidget
- EVM.TTY: drawHelpBar :: UiWidget
- EVM.TTY: drawHelpView :: [UiWidget]
- EVM.TTY: drawSolidityPane :: UiVmState -> UiWidget
- EVM.TTY: drawStackPane :: UiVmState -> UiWidget
- EVM.TTY: drawTestPicker :: UiTestPickerState -> [UiWidget]
- EVM.TTY: drawTracePane :: UiVmState -> UiWidget
- EVM.TTY: drawUi :: UiState -> [UiWidget]
- EVM.TTY: drawVm :: UiVmState -> [UiWidget]
- EVM.TTY: drawVmBrowser :: UiBrowserState -> [UiWidget]
- EVM.TTY: ifTallEnough :: Int -> Widget n -> Widget n -> Widget n
- EVM.TTY: initUiVmState :: VM -> UnitTestOptions -> Stepper () -> UiVmState
- EVM.TTY: initialUiVmStateForTest :: UnitTestOptions -> (Text, Text) -> UiVmState
- EVM.TTY: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Brick.Widgets.List.List EVM.TTY.Name (Data.Text.Internal.Text, Data.Text.Internal.Text), b GHC.Types.~ Brick.Widgets.List.List EVM.TTY.Name (Data.Text.Internal.Text, Data.Text.Internal.Text)) => Optics.Label.LabelOptic "tests" k EVM.TTY.UiTestPickerState EVM.TTY.UiTestPickerState a b
- EVM.TTY: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Brick.Widgets.List.List EVM.TTY.Name (EVM.Types.Addr, EVM.Types.Contract), b GHC.Types.~ Brick.Widgets.List.List EVM.TTY.Name (EVM.Types.Addr, EVM.Types.Contract)) => Optics.Label.LabelOptic "contracts" k EVM.TTY.UiBrowserState EVM.TTY.UiBrowserState a b
- EVM.TTY: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Map.Internal.Map GHC.Types.Int (EVM.Types.VM, EVM.Stepper.Stepper ()), b GHC.Types.~ Data.Map.Internal.Map GHC.Types.Int (EVM.Types.VM, EVM.Stepper.Stepper ())) => Optics.Label.LabelOptic "snapshots" k EVM.TTY.UiVmState EVM.TTY.UiVmState a b
- EVM.TTY: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Dapp.DappInfo, b GHC.Types.~ EVM.Dapp.DappInfo) => Optics.Label.LabelOptic "dapp" k EVM.TTY.UiTestPickerState EVM.TTY.UiTestPickerState a b
- EVM.TTY: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Stepper.Stepper (), b GHC.Types.~ EVM.Stepper.Stepper ()) => Optics.Label.LabelOptic "stepper" k EVM.TTY.UiVmState EVM.TTY.UiVmState a b
- EVM.TTY: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.TTY.UiVmState, b GHC.Types.~ EVM.TTY.UiVmState) => Optics.Label.LabelOptic "vm" k EVM.TTY.UiBrowserState EVM.TTY.UiBrowserState a b
- EVM.TTY: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.VM, b GHC.Types.~ EVM.Types.VM) => Optics.Label.LabelOptic "vm" k EVM.TTY.UiVmState EVM.TTY.UiVmState a b
- EVM.TTY: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.UnitTest.UnitTestOptions, b GHC.Types.~ EVM.UnitTest.UnitTestOptions) => Optics.Label.LabelOptic "opts" k EVM.TTY.UiTestPickerState EVM.TTY.UiTestPickerState a b
- EVM.TTY: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.UnitTest.UnitTestOptions, b GHC.Types.~ EVM.UnitTest.UnitTestOptions) => Optics.Label.LabelOptic "testOpts" k EVM.TTY.UiVmState EVM.TTY.UiVmState a b
- EVM.TTY: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Types.Bool, b GHC.Types.~ GHC.Types.Bool) => Optics.Label.LabelOptic "showMemory" k EVM.TTY.UiVmState EVM.TTY.UiVmState a b
- EVM.TTY: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Types.Int, b GHC.Types.~ GHC.Types.Int) => Optics.Label.LabelOptic "step" k EVM.TTY.UiVmState EVM.TTY.UiVmState a b
- EVM.TTY: instance GHC.Classes.Eq EVM.TTY.Name
- EVM.TTY: instance GHC.Classes.Ord EVM.TTY.Name
- EVM.TTY: instance GHC.Show.Show EVM.TTY.Name
- EVM.TTY: interpret :: (?fetcher :: Fetcher, ?maxIter :: Maybe Integer) => StepMode -> Stepper a -> StateT UiVmState IO (Continuation a)
- EVM.TTY: isExecutionHalted :: UiVmState -> Pred VM
- EVM.TTY: isFuzzTest :: (Test, [AbiType]) -> Bool
- EVM.TTY: isNewTraceAdded :: UiVmState -> Pred VM
- EVM.TTY: isNextSourcePosition :: UiVmState -> Pred VM
- EVM.TTY: isNextSourcePositionWithoutEntering :: UiVmState -> Pred VM
- EVM.TTY: isUnitTestContract :: Text -> DappInfo -> Bool
- EVM.TTY: keepExecuting :: (?fetcher :: Fetcher, ?maxIter :: Maybe Integer) => StepMode -> Stepper a -> StateT UiVmState IO (Continuation a)
- EVM.TTY: main :: UnitTestOptions -> FilePath -> Maybe BuildOutput -> IO ()
- EVM.TTY: message :: VM -> String
- EVM.TTY: mkVty :: IO Vty
- EVM.TTY: myTheme :: [(AttrName, Attr)]
- EVM.TTY: opWidget :: (Integral a, Show a) => (a, Op) -> Widget n
- EVM.TTY: ourWrap :: String -> Widget n
- EVM.TTY: prettyIfConcrete :: Expr Buf -> String
- EVM.TTY: runFromVM :: SolverGroup -> RpcInfo -> Maybe Integer -> DappInfo -> VM -> IO VM
- EVM.TTY: selectedAttr :: AttrName
- EVM.TTY: snapshotInterval :: Int
- EVM.TTY: solidityList :: VM -> DappInfo -> List Name (Int, ByteString)
- EVM.TTY: stepOneOpcode :: Stepper a -> StateT UiVmState IO ()
- EVM.TTY: takeStep :: (?fetcher :: Fetcher, ?maxIter :: Maybe Integer) => UiVmState -> StepMode -> EventM n UiState ()
- EVM.TTY: type Pred a = a -> Bool
- EVM.TTY: type UiWidget = Widget Name
- EVM.TTY: withHighlight :: Bool -> Widget n -> Widget n
- EVM.TTY: wordAttr :: AttrName
- EVM.TTYCenteredList: drawListElements :: (Ord n, Show n) => Bool -> List n e -> (Bool -> e -> Widget n) -> Widget n
- EVM.TTYCenteredList: renderList :: (Ord n, Show n) => (Bool -> e -> Widget n) -> Bool -> List n e -> Widget n
- EVM.Traversals: foldTrace :: forall b. Monoid b => (forall a. Expr a -> b) -> b -> Trace -> b
- EVM.Traversals: foldTraces :: forall b. Monoid b => (forall a. Expr a -> b) -> b -> Traces -> b
- EVM.Traversals: mapTrace :: (forall a. Expr a -> Expr a) -> Trace -> Trace
- EVM.Traversals: mapTraceM :: forall m. Monad m => (forall a. Expr a -> m (Expr a)) -> Trace -> m Trace
- EVM.Traversals: mapTracesM :: forall m. Monad m => (forall a. Expr a -> m (Expr a)) -> Traces -> m Traces
- EVM.Types: ConcreteS :: StorageModel
- EVM.Types: InitialS :: StorageModel
- EVM.Types: SymbolicS :: StorageModel
- EVM.Types: [$sel:allowFFI:VM] :: VM -> Bool
- EVM.Types: [$sel:contractcode:Contract] :: Contract -> ContractCode
- EVM.Types: [$sel:fetchedContracts:Cache] :: Cache -> Map Addr Contract
- EVM.Types: [$sel:fetchedStorage:Cache] :: Cache -> Map W256 (Map W256 W256)
- EVM.Types: [$sel:initialStorage:VMOpts] :: VMOpts -> Expr Storage
- EVM.Types: [$sel:origStorage:Env] :: Env -> Map W256 (Map W256 W256)
- EVM.Types: [$sel:overrideCaller:VM] :: VM -> Maybe Addr
- EVM.Types: [$sel:storage:Env] :: Env -> Expr Storage
- EVM.Types: [Address] :: Int -> Expr EWord
- EVM.Types: [CallCode] :: Expr EWord -> Expr EWord -> Expr EWord -> Expr EWord -> Expr EWord -> Expr EWord -> Expr EWord -> [Expr Log] -> Expr Storage -> Expr EWord
- EVM.Types: [CallValue] :: Int -> Expr EWord
- EVM.Types: [Call] :: Expr EWord -> Maybe (Expr EWord) -> Expr EWord -> Expr EWord -> Expr EWord -> Expr EWord -> Expr EWord -> [Expr Log] -> Expr Storage -> Expr EWord
- EVM.Types: [Caller] :: Int -> Expr EWord
- EVM.Types: [Create2] :: Expr EWord -> Expr EWord -> Expr EWord -> Expr EWord -> Expr Buf -> [Expr Log] -> Expr Storage -> Expr EWord
- EVM.Types: [Create] :: Expr EWord -> Expr EWord -> Expr EWord -> Expr Buf -> [Expr Log] -> Expr Storage -> Expr EWord
- EVM.Types: [DelegeateCall] :: Expr EWord -> Expr EWord -> Expr EWord -> Expr EWord -> Expr EWord -> Expr EWord -> Expr EWord -> [Expr Log] -> Expr Storage -> Expr EWord
- EVM.Types: [EmptyStore] :: Expr Storage
- EVM.Types: [ExtCodeHash] :: Expr EWord -> Expr EWord
- EVM.Types: [SelfBalance] :: Int -> Int -> Expr EWord
- EVM.Types: data StorageModel
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Map.Internal.Map EVM.Types.Addr EVM.Types.Contract, b GHC.Types.~ Data.Map.Internal.Map EVM.Types.Addr EVM.Types.Contract) => Optics.Label.LabelOptic "contracts" k EVM.Types.Env EVM.Types.Env a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Map.Internal.Map EVM.Types.Addr EVM.Types.Contract, b GHC.Types.~ Data.Map.Internal.Map EVM.Types.Addr EVM.Types.Contract) => Optics.Label.LabelOptic "fetchedContracts" k EVM.Types.Cache EVM.Types.Cache a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Map.Internal.Map EVM.Types.Addr EVM.Types.Contract, b GHC.Types.~ Data.Map.Internal.Map EVM.Types.Addr EVM.Types.Contract) => Optics.Label.LabelOptic "txReversion" k EVM.Types.TxState EVM.Types.TxState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Map.Internal.Map EVM.Types.Addr [EVM.Types.W256], b GHC.Types.~ Data.Map.Internal.Map EVM.Types.Addr [EVM.Types.W256]) => Optics.Label.LabelOptic "txAccessList" k EVM.Types.VMOpts EVM.Types.VMOpts a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Map.Internal.Map EVM.Types.CodeLocation (GHC.Types.Int, [EVM.Types.Expr 'EVM.Types.EWord]), b GHC.Types.~ Data.Map.Internal.Map EVM.Types.CodeLocation (GHC.Types.Int, [EVM.Types.Expr 'EVM.Types.EWord])) => Optics.Label.LabelOptic "iterations" k EVM.Types.VM EVM.Types.VM a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Map.Internal.Map EVM.Types.W256 (Data.Map.Internal.Map EVM.Types.W256 EVM.Types.W256), b GHC.Types.~ Data.Map.Internal.Map EVM.Types.W256 (Data.Map.Internal.Map EVM.Types.W256 EVM.Types.W256)) => Optics.Label.LabelOptic "fetchedStorage" k EVM.Types.Cache EVM.Types.Cache a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Map.Internal.Map EVM.Types.W256 (Data.Map.Internal.Map EVM.Types.W256 EVM.Types.W256), b GHC.Types.~ Data.Map.Internal.Map EVM.Types.W256 (Data.Map.Internal.Map EVM.Types.W256 EVM.Types.W256)) => Optics.Label.LabelOptic "origStorage" k EVM.Types.Env EVM.Types.Env a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Set.Internal.Set (EVM.Types.Addr, EVM.Types.W256), b GHC.Types.~ Data.Set.Internal.Set (EVM.Types.Addr, EVM.Types.W256)) => Optics.Label.LabelOptic "accessedStorageKeys" k EVM.Types.SubState EVM.Types.SubState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Set.Internal.Set EVM.Types.Addr, b GHC.Types.~ Data.Set.Internal.Set EVM.Types.Addr) => Optics.Label.LabelOptic "accessedAddresses" k EVM.Types.SubState EVM.Types.SubState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Tree.Zipper.TreePos Data.Tree.Zipper.Empty EVM.Types.Trace, b GHC.Types.~ Data.Tree.Zipper.TreePos Data.Tree.Zipper.Empty EVM.Types.Trace) => Optics.Label.LabelOptic "traces" k EVM.Types.VM EVM.Types.VM a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Addr, b GHC.Types.~ EVM.Types.Addr) => Optics.Label.LabelOptic "address" k EVM.Types.VMOpts EVM.Types.VMOpts a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Addr, b GHC.Types.~ EVM.Types.Addr) => Optics.Label.LabelOptic "codeContract" k EVM.Types.FrameState EVM.Types.FrameState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Addr, b GHC.Types.~ EVM.Types.Addr) => Optics.Label.LabelOptic "coinbase" k EVM.Types.Block EVM.Types.Block a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Addr, b GHC.Types.~ EVM.Types.Addr) => Optics.Label.LabelOptic "coinbase" k EVM.Types.VMOpts EVM.Types.VMOpts a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Addr, b GHC.Types.~ EVM.Types.Addr) => Optics.Label.LabelOptic "contract" k EVM.Types.FrameState EVM.Types.FrameState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Addr, b GHC.Types.~ EVM.Types.Addr) => Optics.Label.LabelOptic "origin" k EVM.Types.TxState EVM.Types.TxState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Addr, b GHC.Types.~ EVM.Types.Addr) => Optics.Label.LabelOptic "origin" k EVM.Types.VMOpts EVM.Types.VMOpts a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Addr, b GHC.Types.~ EVM.Types.Addr) => Optics.Label.LabelOptic "toAddr" k EVM.Types.TxState EVM.Types.TxState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Block, b GHC.Types.~ EVM.Types.Block) => Optics.Label.LabelOptic "block" k EVM.Types.VM EVM.Types.VM a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Cache, b GHC.Types.~ EVM.Types.Cache) => Optics.Label.LabelOptic "cache" k EVM.Types.VM EVM.Types.VM a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.ContractCode, b GHC.Types.~ EVM.Types.ContractCode) => Optics.Label.LabelOptic "code" k EVM.Types.FrameState EVM.Types.FrameState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.ContractCode, b GHC.Types.~ EVM.Types.ContractCode) => Optics.Label.LabelOptic "contractcode" k EVM.Types.Contract EVM.Types.Contract a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Env, b GHC.Types.~ EVM.Types.Env) => Optics.Label.LabelOptic "env" k EVM.Types.VM EVM.Types.VM a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.Buf, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.Buf) => Optics.Label.LabelOptic "calldata" k EVM.Types.FrameState EVM.Types.FrameState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.Buf, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.Buf) => Optics.Label.LabelOptic "memory" k EVM.Types.FrameState EVM.Types.FrameState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.Buf, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.Buf) => Optics.Label.LabelOptic "returndata" k EVM.Types.FrameState EVM.Types.FrameState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EWord, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EWord) => Optics.Label.LabelOptic "caller" k EVM.Types.FrameState EVM.Types.FrameState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EWord, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EWord) => Optics.Label.LabelOptic "caller" k EVM.Types.VMOpts EVM.Types.VMOpts a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EWord, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EWord) => Optics.Label.LabelOptic "callvalue" k EVM.Types.FrameState EVM.Types.FrameState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.Storage, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.Storage) => Optics.Label.LabelOptic "initialStorage" k EVM.Types.VMOpts EVM.Types.VMOpts a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.Storage, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.Storage) => Optics.Label.LabelOptic "storage" k EVM.Types.Env EVM.Types.Env a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.FrameContext, b GHC.Types.~ EVM.Types.FrameContext) => Optics.Label.LabelOptic "context" k EVM.Types.Frame EVM.Types.Frame a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.FrameState, b GHC.Types.~ EVM.Types.FrameState) => Optics.Label.LabelOptic "state" k EVM.Types.Frame EVM.Types.Frame a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.FrameState, b GHC.Types.~ EVM.Types.FrameState) => Optics.Label.LabelOptic "state" k EVM.Types.VM EVM.Types.VM a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.TxState, b GHC.Types.~ EVM.Types.TxState) => Optics.Label.LabelOptic "tx" k EVM.Types.VM EVM.Types.VM 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 "balance" k EVM.Types.Contract EVM.Types.Contract 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 "nonce" k EVM.Types.Contract EVM.Types.Contract a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Maybe.Maybe EVM.Types.Addr, b GHC.Types.~ GHC.Maybe.Maybe EVM.Types.Addr) => Optics.Label.LabelOptic "overrideCaller" k EVM.Types.VM EVM.Types.VM a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Maybe.Maybe EVM.Types.VMResult, b GHC.Types.~ GHC.Maybe.Maybe EVM.Types.VMResult) => Optics.Label.LabelOptic "result" k EVM.Types.VM EVM.Types.VM a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Types.Bool, b GHC.Types.~ GHC.Types.Bool) => Optics.Label.LabelOptic "allowFFI" k EVM.Types.VM EVM.Types.VM a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Types.Bool, b GHC.Types.~ GHC.Types.Bool) => Optics.Label.LabelOptic "static" k EVM.Types.FrameState EVM.Types.FrameState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Types.Int, b GHC.Types.~ GHC.Types.Int) => Optics.Label.LabelOptic "pc" k EVM.Types.FrameState EVM.Types.FrameState 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 "burned" k EVM.Types.VM EVM.Types.VM 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 "gas" k EVM.Types.FrameState EVM.Types.FrameState 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 "memorySize" k EVM.Types.FrameState EVM.Types.FrameState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ [(EVM.Types.Addr, GHC.Word.Word64)], b GHC.Types.~ [(EVM.Types.Addr, GHC.Word.Word64)]) => Optics.Label.LabelOptic "refunds" k EVM.Types.SubState EVM.Types.SubState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ [EVM.Types.Addr], b GHC.Types.~ [EVM.Types.Addr]) => Optics.Label.LabelOptic "selfdestructs" k EVM.Types.SubState EVM.Types.SubState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ [EVM.Types.Addr], b GHC.Types.~ [EVM.Types.Addr]) => Optics.Label.LabelOptic "touchedAccounts" k EVM.Types.SubState EVM.Types.SubState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ [EVM.Types.Expr 'EVM.Types.EWord], b GHC.Types.~ [EVM.Types.Expr 'EVM.Types.EWord]) => Optics.Label.LabelOptic "stack" k EVM.Types.FrameState EVM.Types.FrameState a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ [EVM.Types.Expr 'EVM.Types.Log], b GHC.Types.~ [EVM.Types.Expr 'EVM.Types.Log]) => Optics.Label.LabelOptic "logs" k EVM.Types.VM EVM.Types.VM a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ [EVM.Types.Frame], b GHC.Types.~ [EVM.Types.Frame]) => Optics.Label.LabelOptic "frames" k EVM.Types.VM EVM.Types.VM a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ [EVM.Types.Prop], b GHC.Types.~ [EVM.Types.Prop]) => Optics.Label.LabelOptic "constraints" k EVM.Types.VM EVM.Types.VM a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ [EVM.Types.Prop], b GHC.Types.~ [EVM.Types.Prop]) => Optics.Label.LabelOptic "keccakEqs" k EVM.Types.VM EVM.Types.VM a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.An_AffineTraversal, a GHC.Types.~ (Data.Map.Internal.Map EVM.Types.Addr EVM.Types.Contract, EVM.Types.Expr 'EVM.Types.Storage), b GHC.Types.~ (Data.Map.Internal.Map EVM.Types.Addr EVM.Types.Contract, EVM.Types.Expr 'EVM.Types.Storage)) => Optics.Label.LabelOptic "callreversion" k EVM.Types.FrameContext EVM.Types.FrameContext a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.An_AffineTraversal, a GHC.Types.~ Data.Map.Internal.Map EVM.Types.Addr EVM.Types.Contract, b GHC.Types.~ Data.Map.Internal.Map EVM.Types.Addr EVM.Types.Contract) => Optics.Label.LabelOptic "createreversion" k EVM.Types.FrameContext EVM.Types.FrameContext a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.An_AffineTraversal, a GHC.Types.~ EVM.Types.Addr, b GHC.Types.~ EVM.Types.Addr) => Optics.Label.LabelOptic "address" k EVM.Types.FrameContext EVM.Types.FrameContext a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.An_AffineTraversal, a GHC.Types.~ EVM.Types.Addr, b GHC.Types.~ EVM.Types.Addr) => Optics.Label.LabelOptic "context" k EVM.Types.FrameContext EVM.Types.FrameContext a b
- EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.An_AffineTraversal, a GHC.Types.~ EVM.Types.Addr, b GHC.Types.~ EVM.Types.Addr) => Optics.Label.LabelOptic "target" k EVM.Types.FrameContext EVM.Types.FrameContext a b
- EVM.Types: instance GHC.Generics.Generic EVM.Types.FrameState
- EVM.Types: instance GHC.Generics.Generic EVM.Types.VM
- EVM.Types: instance GHC.Read.Read EVM.Types.StorageModel
- EVM.Types: instance GHC.Show.Show EVM.Types.Choose
- EVM.Types: instance GHC.Show.Show EVM.Types.Effect
- EVM.Types: instance GHC.Show.Show EVM.Types.Frame
- EVM.Types: instance GHC.Show.Show EVM.Types.FrameState
- EVM.Types: instance GHC.Show.Show EVM.Types.Query
- EVM.Types: instance GHC.Show.Show EVM.Types.StorageModel
- EVM.Types: instance GHC.Show.Show EVM.Types.VM
- EVM.Types: instance GHC.Show.Show EVM.Types.VMResult
- EVM.Types: instance Options.Generic.ParseField EVM.Types.StorageModel
- EVM.Types: unifyCachedStorage :: Map W256 W256 -> Map W256 W256 -> Map W256 W256
- EVM.UnitTest: OpLocation :: Contract -> Int -> OpLocation
- EVM.UnitTest: [$sel:covMatch:UnitTestOptions] :: UnitTestOptions -> Maybe Text
- EVM.UnitTest: [$sel:fuzzRuns:UnitTestOptions] :: UnitTestOptions -> Int
- EVM.UnitTest: [$sel:maxDepth:UnitTestOptions] :: UnitTestOptions -> Maybe Int
- EVM.UnitTest: [$sel:replay:UnitTestOptions] :: UnitTestOptions -> Maybe (Text, ByteString)
- EVM.UnitTest: [$sel:srcContract:OpLocation] :: OpLocation -> Contract
- EVM.UnitTest: [$sel:srcOpIx:OpLocation] :: OpLocation -> Int
- EVM.UnitTest: [$sel:vmModifier:UnitTestOptions] :: UnitTestOptions -> VM -> VM
- EVM.UnitTest: checkFailures :: UnitTestOptions -> ABIMethod -> Bool -> Stepper Bool
- EVM.UnitTest: coverageForUnitTestContract :: UnitTestOptions -> Map Text SolcContract -> SourceCache -> (Text, [(Test, [AbiType])]) -> IO (MultiSet SrcMap)
- EVM.UnitTest: coverageReport :: DappInfo -> MultiSet SrcMap -> Map FilePath (Vector (Int, ByteString))
- EVM.UnitTest: currentOpLocation :: VM -> OpLocation
- EVM.UnitTest: data OpLocation
- EVM.UnitTest: decodeCalls :: ByteString -> [ExploreTx]
- EVM.UnitTest: execTest :: UnitTestOptions -> VM -> ABIMethod -> AbiValue -> IO (Bool, VM)
- EVM.UnitTest: execTestStepper :: UnitTestOptions -> ABIMethod -> AbiValue -> Stepper Bool
- EVM.UnitTest: execWithCoverage :: StateT CoverageState IO VMResult
- EVM.UnitTest: explorationStepper :: UnitTestOptions -> ABIMethod -> [ExploreTx] -> [Addr] -> RLP -> Int -> Stepper (Bool, RLP)
- EVM.UnitTest: exploreRun :: UnitTestOptions -> VM -> ABIMethod -> [ExploreTx] -> IO (Text, Either Text Text, VM)
- EVM.UnitTest: exploreStep :: UnitTestOptions -> ByteString -> Stepper Bool
- EVM.UnitTest: fuzzRun :: UnitTestOptions -> VM -> Text -> [AbiType] -> IO (Text, Either Text Text, VM)
- EVM.UnitTest: fuzzTest :: UnitTestOptions -> Text -> [AbiType] -> VM -> Property
- EVM.UnitTest: getParametersFromEnvironmentVariables :: Maybe Text -> IO TestVMParams
- EVM.UnitTest: getTargetContracts :: UnitTestOptions -> Stepper [Addr]
- EVM.UnitTest: initialExplorationStepper :: UnitTestOptions -> ABIMethod -> [ExploreTx] -> [Addr] -> Int -> Stepper (Bool, RLP)
- EVM.UnitTest: instance GHC.Classes.Eq EVM.UnitTest.OpLocation
- EVM.UnitTest: instance GHC.Classes.Ord EVM.UnitTest.OpLocation
- EVM.UnitTest: instance GHC.Show.Show EVM.UnitTest.OpLocation
- EVM.UnitTest: interpretWithCoverage :: UnitTestOptions -> Stepper a -> StateT CoverageState IO a
- EVM.UnitTest: prettyCalldata :: (?context :: DappContext) => SMTCex -> Expr Buf -> Text -> [AbiType] -> Text
- EVM.UnitTest: runOne :: UnitTestOptions -> VM -> ABIMethod -> AbiValue -> IO (Text, Either Text Text, VM)
- EVM.UnitTest: runTest :: UnitTestOptions -> VM -> (Test, [AbiType]) -> IO (Text, Either Text Text, VM)
- EVM.UnitTest: runUnitTest :: UnitTestOptions -> ABIMethod -> AbiValue -> Stepper Bool
- EVM.UnitTest: runWithCoverage :: StateT CoverageState IO VM
- EVM.UnitTest: showCalldata :: (?context :: DappContext) => SMTCex -> [AbiType] -> Expr Buf -> Text
- EVM.UnitTest: showVal :: AbiValue -> Text
- EVM.UnitTest: srcMapForOpLocation :: DappInfo -> OpLocation -> Maybe SrcMap
- EVM.UnitTest: type CoverageState = (VM, MultiSet OpLocation)
- EVM.UnitTest: type ExploreTx = (Addr, Addr, ByteString, W256)
+ EVM: abstractContract :: ContractCode -> Expr EAddr -> Contract
+ EVM: create2Address :: Expr EAddr -> W256 -> ByteString -> EVM s (Expr EAddr)
+ EVM: createAddress :: Expr EAddr -> Maybe W64 -> EVM s (Expr EAddr)
+ EVM: emptyContract :: Contract
+ EVM: forceAddr :: Expr EWord -> String -> (Expr EAddr -> EVM s ()) -> EVM s ()
+ EVM: forceConcreteAddr :: Expr EAddr -> String -> (Addr -> EVM s ()) -> EVM s ()
+ EVM: forceConcreteAddr2 :: (Expr EAddr, Expr EAddr) -> String -> ((Addr, Addr) -> EVM s ()) -> EVM s ()
+ EVM: freezeMemory :: MutableMemory s -> EVM s (Expr Buf)
+ EVM: freshSymAddr :: EVM s (Expr EAddr)
+ EVM: isCreation :: ContractCode -> Bool
+ EVM: isPrecompileAddr :: Expr EAddr -> Bool
+ EVM: noJumpIntoInitData :: Int -> EVM s () -> EVM s ()
+ EVM: parseInitCode :: Expr Buf -> Maybe ContractCode
+ EVM: pushAddr :: Expr EAddr -> EVM s ()
+ EVM: unknownContract :: Expr EAddr -> Contract
+ EVM: writeMemory :: MutableMemory s -> Int -> ByteString -> EVM s ()
+ EVM.ABI: Sig :: Text -> [AbiType] -> Sig
+ EVM.ABI: data Sig
+ EVM.ABI: instance GHC.Classes.Eq EVM.ABI.Sig
+ EVM.ABI: instance GHC.Show.Show EVM.ABI.Sig
+ EVM.Dapp: mkSig :: Method -> Maybe Sig
+ EVM.Dapp: srcMapCode :: SourceCache -> SrcMap -> Maybe ByteString
+ EVM.Dapp: srcMapCodePos :: SourceCache -> SrcMap -> Maybe (FilePath, Int)
+ EVM.Expr: ConstState :: Map (Expr EWord) W256 -> Bool -> ConstState
+ EVM.Expr: [$sel:canBeSat:ConstState] :: ConstState -> Bool
+ EVM.Expr: [$sel:values:ConstState] :: ConstState -> Map (Expr EWord) W256
+ EVM.Expr: concretePrefix :: Expr Buf -> Vector Word8
+ EVM.Expr: constFoldProp :: [Prop] -> Bool
+ EVM.Expr: data ConstState
+ EVM.Expr: flattenProps :: [Prop] -> [Prop]
+ EVM.Expr: getAddr :: Expr Storage -> Maybe (Expr EAddr)
+ EVM.Expr: idsDontMatch :: ByteString -> ByteString -> Bool
+ EVM.Expr: instance GHC.Show.Show EVM.Expr.ConstState
+ EVM.Expr: isFailure :: Expr End -> Bool
+ EVM.Expr: isPartial :: Expr End -> Bool
+ EVM.Expr: isSuccess :: Expr End -> Bool
+ EVM.Expr: litToArrayPreimage :: W256 -> Maybe (Word8, W256)
+ EVM.Expr: maxLit :: W256
+ EVM.Expr: pattern ArraySlotWithOffset :: ByteString -> Expr EWord -> Expr EWord
+ EVM.Expr: pattern ArraySlotZero :: ByteString -> Expr EWord
+ EVM.Expr: pattern Keccak64Bytes :: Expr EWord
+ EVM.Expr: pattern MappingSlot :: ByteString -> Expr EWord -> Expr EWord
+ EVM.Expr: preImages :: [(W256, Word8)]
+ EVM.Expr: remRedundantProps :: [Prop] -> [Prop]
+ EVM.Expr: simplifyProp :: Prop -> Prop
+ EVM.Expr: simplifyProps :: [Prop] -> [Prop]
+ EVM.Expr: slotPos :: Word8 -> ByteString
+ EVM.Expr: structureArraySlots :: Expr a -> Expr a
+ EVM.Expr: wordToAddr :: Expr EWord -> Maybe (Expr EAddr)
+ EVM.FeeSchedule: feeSchedule :: Num n => FeeSchedule n
+ EVM.Format: showVal :: AbiValue -> Text
+ EVM.Keccak: keccakCompute :: [Prop] -> [Expr Buf] -> [Expr Storage] -> [Prop]
+ EVM.SMT: AbstState :: Map (Expr EWord) Int -> Int -> AbstState
+ EVM.SMT: RefinementEqs :: [Builder] -> RefinementEqs
+ EVM.SMT: [$sel:addrs:CexVars] :: CexVars -> [Text]
+ EVM.SMT: [$sel:addrs:SMTCex] :: SMTCex -> Map (Expr EAddr) Addr
+ EVM.SMT: [$sel:count:AbstState] :: AbstState -> Int
+ EVM.SMT: [$sel:words:AbstState] :: AbstState -> Map (Expr EWord) Int
+ EVM.SMT: abstractAwayProps :: AbstRefineConfig -> [Prop] -> ([Prop], AbstState)
+ EVM.SMT: assertPropsNoSimp :: AbstRefineConfig -> [Prop] -> SMT2
+ EVM.SMT: data AbstState
+ EVM.SMT: declareAbstractStores :: [Builder] -> SMT2
+ EVM.SMT: declareAddrs :: [Builder] -> SMT2
+ EVM.SMT: formatEAddr :: Expr EAddr -> Builder
+ EVM.SMT: getAddrs :: (Text -> Expr EAddr) -> (Text -> IO Text) -> [Text] -> IO (Map (Expr EAddr) Addr)
+ EVM.SMT: getOne :: (SpecConstant -> a) -> (Text -> IO Text) -> Map Text a -> Text -> IO (Map Text a)
+ EVM.SMT: instance GHC.Base.Monoid EVM.SMT.RefinementEqs
+ EVM.SMT: instance GHC.Base.Semigroup EVM.SMT.RefinementEqs
+ EVM.SMT: instance GHC.Classes.Eq EVM.SMT.RefinementEqs
+ EVM.SMT: instance GHC.Show.Show EVM.SMT.AbstState
+ EVM.SMT: instance GHC.Show.Show EVM.SMT.RefinementEqs
+ EVM.SMT: newtype RefinementEqs
+ EVM.SMT: parseAddr :: SpecConstant -> Addr
+ EVM.SMT: parseEAddr :: Text -> Expr EAddr
+ EVM.SMT: parseTxCtx :: Text -> Expr EWord
+ EVM.SMT: referencedAbstractStores :: TraversableTerm a => a -> Set Builder
+ EVM.SMT: referencedWAddrs :: TraversableTerm a => a -> Set Builder
+ EVM.SMT: smt2Line :: Builder -> SMT2
+ EVM.SMT: storeName :: Expr EAddr -> Builder
+ EVM.Solvers: checkCommand :: SolverInstance -> Text -> IO ()
+ EVM.SymExec: [$sel:abstRefineConfig:VeriOpts] :: VeriOpts -> AbstRefineConfig
+ EVM.SymExec: debugAbstVeriOpts :: VeriOpts
+ EVM.SymExec: defaultSymbolicValues :: Expr a -> Expr a
+ EVM.SymExec: expandCex :: VM s -> SMTCex -> SMTCex
+ EVM.SymExec: getCex :: ProofResult a b c -> Maybe b
+ EVM.SymExec: getExpr :: SolverGroup -> ByteString -> Maybe Sig -> [String] -> VeriOpts -> IO (Expr End)
+ EVM.SymExec: getTimeout :: ProofResult a b c -> Maybe c
+ EVM.SymExec: prettyCalldata :: SMTCex -> Expr Buf -> Text -> [AbiType] -> Text
+ EVM.SymExec: subAddrs :: Map (Expr EAddr) Addr -> Expr a -> Expr a
+ EVM.SymExec: subBufs :: Map (Expr Buf) ByteString -> Expr a -> Expr a
+ EVM.SymExec: subStores :: Map (Expr EAddr) (Map W256 W256) -> Expr a -> Expr a
+ EVM.SymExec: subVars :: Map (Expr EWord) W256 -> Expr a -> Expr a
+ EVM.SymExec: toEContract :: Contract -> Expr EContract
+ EVM.Traversals: foldCode :: forall b. Monoid b => (forall a. Expr a -> b) -> ContractCode -> b
+ EVM.Traversals: foldContract :: forall b. Monoid b => (forall a. Expr a -> b) -> b -> Contract -> b
+ EVM.Traversals: foldEContract :: forall b. Monoid b => (forall a. Expr a -> b) -> b -> Expr EContract -> b
+ EVM.Traversals: mapCodeM :: Monad m => (forall a. Expr a -> m (Expr a)) -> ContractCode -> m ContractCode
+ EVM.Traversals: mapContractM :: Monad m => (forall a. Expr a -> m (Expr a)) -> Contract -> m Contract
+ EVM.Traversals: mapEContractM :: Monad m => (forall a. Expr a -> m (Expr a)) -> Expr EContract -> m (Expr EContract)
+ EVM.Traversals: mapProp' :: (Prop -> Prop) -> Prop -> Prop
+ EVM.Types: AbstRefineConfig :: Bool -> Bool -> AbstRefineConfig
+ EVM.Types: AbstractBase :: BaseState
+ EVM.Types: ConcreteMemory :: MutableMemory s -> Memory s
+ EVM.Types: EAddr :: EType
+ EVM.Types: EContract :: EType
+ EVM.Types: EmptyBase :: BaseState
+ EVM.Types: JumpIntoSymbolicCode :: Int -> Int -> PartialExec
+ EVM.Types: RuntimeConfig :: Bool -> Maybe (Expr EAddr) -> BaseState -> RuntimeConfig
+ EVM.Types: SymbolicMemory :: !Expr Buf -> Memory s
+ EVM.Types: UnknownCode :: Expr EAddr -> ContractCode
+ EVM.Types: [$sel:allowFFI:RuntimeConfig] :: RuntimeConfig -> Bool
+ EVM.Types: [$sel:arith:AbstRefineConfig] :: AbstRefineConfig -> Bool
+ EVM.Types: [$sel:baseState:RuntimeConfig] :: RuntimeConfig -> BaseState
+ EVM.Types: [$sel:baseState:VMOpts] :: VMOpts -> BaseState
+ EVM.Types: [$sel:code:Contract] :: Contract -> ContractCode
+ EVM.Types: [$sel:config:VM] :: VM s -> RuntimeConfig
+ EVM.Types: [$sel:fetched:Cache] :: Cache -> Map Addr Contract
+ EVM.Types: [$sel:freshAddresses:Env] :: Env -> Int
+ EVM.Types: [$sel:jumpDst:UnexpectedSymbolicArg] :: PartialExec -> Int
+ EVM.Types: [$sel:mem:AbstRefineConfig] :: AbstRefineConfig -> Bool
+ EVM.Types: [$sel:origStorage:Contract] :: Contract -> Expr Storage
+ EVM.Types: [$sel:otherContracts:VMOpts] :: VMOpts -> [(Expr EAddr, Contract)]
+ EVM.Types: [$sel:overrideCaller:RuntimeConfig] :: RuntimeConfig -> Maybe (Expr EAddr)
+ EVM.Types: [$sel:storage:Contract] :: Contract -> Expr Storage
+ EVM.Types: [C] :: ContractCode -> Expr Storage -> Expr EWord -> Maybe W64 -> Expr EContract
+ EVM.Types: [CodeHash] :: Expr EAddr -> Expr EWord
+ EVM.Types: [LitAddr] :: Addr -> Expr EAddr
+ EVM.Types: [SymAddr] :: Text -> Expr EAddr
+ EVM.Types: [TxValue] :: Expr EWord
+ EVM.Types: [WAddr] :: Expr EAddr -> Expr EWord
+ EVM.Types: abstRefineDefault :: AbstRefineConfig
+ EVM.Types: data AbstRefineConfig
+ EVM.Types: data BaseState
+ EVM.Types: data Memory s
+ EVM.Types: data RuntimeConfig
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Map.Internal.Map (EVM.Types.Expr 'EVM.Types.EAddr) EVM.Types.Contract, b GHC.Types.~ Data.Map.Internal.Map (EVM.Types.Expr 'EVM.Types.EAddr) EVM.Types.Contract) => Optics.Label.LabelOptic "contracts" k EVM.Types.Env EVM.Types.Env a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Map.Internal.Map (EVM.Types.Expr 'EVM.Types.EAddr) EVM.Types.Contract, b GHC.Types.~ Data.Map.Internal.Map (EVM.Types.Expr 'EVM.Types.EAddr) EVM.Types.Contract) => Optics.Label.LabelOptic "txReversion" k EVM.Types.TxState EVM.Types.TxState a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Map.Internal.Map (EVM.Types.Expr 'EVM.Types.EAddr) [EVM.Types.W256], b GHC.Types.~ Data.Map.Internal.Map (EVM.Types.Expr 'EVM.Types.EAddr) [EVM.Types.W256]) => Optics.Label.LabelOptic "txAccessList" k EVM.Types.VMOpts EVM.Types.VMOpts a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Map.Internal.Map EVM.Types.Addr EVM.Types.Contract, b GHC.Types.~ Data.Map.Internal.Map EVM.Types.Addr EVM.Types.Contract) => Optics.Label.LabelOptic "fetched" k EVM.Types.Cache EVM.Types.Cache a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Map.Internal.Map EVM.Types.CodeLocation (GHC.Types.Int, [EVM.Types.Expr 'EVM.Types.EWord]), b GHC.Types.~ Data.Map.Internal.Map EVM.Types.CodeLocation (GHC.Types.Int, [EVM.Types.Expr 'EVM.Types.EWord])) => Optics.Label.LabelOptic "iterations" k (EVM.Types.VM s) (EVM.Types.VM s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Set.Internal.Set (EVM.Types.Expr 'EVM.Types.EAddr), b GHC.Types.~ Data.Set.Internal.Set (EVM.Types.Expr 'EVM.Types.EAddr)) => Optics.Label.LabelOptic "accessedAddresses" k EVM.Types.SubState EVM.Types.SubState a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Set.Internal.Set (EVM.Types.Expr 'EVM.Types.EAddr, EVM.Types.W256), b GHC.Types.~ Data.Set.Internal.Set (EVM.Types.Expr 'EVM.Types.EAddr, EVM.Types.W256)) => Optics.Label.LabelOptic "accessedStorageKeys" k EVM.Types.SubState EVM.Types.SubState a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ Data.Tree.Zipper.TreePos Data.Tree.Zipper.Empty EVM.Types.Trace, b GHC.Types.~ Data.Tree.Zipper.TreePos Data.Tree.Zipper.Empty EVM.Types.Trace) => Optics.Label.LabelOptic "traces" k (EVM.Types.VM s) (EVM.Types.VM s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.BaseState, b GHC.Types.~ EVM.Types.BaseState) => Optics.Label.LabelOptic "baseState" k EVM.Types.RuntimeConfig EVM.Types.RuntimeConfig a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.BaseState, b GHC.Types.~ EVM.Types.BaseState) => Optics.Label.LabelOptic "baseState" k EVM.Types.VMOpts EVM.Types.VMOpts a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Block, b GHC.Types.~ EVM.Types.Block) => Optics.Label.LabelOptic "block" k (EVM.Types.VM s) (EVM.Types.VM s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Cache, b GHC.Types.~ EVM.Types.Cache) => Optics.Label.LabelOptic "cache" k (EVM.Types.VM s) (EVM.Types.VM s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.ContractCode, b GHC.Types.~ EVM.Types.ContractCode) => Optics.Label.LabelOptic "code" k (EVM.Types.FrameState s) (EVM.Types.FrameState s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.ContractCode, b GHC.Types.~ EVM.Types.ContractCode) => Optics.Label.LabelOptic "code" k EVM.Types.Contract EVM.Types.Contract a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Env, b GHC.Types.~ EVM.Types.Env) => Optics.Label.LabelOptic "env" k (EVM.Types.VM s) (EVM.Types.VM s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.Buf, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.Buf) => Optics.Label.LabelOptic "calldata" k (EVM.Types.FrameState s) (EVM.Types.FrameState s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.Buf, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.Buf) => Optics.Label.LabelOptic "returndata" k (EVM.Types.FrameState s) (EVM.Types.FrameState s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr) => Optics.Label.LabelOptic "address" k EVM.Types.VMOpts EVM.Types.VMOpts a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr) => Optics.Label.LabelOptic "caller" k (EVM.Types.FrameState s) (EVM.Types.FrameState s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr) => Optics.Label.LabelOptic "caller" k EVM.Types.VMOpts EVM.Types.VMOpts a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr) => Optics.Label.LabelOptic "codeContract" k (EVM.Types.FrameState s) (EVM.Types.FrameState s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr) => Optics.Label.LabelOptic "coinbase" k EVM.Types.Block EVM.Types.Block a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr) => Optics.Label.LabelOptic "coinbase" k EVM.Types.VMOpts EVM.Types.VMOpts a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr) => Optics.Label.LabelOptic "contract" k (EVM.Types.FrameState s) (EVM.Types.FrameState s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr) => Optics.Label.LabelOptic "origin" k EVM.Types.TxState EVM.Types.TxState a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr) => Optics.Label.LabelOptic "origin" k EVM.Types.VMOpts EVM.Types.VMOpts a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr) => Optics.Label.LabelOptic "toAddr" k EVM.Types.TxState EVM.Types.TxState a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EWord, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EWord) => Optics.Label.LabelOptic "balance" k EVM.Types.Contract EVM.Types.Contract a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EWord, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EWord) => Optics.Label.LabelOptic "callvalue" k (EVM.Types.FrameState s) (EVM.Types.FrameState s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.Storage, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.Storage) => Optics.Label.LabelOptic "origStorage" k EVM.Types.Contract EVM.Types.Contract a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.Storage, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.Storage) => Optics.Label.LabelOptic "storage" k EVM.Types.Contract EVM.Types.Contract a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.FrameContext, b GHC.Types.~ EVM.Types.FrameContext) => Optics.Label.LabelOptic "context" k (EVM.Types.Frame s) (EVM.Types.Frame s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.FrameState s, b GHC.Types.~ EVM.Types.FrameState s) => Optics.Label.LabelOptic "state" k (EVM.Types.VM s) (EVM.Types.VM s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.FrameState s1, b GHC.Types.~ EVM.Types.FrameState s2) => Optics.Label.LabelOptic "state" k (EVM.Types.Frame s1) (EVM.Types.Frame s2) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.Memory s1, b GHC.Types.~ EVM.Types.Memory s2) => Optics.Label.LabelOptic "memory" k (EVM.Types.FrameState s1) (EVM.Types.FrameState s2) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.RuntimeConfig, b GHC.Types.~ EVM.Types.RuntimeConfig) => Optics.Label.LabelOptic "config" k (EVM.Types.VM s) (EVM.Types.VM s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ EVM.Types.TxState, b GHC.Types.~ EVM.Types.TxState) => Optics.Label.LabelOptic "tx" k (EVM.Types.VM s) (EVM.Types.VM s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Maybe.Maybe (EVM.Types.Expr 'EVM.Types.EAddr), b GHC.Types.~ GHC.Maybe.Maybe (EVM.Types.Expr 'EVM.Types.EAddr)) => Optics.Label.LabelOptic "overrideCaller" k EVM.Types.RuntimeConfig EVM.Types.RuntimeConfig a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Maybe.Maybe (EVM.Types.VMResult s), b GHC.Types.~ GHC.Maybe.Maybe (EVM.Types.VMResult s)) => Optics.Label.LabelOptic "result" k (EVM.Types.VM s) (EVM.Types.VM s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Maybe.Maybe EVM.Types.W64, b GHC.Types.~ GHC.Maybe.Maybe EVM.Types.W64) => Optics.Label.LabelOptic "nonce" k EVM.Types.Contract EVM.Types.Contract a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Types.Bool, b GHC.Types.~ GHC.Types.Bool) => Optics.Label.LabelOptic "allowFFI" k EVM.Types.RuntimeConfig EVM.Types.RuntimeConfig a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Types.Bool, b GHC.Types.~ GHC.Types.Bool) => Optics.Label.LabelOptic "static" k (EVM.Types.FrameState s) (EVM.Types.FrameState s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Types.Int, b GHC.Types.~ GHC.Types.Int) => Optics.Label.LabelOptic "freshAddresses" k EVM.Types.Env EVM.Types.Env a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ GHC.Types.Int, b GHC.Types.~ GHC.Types.Int) => Optics.Label.LabelOptic "pc" k (EVM.Types.FrameState s) (EVM.Types.FrameState s) 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 "burned" k (EVM.Types.VM s) (EVM.Types.VM s) 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 "gas" k (EVM.Types.FrameState s) (EVM.Types.FrameState s) 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 "memorySize" k (EVM.Types.FrameState s) (EVM.Types.FrameState s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ [(EVM.Types.Expr 'EVM.Types.EAddr, EVM.Types.Contract)], b GHC.Types.~ [(EVM.Types.Expr 'EVM.Types.EAddr, EVM.Types.Contract)]) => Optics.Label.LabelOptic "otherContracts" k EVM.Types.VMOpts EVM.Types.VMOpts a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ [(EVM.Types.Expr 'EVM.Types.EAddr, GHC.Word.Word64)], b GHC.Types.~ [(EVM.Types.Expr 'EVM.Types.EAddr, GHC.Word.Word64)]) => Optics.Label.LabelOptic "refunds" k EVM.Types.SubState EVM.Types.SubState a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ [EVM.Types.Expr 'EVM.Types.EAddr], b GHC.Types.~ [EVM.Types.Expr 'EVM.Types.EAddr]) => Optics.Label.LabelOptic "selfdestructs" k EVM.Types.SubState EVM.Types.SubState a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ [EVM.Types.Expr 'EVM.Types.EAddr], b GHC.Types.~ [EVM.Types.Expr 'EVM.Types.EAddr]) => Optics.Label.LabelOptic "touchedAccounts" k EVM.Types.SubState EVM.Types.SubState a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ [EVM.Types.Expr 'EVM.Types.EWord], b GHC.Types.~ [EVM.Types.Expr 'EVM.Types.EWord]) => Optics.Label.LabelOptic "stack" k (EVM.Types.FrameState s) (EVM.Types.FrameState s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ [EVM.Types.Expr 'EVM.Types.Log], b GHC.Types.~ [EVM.Types.Expr 'EVM.Types.Log]) => Optics.Label.LabelOptic "logs" k (EVM.Types.VM s) (EVM.Types.VM s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ [EVM.Types.Frame s], b GHC.Types.~ [EVM.Types.Frame s]) => Optics.Label.LabelOptic "frames" k (EVM.Types.VM s) (EVM.Types.VM s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ [EVM.Types.Prop], b GHC.Types.~ [EVM.Types.Prop]) => Optics.Label.LabelOptic "constraints" k (EVM.Types.VM s) (EVM.Types.VM s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.A_Lens, a GHC.Types.~ [EVM.Types.Prop], b GHC.Types.~ [EVM.Types.Prop]) => Optics.Label.LabelOptic "keccakEqs" k (EVM.Types.VM s) (EVM.Types.VM s) a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.An_AffineTraversal, a GHC.Types.~ Data.Map.Internal.Map (EVM.Types.Expr 'EVM.Types.EAddr) EVM.Types.Contract, b GHC.Types.~ Data.Map.Internal.Map (EVM.Types.Expr 'EVM.Types.EAddr) EVM.Types.Contract) => Optics.Label.LabelOptic "callreversion" k EVM.Types.FrameContext EVM.Types.FrameContext a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.An_AffineTraversal, a GHC.Types.~ Data.Map.Internal.Map (EVM.Types.Expr 'EVM.Types.EAddr) EVM.Types.Contract, b GHC.Types.~ Data.Map.Internal.Map (EVM.Types.Expr 'EVM.Types.EAddr) EVM.Types.Contract) => Optics.Label.LabelOptic "createreversion" k EVM.Types.FrameContext EVM.Types.FrameContext a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.An_AffineTraversal, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr) => Optics.Label.LabelOptic "address" k EVM.Types.FrameContext EVM.Types.FrameContext a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.An_AffineTraversal, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr) => Optics.Label.LabelOptic "context" k EVM.Types.FrameContext EVM.Types.FrameContext a b
+ EVM.Types: instance (k GHC.Types.~ Optics.Internal.Optic.Types.An_AffineTraversal, a GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr, b GHC.Types.~ EVM.Types.Expr 'EVM.Types.EAddr) => Optics.Label.LabelOptic "target" k EVM.Types.FrameContext EVM.Types.FrameContext a b
+ EVM.Types: instance GHC.Classes.Eq EVM.Types.AbstRefineConfig
+ EVM.Types: instance GHC.Generics.Generic (EVM.Types.FrameState s)
+ EVM.Types: instance GHC.Generics.Generic (EVM.Types.VM s)
+ EVM.Types: instance GHC.Show.Show (EVM.Types.Choose s)
+ EVM.Types: instance GHC.Show.Show (EVM.Types.Effect s)
+ EVM.Types: instance GHC.Show.Show (EVM.Types.Frame s)
+ EVM.Types: instance GHC.Show.Show (EVM.Types.FrameState s)
+ EVM.Types: instance GHC.Show.Show (EVM.Types.Memory s)
+ EVM.Types: instance GHC.Show.Show (EVM.Types.Query s)
+ EVM.Types: instance GHC.Show.Show (EVM.Types.VM s)
+ EVM.Types: instance GHC.Show.Show (EVM.Types.VMResult s)
+ EVM.Types: instance GHC.Show.Show EVM.Types.AbstRefineConfig
+ EVM.Types: instance GHC.Show.Show EVM.Types.BaseState
+ EVM.Types: instance GHC.Show.Show EVM.Types.RuntimeConfig
+ EVM.Types: instance Witch.From.From EVM.Types.W64 EVM.Types.W256
+ EVM.Types: instance Witch.From.From GHC.Word.Word32 Data.ByteString.Internal.Type.ByteString
+ EVM.Types: instance Witch.TryFrom.TryFrom EVM.Types.W256 EVM.Types.W64
+ EVM.Types: instance Witch.TryFrom.TryFrom GHC.Num.Integer.Integer EVM.Types.Addr
+ EVM.Types: isPBool :: Prop -> Bool
+ EVM.Types: maybeConcreteStore :: Expr Storage -> Maybe (Map W256 W256)
+ EVM.Types: maybeLitAddr :: Expr EAddr -> Maybe Addr
+ EVM.Types: paddedShowHex :: (Show a, Integral a) => Int -> a -> String
+ EVM.Types: truncateToAddr :: W256 -> Addr
+ EVM.Types: type MutableMemory s = STVector s Word8
+ EVM.UnitTest: allBranchRev :: Text -> Text
+ EVM.UnitTest: paramsFromRpc :: RpcInfo -> IO TestVMParams
- EVM: accessAccountForGas :: Addr -> EVM Bool
+ EVM: accessAccountForGas :: Expr EAddr -> EVM s Bool
- EVM: accessAndBurn :: Addr -> EVM () -> EVM ()
+ EVM: accessAndBurn :: Expr EAddr -> EVM s () -> EVM s ()
- EVM: accessMemoryRange :: W256 -> W256 -> EVM () -> EVM ()
+ EVM: accessMemoryRange :: W256 -> W256 -> EVM s () -> EVM s ()
- EVM: accessMemoryWord :: W256 -> EVM () -> EVM ()
+ EVM: accessMemoryWord :: W256 -> EVM s () -> EVM s ()
- EVM: accessStorage :: Addr -> Expr EWord -> (Expr EWord -> EVM ()) -> EVM ()
+ EVM: accessStorage :: Expr EAddr -> Expr EWord -> (Expr EWord -> EVM s ()) -> EVM s ()
- EVM: accessStorageForGas :: Addr -> Expr EWord -> EVM Bool
+ EVM: accessStorageForGas :: Expr EAddr -> Expr EWord -> EVM s Bool
- EVM: accessUnboundedMemoryRange :: Word64 -> Word64 -> EVM () -> EVM ()
+ EVM: accessUnboundedMemoryRange :: Word64 -> Word64 -> EVM s () -> EVM s ()
- EVM: accountExists :: Addr -> VM -> Bool
+ EVM: accountExists :: Expr EAddr -> VM s -> Bool
- EVM: blankState :: FrameState
+ EVM: blankState :: ST s (FrameState s)
- EVM: branch :: CodeLocation -> Expr EWord -> (Bool -> EVM ()) -> EVM ()
+ EVM: branch :: forall s. Expr EWord -> (Bool -> EVM s ()) -> EVM s ()
- EVM: burn :: Word64 -> EVM () -> EVM ()
+ EVM: burn :: Word64 -> EVM s () -> EVM s ()
- EVM: bytecode :: Getter Contract (Expr Buf)
+ EVM: bytecode :: Getter Contract (Maybe (Expr Buf))
- EVM: callChecks :: (?op :: Word8) => Contract -> Word64 -> Addr -> Addr -> W256 -> W256 -> W256 -> W256 -> W256 -> [Expr EWord] -> (Word64 -> EVM ()) -> EVM ()
+ EVM: callChecks :: (?op :: Word8) => Contract -> Word64 -> Expr EAddr -> Expr EAddr -> Expr EWord -> W256 -> W256 -> W256 -> W256 -> [Expr EWord] -> (Word64 -> EVM s ()) -> EVM s ()
- EVM: cheat :: (?op :: Word8) => (W256, W256) -> (W256, W256) -> EVM ()
+ EVM: cheat :: (?op :: Word8) => (W256, W256) -> (W256, W256) -> EVM s ()
- EVM: cheatActions :: Map FunctionSelector CheatAction
+ EVM: cheatActions :: Map FunctionSelector (CheatAction s)
- EVM: cheatCode :: Addr
+ EVM: cheatCode :: Expr EAddr
- EVM: checkJump :: Int -> [Expr EWord] -> EVM ()
+ EVM: checkJump :: Int -> [Expr EWord] -> EVM s ()
- EVM: choose :: Choose -> EVM ()
+ EVM: choose :: Choose s -> EVM s ()
- EVM: codeloc :: EVM CodeLocation
+ EVM: codeloc :: EVM s CodeLocation
- EVM: copyBytesToMemory :: Expr Buf -> Expr EWord -> Expr EWord -> Expr EWord -> EVM ()
+ EVM: copyBytesToMemory :: Expr Buf -> Expr EWord -> Expr EWord -> Expr EWord -> EVM s ()
- EVM: copyCallBytesToMemory :: Expr Buf -> Expr EWord -> Expr EWord -> Expr EWord -> EVM ()
+ EVM: copyCallBytesToMemory :: Expr Buf -> Expr EWord -> Expr EWord -> EVM s ()
- EVM: costOfCall :: FeeSchedule Word64 -> Bool -> W256 -> Word64 -> Word64 -> Addr -> EVM (Word64, Word64)
+ EVM: costOfCall :: FeeSchedule Word64 -> Bool -> Expr EWord -> Word64 -> Word64 -> Expr EAddr -> EVM s (Word64, Word64)
- EVM: create :: (?op :: Word8) => Addr -> Contract -> W256 -> Word64 -> W256 -> [Expr EWord] -> Addr -> Expr Buf -> EVM ()
+ EVM: create :: (?op :: Word8) => Expr EAddr -> Contract -> W256 -> Word64 -> Expr EWord -> [Expr EWord] -> Expr EAddr -> Expr Buf -> EVM s ()
- EVM: currentContract :: VM -> Maybe Contract
+ EVM: currentContract :: VM s -> Maybe Contract
- EVM: delegateCall :: (?op :: Word8) => Contract -> Word64 -> Expr EWord -> Expr EWord -> W256 -> W256 -> W256 -> W256 -> W256 -> [Expr EWord] -> (Addr -> EVM ()) -> EVM ()
+ EVM: delegateCall :: (?op :: Word8) => Contract -> Word64 -> Expr EAddr -> Expr EAddr -> Expr EWord -> W256 -> W256 -> W256 -> W256 -> [Expr EWord] -> (Expr EAddr -> EVM s ()) -> EVM s ()
- EVM: exec1 :: EVM ()
+ EVM: exec1 :: EVM s ()
- EVM: executePrecompile :: (?op :: Word8) => Addr -> Word64 -> W256 -> W256 -> W256 -> W256 -> [Expr EWord] -> EVM ()
+ EVM: executePrecompile :: (?op :: Word8) => Addr -> Word64 -> W256 -> W256 -> W256 -> W256 -> [Expr EWord] -> EVM s ()
- EVM: fetchAccount :: Addr -> (Contract -> EVM ()) -> EVM ()
+ EVM: fetchAccount :: Expr EAddr -> (Contract -> EVM s ()) -> EVM s ()
- EVM: finalize :: EVM ()
+ EVM: finalize :: EVM s ()
- EVM: finishFrame :: FrameResult -> EVM ()
+ EVM: finishFrame :: FrameResult -> EVM s ()
- EVM: forceConcrete :: Expr EWord -> String -> (W256 -> EVM ()) -> EVM ()
+ EVM: forceConcrete :: Expr EWord -> String -> (W256 -> EVM s ()) -> EVM s ()
- EVM: forceConcrete2 :: (Expr EWord, Expr EWord) -> String -> ((W256, W256) -> EVM ()) -> EVM ()
+ EVM: forceConcrete2 :: (Expr EWord, Expr EWord) -> String -> ((W256, W256) -> EVM s ()) -> EVM s ()
- EVM: forceConcrete3 :: (Expr EWord, Expr EWord, Expr EWord) -> String -> ((W256, W256, W256) -> EVM ()) -> EVM ()
+ EVM: forceConcrete3 :: (Expr EWord, Expr EWord, Expr EWord) -> String -> ((W256, W256, W256) -> EVM s ()) -> EVM s ()
- EVM: forceConcrete4 :: (Expr EWord, Expr EWord, Expr EWord, Expr EWord) -> String -> ((W256, W256, W256, W256) -> EVM ()) -> EVM ()
+ EVM: forceConcrete4 :: (Expr EWord, Expr EWord, Expr EWord, Expr EWord) -> String -> ((W256, W256, W256, W256) -> EVM s ()) -> EVM s ()
- EVM: forceConcrete5 :: (Expr EWord, Expr EWord, Expr EWord, Expr EWord, Expr EWord) -> String -> ((W256, W256, W256, W256, W256) -> EVM ()) -> EVM ()
+ EVM: forceConcrete5 :: (Expr EWord, Expr EWord, Expr EWord, Expr EWord, Expr EWord) -> String -> ((W256, W256, W256, W256, W256) -> EVM s ()) -> EVM s ()
- EVM: forceConcrete6 :: (Expr EWord, Expr EWord, Expr EWord, Expr EWord, Expr EWord, Expr EWord) -> String -> ((W256, W256, W256, W256, W256, W256) -> EVM ()) -> EVM ()
+ EVM: forceConcrete6 :: (Expr EWord, Expr EWord, Expr EWord, Expr EWord, Expr EWord, Expr EWord) -> String -> ((W256, W256, W256, W256, W256, W256) -> EVM s ()) -> EVM s ()
- EVM: forceConcreteBuf :: Expr Buf -> String -> (ByteString -> EVM ()) -> EVM ()
+ EVM: forceConcreteBuf :: Expr Buf -> String -> (ByteString -> EVM s ()) -> EVM s ()
- EVM: getCodeLocation :: VM -> CodeLocation
+ EVM: getCodeLocation :: VM s -> CodeLocation
- EVM: insertTrace :: TraceData -> EVM ()
+ EVM: insertTrace :: TraceData -> EVM s ()
- EVM: isValidJumpDest :: VM -> Int -> Bool
+ EVM: isValidJumpDest :: VM s -> Int -> Bool
- EVM: limitStack :: Int -> EVM () -> EVM ()
+ EVM: limitStack :: Int -> EVM s () -> EVM s ()
- EVM: loadContract :: Addr -> EVM ()
+ EVM: loadContract :: Expr EAddr -> State (VM s) ()
- EVM: makeVm :: VMOpts -> VM
+ EVM: makeVm :: VMOpts -> ST s (VM s)
- EVM: next :: (?op :: Word8) => EVM ()
+ EVM: next :: (?op :: Word8) => EVM s ()
- EVM: notStatic :: EVM () -> EVM ()
+ EVM: notStatic :: EVM s () -> EVM s ()
- EVM: partial :: PartialExec -> EVM ()
+ EVM: partial :: PartialExec -> EVM s ()
- EVM: popTrace :: EVM ()
+ EVM: popTrace :: EVM s ()
- EVM: precompiledContract :: (?op :: Word8) => Contract -> Word64 -> Addr -> Addr -> W256 -> W256 -> W256 -> W256 -> W256 -> [Expr EWord] -> EVM ()
+ EVM: precompiledContract :: (?op :: Word8) => Contract -> Word64 -> Addr -> Addr -> Expr EWord -> W256 -> W256 -> W256 -> W256 -> [Expr EWord] -> EVM s ()
- EVM: push :: W256 -> EVM ()
+ EVM: push :: W256 -> EVM s ()
- EVM: pushSym :: Expr EWord -> EVM ()
+ EVM: pushSym :: Expr EWord -> EVM s ()
- EVM: pushTrace :: TraceData -> EVM ()
+ EVM: pushTrace :: TraceData -> EVM s ()
- EVM: query :: Query -> EVM ()
+ EVM: query :: Query s -> EVM s ()
- EVM: readMemory :: Expr EWord -> Expr EWord -> VM -> Expr Buf
+ EVM: readMemory :: Expr EWord -> Expr EWord -> EVM s (Expr Buf)
- EVM: refund :: Word64 -> EVM ()
+ EVM: refund :: Word64 -> EVM s ()
- EVM: replaceCode :: Addr -> ContractCode -> EVM ()
+ EVM: replaceCode :: Expr EAddr -> ContractCode -> EVM s ()
- EVM: replaceCodeOfSelf :: ContractCode -> EVM ()
+ EVM: replaceCodeOfSelf :: ContractCode -> EVM s ()
- EVM: resetState :: EVM ()
+ EVM: resetState :: EVM s ()
- EVM: selfdestruct :: Addr -> EVM ()
+ EVM: selfdestruct :: Expr EAddr -> EVM s ()
- EVM: stackOp1 :: (?op :: Word8) => Word64 -> (Expr EWord -> Expr EWord) -> EVM ()
+ EVM: stackOp1 :: (?op :: Word8) => Word64 -> (Expr EWord -> Expr EWord) -> EVM s ()
- EVM: stackOp2 :: (?op :: Word8) => Word64 -> ((Expr EWord, Expr EWord) -> Expr EWord) -> EVM ()
+ EVM: stackOp2 :: (?op :: Word8) => Word64 -> (Expr EWord -> Expr EWord -> Expr EWord) -> EVM s ()
- EVM: stackOp3 :: (?op :: Word8) => Word64 -> ((Expr EWord, Expr EWord, Expr EWord) -> Expr EWord) -> EVM ()
+ EVM: stackOp3 :: (?op :: Word8) => Word64 -> (Expr EWord -> Expr EWord -> Expr EWord -> Expr EWord) -> EVM s ()
- EVM: toBuf :: ContractCode -> Expr Buf
+ EVM: toBuf :: ContractCode -> Maybe (Expr Buf)
- EVM: touchAccount :: Addr -> EVM ()
+ EVM: touchAccount :: Expr EAddr -> EVM s ()
- EVM: traceContext :: Expr End -> Map Addr Contract
+ EVM: traceContext :: Expr End -> Map (Expr EAddr) Contract
- EVM: traceForest :: VM -> Forest Trace
+ EVM: traceForest :: VM s -> Forest Trace
- EVM: traceTopLog :: [Expr Log] -> EVM ()
+ EVM: traceTopLog :: [Expr Log] -> EVM s ()
- EVM: transfer :: Addr -> Addr -> W256 -> EVM ()
+ EVM: transfer :: Expr EAddr -> Expr EAddr -> Expr EWord -> EVM s ()
- EVM: type CheatAction = Expr EWord -> Expr EWord -> Expr Buf -> EVM ()
+ EVM: type CheatAction s = Expr EWord -> Expr EWord -> Expr Buf -> EVM s ()
- EVM: unRefund :: Word64 -> EVM ()
+ EVM: unRefund :: Word64 -> EVM s ()
- EVM: underrun :: EVM ()
+ EVM: underrun :: EVM s ()
- EVM: use' :: (VM -> a) -> EVM a
+ EVM: use' :: (VM s -> a) -> EVM s a
- EVM: vmError :: EvmError -> EVM ()
+ EVM: vmError :: EvmError -> EVM s ()
- EVM: vmOp :: VM -> Maybe Op
+ EVM: vmOp :: VM s -> Maybe Op
- EVM: vmOpIx :: VM -> Maybe Int
+ EVM: vmOpIx :: VM s -> Maybe Int
- EVM: withTraceLocation :: TraceData -> EVM Trace
+ EVM: withTraceLocation :: TraceData -> EVM s Trace
- EVM.Concrete: create2Address :: Addr -> W256 -> ByteString -> Addr
+ EVM.Concrete: create2Address :: Addr -> W256 -> ByteString -> Expr EAddr
- EVM.Concrete: createAddress :: Addr -> W256 -> Addr
+ EVM.Concrete: createAddress :: Addr -> W64 -> Expr EAddr
- EVM.Dapp: DappContext :: DappInfo -> Map Addr Contract -> DappContext
+ EVM.Dapp: DappContext :: DappInfo -> Map (Expr EAddr) Contract -> DappContext
- EVM.Dapp: DappInfo :: FilePath -> Map Text SolcContract -> Map W256 (CodeType, SolcContract) -> [(Code, SolcContract)] -> SourceCache -> [(Text, [(Test, [AbiType])])] -> Map FunctionSelector Method -> Map W256 Event -> Map W256 SolError -> Map Int Value -> (SrcMap -> Maybe Value) -> DappInfo
+ EVM.Dapp: DappInfo :: FilePath -> Map Text SolcContract -> Map W256 (CodeType, SolcContract) -> [(Code, SolcContract)] -> SourceCache -> [(Text, [Sig])] -> Map FunctionSelector Method -> Map W256 Event -> Map W256 SolError -> Map Int Value -> (SrcMap -> Maybe Value) -> DappInfo
- EVM.Dapp: [$sel:env:DappContext] :: DappContext -> Map Addr Contract
+ EVM.Dapp: [$sel:env:DappContext] :: DappContext -> Map (Expr EAddr) Contract
- EVM.Dapp: [$sel:unitTests:DappInfo] :: DappInfo -> [(Text, [(Test, [AbiType])])]
+ EVM.Dapp: [$sel:unitTests:DappInfo] :: DappInfo -> [(Text, [Sig])]
- EVM.Dapp: findAllUnitTests :: [SolcContract] -> [(Text, [(Test, [AbiType])])]
+ EVM.Dapp: findAllUnitTests :: [SolcContract] -> [(Text, [Sig])]
- EVM.Dapp: findUnitTests :: Text -> [SolcContract] -> [(Text, [(Test, [AbiType])])]
+ EVM.Dapp: findUnitTests :: Text -> [SolcContract] -> [(Text, [Sig])]
- EVM.Dapp: unitTestMethods :: SolcContract -> [(Test, [AbiType])]
+ EVM.Dapp: unitTestMethods :: SolcContract -> [Sig]
- EVM.Dapp: unitTestMethodsFiltered :: (Text -> Bool) -> SolcContract -> [(Test, [AbiType])]
+ EVM.Dapp: unitTestMethodsFiltered :: (Text -> Bool) -> SolcContract -> [Sig]
- EVM.Exec: exec :: State VM VMResult
+ EVM.Exec: exec :: EVM s (VMResult s)
- EVM.Exec: execWhile :: (VM -> Bool) -> State VM Int
+ EVM.Exec: execWhile :: (VM s -> Bool) -> State (VM s) Int
- EVM.Exec: run :: State VM VM
+ EVM.Exec: run :: EVM s (VM s)
- EVM.Exec: vmForEthrunCreation :: ByteString -> VM
+ EVM.Exec: vmForEthrunCreation :: ByteString -> ST s (VM s)
- EVM.Expr: readStorage :: Expr EWord -> Expr EWord -> Expr Storage -> Maybe (Expr EWord)
+ EVM.Expr: readStorage :: Expr EWord -> Expr Storage -> Maybe (Expr EWord)
- EVM.Expr: readStorage' :: Expr EWord -> Expr EWord -> Expr Storage -> Expr EWord
+ EVM.Expr: readStorage' :: Expr EWord -> Expr Storage -> Expr EWord
- EVM.Expr: writeStorage :: Expr EWord -> Expr EWord -> Expr EWord -> Expr Storage -> Expr Storage
+ EVM.Expr: writeStorage :: Expr EWord -> Expr EWord -> Expr Storage -> Expr Storage
- EVM.Fetch: [QueryNonce] :: Addr -> RpcQuery W256
+ EVM.Fetch: [QueryNonce] :: Addr -> RpcQuery W64
- EVM.Fetch: http :: Natural -> Maybe Natural -> BlockNumber -> Text -> Fetcher
+ EVM.Fetch: http :: Natural -> Maybe Natural -> BlockNumber -> Text -> Fetcher s
- EVM.Fetch: oracle :: SolverGroup -> RpcInfo -> Fetcher
+ EVM.Fetch: oracle :: SolverGroup -> RpcInfo -> Fetcher s
- EVM.Fetch: type Fetcher = Query -> IO (EVM ())
+ EVM.Fetch: type Fetcher s = Query s -> IO (EVM s ())
- EVM.Fetch: zero :: Natural -> Maybe Natural -> Fetcher
+ EVM.Fetch: zero :: Natural -> Maybe Natural -> Fetcher s
- EVM.Format: showTraceTree :: DappInfo -> VM -> Text
+ EVM.Format: showTraceTree :: DappInfo -> VM s -> Text
- EVM.Format: showWordExact :: W256 -> Text
+ EVM.Format: showWordExact :: Integral i => i -> Text
- EVM.SMT: CexVars :: [Text] -> Map Text (Expr EWord) -> [(Expr EWord, Expr EWord)] -> [Text] -> [Text] -> CexVars
+ EVM.SMT: CexVars :: [Text] -> [Text] -> Map Text (Expr EWord) -> Map (Expr EAddr) (Set (Expr EWord)) -> [Text] -> [Text] -> CexVars
- EVM.SMT: SMT2 :: [Builder] -> CexVars -> SMT2
+ EVM.SMT: SMT2 :: [Builder] -> RefinementEqs -> CexVars -> SMT2
- EVM.SMT: SMTCex :: Map (Expr EWord) W256 -> Map (Expr Buf) BufModel -> Map W256 (Map W256 W256) -> Map (Expr EWord) W256 -> Map (Expr EWord) W256 -> SMTCex
+ EVM.SMT: SMTCex :: Map (Expr EWord) W256 -> Map (Expr EAddr) Addr -> Map (Expr Buf) BufModel -> Map (Expr EAddr) (Map W256 W256) -> Map (Expr EWord) W256 -> Map (Expr EWord) W256 -> SMTCex
- EVM.SMT: [$sel:store:SMTCex] :: SMTCex -> Map W256 (Map W256 W256)
+ EVM.SMT: [$sel:store:SMTCex] :: SMTCex -> Map (Expr EAddr) (Map W256 W256)
- EVM.SMT: [$sel:storeReads:CexVars] :: CexVars -> [(Expr EWord, Expr EWord)]
+ EVM.SMT: [$sel:storeReads:CexVars] :: CexVars -> Map (Expr EAddr) (Set (Expr EWord))
- EVM.SMT: assertProps :: [Prop] -> SMT2
+ EVM.SMT: assertProps :: AbstRefineConfig -> [Prop] -> SMT2
- EVM.SMT: encodeConcreteStore :: Map W256 (Map W256 W256) -> Builder
+ EVM.SMT: encodeConcreteStore :: Map W256 W256 -> Builder
- EVM.SMT: findStorageReads :: Prop -> [(Expr EWord, Expr EWord)]
+ EVM.SMT: findStorageReads :: Prop -> Map (Expr EAddr) (Set (Expr EWord))
- EVM.SMT: getStore :: (Text -> IO Text) -> [(Expr EWord, Expr EWord)] -> IO (Map W256 (Map W256 W256))
+ EVM.SMT: getStore :: (Text -> IO Text) -> Map (Expr EAddr) (Set (Expr EWord)) -> IO (Map (Expr EAddr) (Map W256 W256))
- EVM.SMT: referencedBlockContext :: Expr a -> [(Builder, [Prop])]
+ EVM.SMT: referencedBlockContext :: TraversableTerm a => a -> [(Builder, [Prop])]
- EVM.SMT: referencedBufs :: Expr a -> [Builder]
+ EVM.SMT: referencedBufs :: TraversableTerm a => a -> [Builder]
- EVM.SMT: referencedFrameContext :: Expr a -> [(Builder, [Prop])]
+ EVM.SMT: referencedFrameContext :: TraversableTerm a => a -> [(Builder, [Prop])]
- EVM.SMT: referencedVars :: Expr a -> [Builder]
+ EVM.SMT: referencedVars :: TraversableTerm a => a -> [Builder]
- EVM.Solvers: splitSExpr :: [Text] -> Text
+ EVM.Solvers: splitSExpr :: [Text] -> [Text]
- EVM.Stepper: [Ask] :: Choose -> Action ()
+ EVM.Stepper: [Ask] :: Choose s -> Action s ()
- EVM.Stepper: [EVM] :: EVM a -> Action a
+ EVM.Stepper: [EVM] :: EVM s a -> Action s a
- EVM.Stepper: [Exec] :: Action VMResult
+ EVM.Stepper: [Exec] :: Action s (VMResult s)
- EVM.Stepper: [IOAct] :: StateT VM IO a -> Action a
+ EVM.Stepper: [IOAct] :: IO a -> Action s a
- EVM.Stepper: [Wait] :: Query -> Action ()
+ EVM.Stepper: [Wait] :: Query s -> Action s ()
- EVM.Stepper: ask :: Choose -> Stepper ()
+ EVM.Stepper: ask :: Choose s -> Stepper s ()
- EVM.Stepper: data Action a
+ EVM.Stepper: data Action s a
- EVM.Stepper: enter :: Text -> Stepper ()
+ EVM.Stepper: enter :: Text -> Stepper s ()
- EVM.Stepper: evm :: EVM a -> Stepper a
+ EVM.Stepper: evm :: EVM s a -> Stepper s a
- EVM.Stepper: evmIO :: StateT VM IO a -> Stepper a
+ EVM.Stepper: evmIO :: IO a -> Stepper s a
- EVM.Stepper: exec :: Stepper VMResult
+ EVM.Stepper: exec :: Stepper s (VMResult s)
- EVM.Stepper: execFully :: Stepper (Either EvmError (Expr Buf))
+ EVM.Stepper: execFully :: Stepper s (Either EvmError (Expr Buf))
- EVM.Stepper: interpret :: Fetcher -> VM -> Stepper a -> IO a
+ EVM.Stepper: interpret :: Fetcher RealWorld -> VM RealWorld -> Stepper RealWorld a -> IO a
- EVM.Stepper: run :: Stepper VM
+ EVM.Stepper: run :: Stepper s (VM s)
- EVM.Stepper: runFully :: Stepper VM
+ EVM.Stepper: runFully :: Stepper s (VM s)
- EVM.Stepper: type Stepper a = Program Action a
+ EVM.Stepper: type Stepper s a = Program (Action s) a
- EVM.Stepper: wait :: Query -> Stepper ()
+ EVM.Stepper: wait :: Query s -> Stepper s ()
- EVM.SymExec: VeriOpts :: Bool -> Bool -> Maybe Integer -> Integer -> LoopHeuristic -> RpcInfo -> VeriOpts
+ EVM.SymExec: VeriOpts :: Bool -> Bool -> Maybe Integer -> Integer -> LoopHeuristic -> AbstRefineConfig -> RpcInfo -> VeriOpts
- EVM.SymExec: abstractVM :: (Expr Buf, [Prop]) -> ByteString -> Maybe Precondition -> Expr Storage -> VM
+ EVM.SymExec: abstractVM :: (Expr Buf, [Prop]) -> ByteString -> Maybe (Precondition s) -> Bool -> ST s (VM s)
- EVM.SymExec: askSmtItersReached :: VM -> Integer -> Bool
+ EVM.SymExec: askSmtItersReached :: VM s -> Integer -> Bool
- EVM.SymExec: checkAssertions :: [Word256] -> Postcondition
+ EVM.SymExec: checkAssertions :: [Word256] -> Postcondition s
- EVM.SymExec: formatCex :: Expr Buf -> SMTCex -> Text
+ EVM.SymExec: formatCex :: Expr Buf -> Maybe Sig -> SMTCex -> Text
- EVM.SymExec: interpret :: Fetcher -> Maybe Integer -> Integer -> LoopHeuristic -> VM -> Stepper (Expr End) -> IO (Expr End)
+ EVM.SymExec: interpret :: Fetcher RealWorld -> Maybe Integer -> Integer -> LoopHeuristic -> VM RealWorld -> Stepper RealWorld (Expr End) -> IO (Expr End)
- EVM.SymExec: isLoopHead :: LoopHeuristic -> VM -> Maybe Bool
+ EVM.SymExec: isLoopHead :: LoopHeuristic -> VM s -> Maybe Bool
- EVM.SymExec: loadSymVM :: ContractCode -> Expr Storage -> Expr EWord -> Expr EWord -> (Expr Buf, [Prop]) -> VM
+ EVM.SymExec: loadSymVM :: ContractCode -> Expr EWord -> (Expr Buf, [Prop]) -> Bool -> ST s (VM s)
- EVM.SymExec: maxIterationsReached :: VM -> Maybe Integer -> Maybe Bool
+ EVM.SymExec: maxIterationsReached :: VM s -> Maybe Integer -> Maybe Bool
- EVM.SymExec: runExpr :: Stepper (Expr End)
+ EVM.SymExec: runExpr :: Stepper RealWorld (Expr End)
- EVM.SymExec: type Postcondition = VM -> Expr End -> Prop
+ EVM.SymExec: type Postcondition s = VM s -> Expr End -> Prop
- EVM.SymExec: type Precondition = VM -> Prop
+ EVM.SymExec: type Precondition s = VM s -> Prop
- EVM.SymExec: verify :: SolverGroup -> VeriOpts -> VM -> Maybe Postcondition -> IO (Expr End, [VerifyResult])
+ EVM.SymExec: verify :: SolverGroup -> VeriOpts -> VM RealWorld -> Maybe (Postcondition RealWorld) -> IO (Expr End, [VerifyResult])
- EVM.SymExec: verifyContract :: SolverGroup -> ByteString -> Maybe Sig -> [String] -> VeriOpts -> Expr Storage -> Maybe Precondition -> Maybe Postcondition -> IO (Expr End, [VerifyResult])
+ EVM.SymExec: verifyContract :: SolverGroup -> ByteString -> Maybe Sig -> [String] -> VeriOpts -> Maybe (Precondition RealWorld) -> Maybe (Postcondition RealWorld) -> IO (Expr End, [VerifyResult])
- EVM.Transaction: accountAt :: Addr -> Getter (Map Addr Contract) Contract
+ EVM.Transaction: accountAt :: Expr EAddr -> Getter (Map (Expr EAddr) Contract) Contract
- EVM.Transaction: initTx :: VM -> VM
+ EVM.Transaction: initTx :: VM s -> VM s
- EVM.Transaction: setupTx :: Addr -> Addr -> W256 -> Word64 -> Map Addr Contract -> Map Addr Contract
+ EVM.Transaction: setupTx :: Expr EAddr -> Expr EAddr -> W256 -> Word64 -> Map (Expr EAddr) Contract -> Map (Expr EAddr) Contract
- EVM.Transaction: touchAccount :: Addr -> Map Addr Contract -> Map Addr Contract
+ EVM.Transaction: touchAccount :: Expr EAddr -> Map (Expr EAddr) Contract -> Map (Expr EAddr) Contract
- EVM.Types: BalanceTooLow :: W256 -> W256 -> EvmError
+ EVM.Types: BalanceTooLow :: Expr EWord -> Expr EWord -> EvmError
- EVM.Types: Block :: Addr -> Expr EWord -> W256 -> W256 -> Word64 -> W256 -> W256 -> FeeSchedule Word64 -> Block
+ EVM.Types: Block :: Expr EAddr -> Expr EWord -> W256 -> W256 -> Word64 -> W256 -> W256 -> FeeSchedule Word64 -> Block
- EVM.Types: Cache :: Map Addr Contract -> Map W256 (Map W256 W256) -> Map (CodeLocation, Int) Bool -> Cache
+ EVM.Types: Cache :: Map Addr Contract -> Map (CodeLocation, Int) Bool -> Cache
- EVM.Types: CallContext :: Addr -> Addr -> W256 -> W256 -> Expr EWord -> Maybe W256 -> Expr Buf -> (Map Addr Contract, Expr Storage) -> SubState -> FrameContext
+ EVM.Types: CallContext :: Expr EAddr -> Expr EAddr -> W256 -> W256 -> Expr EWord -> Maybe W256 -> Expr Buf -> Map (Expr EAddr) Contract -> SubState -> FrameContext
- EVM.Types: Choose :: Choose -> Effect
+ EVM.Types: Choose :: Choose s -> Effect s
- EVM.Types: Contract :: ContractCode -> W256 -> W256 -> Expr EWord -> Vector Int -> Vector (Int, Op) -> Bool -> Contract
+ EVM.Types: Contract :: ContractCode -> Expr Storage -> Expr Storage -> Expr EWord -> Maybe W64 -> Expr EWord -> Vector Int -> Vector (Int, Op) -> Bool -> Contract
- EVM.Types: CreationContext :: Addr -> Expr EWord -> Map Addr Contract -> SubState -> FrameContext
+ EVM.Types: CreationContext :: Expr EAddr -> Expr EWord -> Map (Expr EAddr) Contract -> SubState -> FrameContext
- EVM.Types: Env :: Map Addr Contract -> W256 -> Expr Storage -> Map W256 (Map W256 W256) -> Env
+ EVM.Types: Env :: Map (Expr EAddr) Contract -> W256 -> Int -> Env
- EVM.Types: Frame :: FrameContext -> FrameState -> Frame
+ EVM.Types: Frame :: FrameContext -> FrameState s -> Frame s
- EVM.Types: FrameState :: Addr -> Addr -> ContractCode -> {-# UNPACK #-} !Int -> [Expr EWord] -> Expr Buf -> Word64 -> Expr Buf -> Expr EWord -> Expr EWord -> {-# UNPACK #-} !Word64 -> Expr Buf -> Bool -> FrameState
+ EVM.Types: FrameState :: Expr EAddr -> Expr EAddr -> ContractCode -> {-# UNPACK #-} !Int -> [Expr EWord] -> Memory s -> Word64 -> Expr Buf -> Expr EWord -> Expr EAddr -> {-# UNPACK #-} !Word64 -> Expr Buf -> Bool -> FrameState s
- EVM.Types: HandleEffect :: Effect -> VMResult
+ EVM.Types: HandleEffect :: Effect s -> VMResult s
- EVM.Types: MaxIterationsReached :: Int -> Addr -> PartialExec
+ EVM.Types: MaxIterationsReached :: Int -> Expr EAddr -> PartialExec
- EVM.Types: Query :: Query -> Effect
+ EVM.Types: Query :: Query s -> Effect s
- EVM.Types: SubState :: [Addr] -> [Addr] -> Set Addr -> Set (Addr, W256) -> [(Addr, Word64)] -> SubState
+ EVM.Types: SubState :: [Expr EAddr] -> [Expr EAddr] -> Set (Expr EAddr) -> Set (Expr EAddr, W256) -> [(Expr EAddr, Word64)] -> SubState
- EVM.Types: Traces :: Forest Trace -> Map Addr Contract -> Traces
+ EVM.Types: Traces :: Forest Trace -> Map (Expr EAddr) Contract -> Traces
- EVM.Types: TxState :: W256 -> Word64 -> W256 -> Addr -> Addr -> Expr EWord -> SubState -> Bool -> Map Addr Contract -> TxState
+ EVM.Types: TxState :: W256 -> Word64 -> W256 -> Expr EAddr -> Expr EAddr -> Expr EWord -> SubState -> Bool -> Map (Expr EAddr) Contract -> TxState
- EVM.Types: Unfinished :: PartialExec -> VMResult
+ EVM.Types: Unfinished :: PartialExec -> VMResult s
- EVM.Types: VM :: Maybe VMResult -> FrameState -> [Frame] -> Env -> Block -> TxState -> [Expr Log] -> TreePos Empty Trace -> Cache -> {-# UNPACK #-} !Word64 -> Map CodeLocation (Int, [Expr EWord]) -> [Prop] -> [Prop] -> Bool -> Maybe Addr -> VM
+ EVM.Types: VM :: Maybe (VMResult s) -> FrameState s -> [Frame s] -> Env -> Block -> TxState -> [Expr Log] -> TreePos Empty Trace -> Cache -> {-# UNPACK #-} !Word64 -> Map CodeLocation (Int, [Expr EWord]) -> [Prop] -> [Prop] -> RuntimeConfig -> VM s
- EVM.Types: VMFailure :: EvmError -> VMResult
+ EVM.Types: VMFailure :: EvmError -> VMResult s
- EVM.Types: VMOpts :: Contract -> (Expr Buf, [Prop]) -> Expr Storage -> Expr EWord -> W256 -> Addr -> Expr EWord -> Addr -> Word64 -> Word64 -> W256 -> Expr EWord -> Addr -> W256 -> W256 -> Word64 -> W256 -> W256 -> FeeSchedule Word64 -> W256 -> Bool -> Map Addr [W256] -> Bool -> VMOpts
+ EVM.Types: VMOpts :: Contract -> [(Expr EAddr, Contract)] -> (Expr Buf, [Prop]) -> BaseState -> Expr EWord -> W256 -> Expr EAddr -> Expr EAddr -> Expr EAddr -> Word64 -> Word64 -> W256 -> Expr EWord -> Expr EAddr -> W256 -> W256 -> Word64 -> W256 -> W256 -> FeeSchedule Word64 -> W256 -> Bool -> Map (Expr EAddr) [W256] -> Bool -> VMOpts
- EVM.Types: VMSuccess :: Expr Buf -> VMResult
+ EVM.Types: VMSuccess :: Expr Buf -> VMResult s
- EVM.Types: [$sel:accessedAddresses:SubState] :: SubState -> Set Addr
+ EVM.Types: [$sel:accessedAddresses:SubState] :: SubState -> Set (Expr EAddr)
- EVM.Types: [$sel:accessedStorageKeys:SubState] :: SubState -> Set (Addr, W256)
+ EVM.Types: [$sel:accessedStorageKeys:SubState] :: SubState -> Set (Expr EAddr, W256)
- EVM.Types: [$sel:addr:UnexpectedSymbolicArg] :: PartialExec -> Addr
+ EVM.Types: [$sel:addr:UnexpectedSymbolicArg] :: PartialExec -> Expr EAddr
- EVM.Types: [$sel:address:CreationContext] :: FrameContext -> Addr
+ EVM.Types: [$sel:address:CreationContext] :: FrameContext -> Expr EAddr
- EVM.Types: [$sel:address:VMOpts] :: VMOpts -> Addr
+ EVM.Types: [$sel:address:VMOpts] :: VMOpts -> Expr EAddr
- EVM.Types: [$sel:balance:Contract] :: Contract -> W256
+ EVM.Types: [$sel:balance:Contract] :: Contract -> Expr EWord
- EVM.Types: [$sel:block:VM] :: VM -> Block
+ EVM.Types: [$sel:block:VM] :: VM s -> Block
- EVM.Types: [$sel:burned:VM] :: VM -> {-# UNPACK #-} !Word64
+ EVM.Types: [$sel:burned:VM] :: VM s -> {-# UNPACK #-} !Word64
- EVM.Types: [$sel:cache:VM] :: VM -> Cache
+ EVM.Types: [$sel:cache:VM] :: VM s -> Cache
- EVM.Types: [$sel:calldata:FrameState] :: FrameState -> Expr Buf
+ EVM.Types: [$sel:calldata:FrameState] :: FrameState s -> Expr Buf
- EVM.Types: [$sel:caller:FrameState] :: FrameState -> Expr EWord
+ EVM.Types: [$sel:caller:FrameState] :: FrameState s -> Expr EAddr
- EVM.Types: [$sel:caller:VMOpts] :: VMOpts -> Expr EWord
+ EVM.Types: [$sel:caller:VMOpts] :: VMOpts -> Expr EAddr
- EVM.Types: [$sel:callreversion:CreationContext] :: FrameContext -> (Map Addr Contract, Expr Storage)
+ EVM.Types: [$sel:callreversion:CreationContext] :: FrameContext -> Map (Expr EAddr) Contract
- EVM.Types: [$sel:callvalue:FrameState] :: FrameState -> Expr EWord
+ EVM.Types: [$sel:callvalue:FrameState] :: FrameState s -> Expr EWord
- EVM.Types: [$sel:code:FrameState] :: FrameState -> ContractCode
+ EVM.Types: [$sel:code:FrameState] :: FrameState s -> ContractCode
- EVM.Types: [$sel:codeContract:FrameState] :: FrameState -> Addr
+ EVM.Types: [$sel:codeContract:FrameState] :: FrameState s -> Expr EAddr
- EVM.Types: [$sel:coinbase:Block] :: Block -> Addr
+ EVM.Types: [$sel:coinbase:Block] :: Block -> Expr EAddr
- EVM.Types: [$sel:coinbase:VMOpts] :: VMOpts -> Addr
+ EVM.Types: [$sel:coinbase:VMOpts] :: VMOpts -> Expr EAddr
- EVM.Types: [$sel:constraints:VM] :: VM -> [Prop]
+ EVM.Types: [$sel:constraints:VM] :: VM s -> [Prop]
- EVM.Types: [$sel:context:CreationContext] :: FrameContext -> Addr
+ EVM.Types: [$sel:context:CreationContext] :: FrameContext -> Expr EAddr
- EVM.Types: [$sel:context:Frame] :: Frame -> FrameContext
+ EVM.Types: [$sel:context:Frame] :: Frame s -> FrameContext
- EVM.Types: [$sel:contract:FrameState] :: FrameState -> Addr
+ EVM.Types: [$sel:contract:FrameState] :: FrameState s -> Expr EAddr
- EVM.Types: [$sel:contracts:Env] :: Env -> Map Addr Contract
+ EVM.Types: [$sel:contracts:Env] :: Env -> Map (Expr EAddr) Contract
- EVM.Types: [$sel:contracts:Traces] :: Traces -> Map Addr Contract
+ EVM.Types: [$sel:contracts:Traces] :: Traces -> Map (Expr EAddr) Contract
- EVM.Types: [$sel:createreversion:CreationContext] :: FrameContext -> Map Addr Contract
+ EVM.Types: [$sel:createreversion:CreationContext] :: FrameContext -> Map (Expr EAddr) Contract
- EVM.Types: [$sel:env:VM] :: VM -> Env
+ EVM.Types: [$sel:env:VM] :: VM s -> Env
- EVM.Types: [$sel:frames:VM] :: VM -> [Frame]
+ EVM.Types: [$sel:frames:VM] :: VM s -> [Frame s]
- EVM.Types: [$sel:gas:FrameState] :: FrameState -> {-# UNPACK #-} !Word64
+ EVM.Types: [$sel:gas:FrameState] :: FrameState s -> {-# UNPACK #-} !Word64
- EVM.Types: [$sel:iterations:VM] :: VM -> Map CodeLocation (Int, [Expr EWord])
+ EVM.Types: [$sel:iterations:VM] :: VM s -> Map CodeLocation (Int, [Expr EWord])
- EVM.Types: [$sel:keccakEqs:VM] :: VM -> [Prop]
+ EVM.Types: [$sel:keccakEqs:VM] :: VM s -> [Prop]
- EVM.Types: [$sel:logs:VM] :: VM -> [Expr Log]
+ EVM.Types: [$sel:logs:VM] :: VM s -> [Expr Log]
- EVM.Types: [$sel:memory:FrameState] :: FrameState -> Expr Buf
+ EVM.Types: [$sel:memory:FrameState] :: FrameState s -> Memory s
- EVM.Types: [$sel:memorySize:FrameState] :: FrameState -> Word64
+ EVM.Types: [$sel:memorySize:FrameState] :: FrameState s -> Word64
- EVM.Types: [$sel:nonce:Contract] :: Contract -> W256
+ EVM.Types: [$sel:nonce:Contract] :: Contract -> Maybe W64
- EVM.Types: [$sel:origin:TxState] :: TxState -> Addr
+ EVM.Types: [$sel:origin:TxState] :: TxState -> Expr EAddr
- EVM.Types: [$sel:origin:VMOpts] :: VMOpts -> Addr
+ EVM.Types: [$sel:origin:VMOpts] :: VMOpts -> Expr EAddr
- EVM.Types: [$sel:pc:FrameState] :: FrameState -> {-# UNPACK #-} !Int
+ EVM.Types: [$sel:pc:FrameState] :: FrameState s -> {-# UNPACK #-} !Int
- EVM.Types: [$sel:refunds:SubState] :: SubState -> [(Addr, Word64)]
+ EVM.Types: [$sel:refunds:SubState] :: SubState -> [(Expr EAddr, Word64)]
- EVM.Types: [$sel:result:VM] :: VM -> Maybe VMResult
+ EVM.Types: [$sel:result:VM] :: VM s -> Maybe (VMResult s)
- EVM.Types: [$sel:returndata:FrameState] :: FrameState -> Expr Buf
+ EVM.Types: [$sel:returndata:FrameState] :: FrameState s -> Expr Buf
- EVM.Types: [$sel:selfdestructs:SubState] :: SubState -> [Addr]
+ EVM.Types: [$sel:selfdestructs:SubState] :: SubState -> [Expr EAddr]
- EVM.Types: [$sel:stack:FrameState] :: FrameState -> [Expr EWord]
+ EVM.Types: [$sel:stack:FrameState] :: FrameState s -> [Expr EWord]
- EVM.Types: [$sel:state:Frame] :: Frame -> FrameState
+ EVM.Types: [$sel:state:Frame] :: Frame s -> FrameState s
- EVM.Types: [$sel:state:VM] :: VM -> FrameState
+ EVM.Types: [$sel:state:VM] :: VM s -> FrameState s
- EVM.Types: [$sel:static:FrameState] :: FrameState -> Bool
+ EVM.Types: [$sel:static:FrameState] :: FrameState s -> Bool
- EVM.Types: [$sel:target:CreationContext] :: FrameContext -> Addr
+ EVM.Types: [$sel:target:CreationContext] :: FrameContext -> Expr EAddr
- EVM.Types: [$sel:toAddr:TxState] :: TxState -> Addr
+ EVM.Types: [$sel:toAddr:TxState] :: TxState -> Expr EAddr
- EVM.Types: [$sel:touchedAccounts:SubState] :: SubState -> [Addr]
+ EVM.Types: [$sel:touchedAccounts:SubState] :: SubState -> [Expr EAddr]
- EVM.Types: [$sel:traces:VM] :: VM -> TreePos Empty Trace
+ EVM.Types: [$sel:traces:VM] :: VM s -> TreePos Empty Trace
- EVM.Types: [$sel:tx:VM] :: VM -> TxState
+ EVM.Types: [$sel:tx:VM] :: VM s -> TxState
- EVM.Types: [$sel:txAccessList:VMOpts] :: VMOpts -> Map Addr [W256]
+ EVM.Types: [$sel:txAccessList:VMOpts] :: VMOpts -> Map (Expr EAddr) [W256]
- EVM.Types: [$sel:txReversion:TxState] :: TxState -> Map Addr Contract
+ EVM.Types: [$sel:txReversion:TxState] :: TxState -> Map (Expr EAddr) Contract
- EVM.Types: [AbstractStore] :: Expr Storage
+ EVM.Types: [AbstractStore] :: Expr EAddr -> Expr Storage
- EVM.Types: [Balance] :: Int -> Int -> Expr EWord -> Expr EWord
+ EVM.Types: [Balance] :: Expr EAddr -> Expr EWord
- EVM.Types: [CodeSize] :: Expr EWord -> Expr EWord
+ EVM.Types: [CodeSize] :: Expr EAddr -> Expr EWord
- EVM.Types: [ConcreteStore] :: Map W256 (Map W256 W256) -> Expr Storage
+ EVM.Types: [ConcreteStore] :: Map W256 W256 -> Expr Storage
- EVM.Types: [PleaseAskSMT] :: Expr EWord -> [Prop] -> (BranchCondition -> EVM ()) -> Query
+ EVM.Types: [PleaseAskSMT] :: Expr EWord -> [Prop] -> (BranchCondition -> EVM s ()) -> Query s
- EVM.Types: [PleaseChoosePath] :: Expr EWord -> (Bool -> EVM ()) -> Choose
+ EVM.Types: [PleaseChoosePath] :: Expr EWord -> (Bool -> EVM s ()) -> Choose s
- EVM.Types: [PleaseDoFFI] :: [String] -> (ByteString -> EVM ()) -> Query
+ EVM.Types: [PleaseDoFFI] :: [String] -> (ByteString -> EVM s ()) -> Query s
- EVM.Types: [PleaseFetchContract] :: Addr -> (Contract -> EVM ()) -> Query
+ EVM.Types: [PleaseFetchContract] :: Addr -> BaseState -> (Contract -> EVM s ()) -> Query s
- EVM.Types: [PleaseFetchSlot] :: Addr -> W256 -> (W256 -> EVM ()) -> Query
+ EVM.Types: [PleaseFetchSlot] :: Addr -> W256 -> (W256 -> EVM s ()) -> Query s
- EVM.Types: [SLoad] :: Expr EWord -> Expr EWord -> Expr Storage -> Expr EWord
+ EVM.Types: [SLoad] :: Expr EWord -> Expr Storage -> Expr EWord
- EVM.Types: [SStore] :: Expr EWord -> Expr EWord -> Expr EWord -> Expr Storage -> Expr Storage
+ EVM.Types: [SStore] :: Expr EWord -> Expr EWord -> Expr Storage -> Expr Storage
- EVM.Types: [Success] :: [Prop] -> Traces -> Expr Buf -> Expr Storage -> Expr End
+ EVM.Types: [Success] :: [Prop] -> Traces -> Expr Buf -> Map (Expr EAddr) (Expr EContract) -> Expr End
- EVM.Types: data Choose
+ EVM.Types: data Choose s
- EVM.Types: data Effect
+ EVM.Types: data Effect s
- EVM.Types: data Frame
+ EVM.Types: data Frame s
- EVM.Types: data FrameState
+ EVM.Types: data FrameState s
- EVM.Types: data Query
+ EVM.Types: data Query s
- EVM.Types: data VM
+ EVM.Types: data VM s
- EVM.Types: data VMResult
+ EVM.Types: data VMResult s
- EVM.Types: type CodeLocation = (Addr, Int)
+ EVM.Types: type CodeLocation = (Expr EAddr, Int)
- EVM.Types: type EVM a = State VM a
+ EVM.Types: type EVM s a = StateT (VM s) (ST s) a
- EVM.UnitTest: TestVMParams :: Addr -> Addr -> Addr -> Word64 -> Word64 -> W256 -> W256 -> W256 -> Addr -> W256 -> W256 -> Word64 -> W256 -> W256 -> W256 -> W256 -> TestVMParams
+ EVM.UnitTest: TestVMParams :: Expr EAddr -> Expr EAddr -> Expr EAddr -> Word64 -> Word64 -> W256 -> W256 -> W256 -> Expr EAddr -> W256 -> W256 -> Word64 -> W256 -> W256 -> W256 -> W256 -> TestVMParams
- EVM.UnitTest: UnitTestOptions :: RpcInfo -> SolverGroup -> Maybe Int -> Maybe Integer -> Integer -> Bool -> Maybe Int -> Maybe Natural -> Maybe Text -> Maybe Text -> Text -> Int -> Maybe (Text, ByteString) -> (VM -> VM) -> DappInfo -> TestVMParams -> Bool -> UnitTestOptions
+ EVM.UnitTest: UnitTestOptions :: RpcInfo -> SolverGroup -> Maybe Int -> Maybe Integer -> Integer -> Bool -> Maybe Natural -> Maybe Text -> Text -> DappInfo -> TestVMParams -> Bool -> UnitTestOptions s
- EVM.UnitTest: [$sel:address:TestVMParams] :: TestVMParams -> Addr
+ EVM.UnitTest: [$sel:address:TestVMParams] :: TestVMParams -> Expr EAddr
- EVM.UnitTest: [$sel:askSmtIters:UnitTestOptions] :: UnitTestOptions -> Integer
+ EVM.UnitTest: [$sel:askSmtIters:UnitTestOptions] :: UnitTestOptions s -> Integer
- EVM.UnitTest: [$sel:caller:TestVMParams] :: TestVMParams -> Addr
+ EVM.UnitTest: [$sel:caller:TestVMParams] :: TestVMParams -> Expr EAddr
- EVM.UnitTest: [$sel:coinbase:TestVMParams] :: TestVMParams -> Addr
+ EVM.UnitTest: [$sel:coinbase:TestVMParams] :: TestVMParams -> Expr EAddr
- EVM.UnitTest: [$sel:dapp:UnitTestOptions] :: UnitTestOptions -> DappInfo
+ EVM.UnitTest: [$sel:dapp:UnitTestOptions] :: UnitTestOptions s -> DappInfo
- EVM.UnitTest: [$sel:ffiAllowed:UnitTestOptions] :: UnitTestOptions -> Bool
+ EVM.UnitTest: [$sel:ffiAllowed:UnitTestOptions] :: UnitTestOptions s -> Bool
- EVM.UnitTest: [$sel:match:UnitTestOptions] :: UnitTestOptions -> Text
+ EVM.UnitTest: [$sel:match:UnitTestOptions] :: UnitTestOptions s -> Text
- EVM.UnitTest: [$sel:maxIter:UnitTestOptions] :: UnitTestOptions -> Maybe Integer
+ EVM.UnitTest: [$sel:maxIter:UnitTestOptions] :: UnitTestOptions s -> Maybe Integer
- EVM.UnitTest: [$sel:origin:TestVMParams] :: TestVMParams -> Addr
+ EVM.UnitTest: [$sel:origin:TestVMParams] :: TestVMParams -> Expr EAddr
- EVM.UnitTest: [$sel:rpcInfo:UnitTestOptions] :: UnitTestOptions -> RpcInfo
+ EVM.UnitTest: [$sel:rpcInfo:UnitTestOptions] :: UnitTestOptions s -> RpcInfo
- EVM.UnitTest: [$sel:smtDebug:UnitTestOptions] :: UnitTestOptions -> Bool
+ EVM.UnitTest: [$sel:smtDebug:UnitTestOptions] :: UnitTestOptions s -> Bool
- EVM.UnitTest: [$sel:smtTimeout:UnitTestOptions] :: UnitTestOptions -> Maybe Natural
+ EVM.UnitTest: [$sel:smtTimeout:UnitTestOptions] :: UnitTestOptions s -> Maybe Natural
- EVM.UnitTest: [$sel:solver:UnitTestOptions] :: UnitTestOptions -> Maybe Text
+ EVM.UnitTest: [$sel:solver:UnitTestOptions] :: UnitTestOptions s -> Maybe Text
- EVM.UnitTest: [$sel:solvers:UnitTestOptions] :: UnitTestOptions -> SolverGroup
+ EVM.UnitTest: [$sel:solvers:UnitTestOptions] :: UnitTestOptions s -> SolverGroup
- EVM.UnitTest: [$sel:testParams:UnitTestOptions] :: UnitTestOptions -> TestVMParams
+ EVM.UnitTest: [$sel:testParams:UnitTestOptions] :: UnitTestOptions s -> TestVMParams
- EVM.UnitTest: [$sel:verbose:UnitTestOptions] :: UnitTestOptions -> Maybe Int
+ EVM.UnitTest: [$sel:verbose:UnitTestOptions] :: UnitTestOptions s -> Maybe Int
- EVM.UnitTest: abiCall :: TestVMParams -> Either (Text, AbiValue) ByteString -> EVM ()
+ EVM.UnitTest: abiCall :: TestVMParams -> Either (Text, AbiValue) ByteString -> EVM s ()
- EVM.UnitTest: checkSymFailures :: UnitTestOptions -> Stepper VM
+ EVM.UnitTest: checkSymFailures :: UnitTestOptions RealWorld -> Stepper RealWorld (VM RealWorld)
- EVM.UnitTest: data UnitTestOptions
+ EVM.UnitTest: data UnitTestOptions s
- EVM.UnitTest: execSymTest :: UnitTestOptions -> ABIMethod -> (Expr Buf, [Prop]) -> Stepper (Expr End)
+ EVM.UnitTest: execSymTest :: UnitTestOptions RealWorld -> ABIMethod -> (Expr Buf, [Prop]) -> Stepper RealWorld (Expr End)
- EVM.UnitTest: failOutput :: VM -> UnitTestOptions -> Text -> Text
+ EVM.UnitTest: failOutput :: VM s -> UnitTestOptions s -> Text -> Text
- EVM.UnitTest: initialUnitTestVm :: UnitTestOptions -> SolcContract -> VM
+ EVM.UnitTest: initialUnitTestVm :: UnitTestOptions s -> SolcContract -> ST s (VM s)
- EVM.UnitTest: initializeUnitTest :: UnitTestOptions -> SolcContract -> Stepper ()
+ EVM.UnitTest: initializeUnitTest :: UnitTestOptions s -> SolcContract -> Stepper s ()
- EVM.UnitTest: makeTxCall :: TestVMParams -> (Expr Buf, [Prop]) -> EVM ()
+ EVM.UnitTest: makeTxCall :: TestVMParams -> (Expr Buf, [Prop]) -> EVM s ()
- EVM.UnitTest: makeVeriOpts :: UnitTestOptions -> VeriOpts
+ EVM.UnitTest: makeVeriOpts :: UnitTestOptions s -> VeriOpts
- EVM.UnitTest: passOutput :: VM -> UnitTestOptions -> Text -> Text
+ EVM.UnitTest: passOutput :: VM s -> UnitTestOptions s -> Text -> Text
- EVM.UnitTest: runUnitTestContract :: UnitTestOptions -> Map Text SolcContract -> (Text, [(Test, [AbiType])]) -> IO [(Bool, VM)]
+ EVM.UnitTest: runUnitTestContract :: UnitTestOptions RealWorld -> Map Text SolcContract -> (Text, [Sig]) -> IO [Bool]
- EVM.UnitTest: symFailure :: UnitTestOptions -> Text -> Expr Buf -> [AbiType] -> [(Expr End, SMTCex)] -> Text
+ EVM.UnitTest: symFailure :: UnitTestOptions RealWorld -> Text -> Expr Buf -> [AbiType] -> [(Expr End, SMTCex)] -> Text
- EVM.UnitTest: symRun :: UnitTestOptions -> VM -> Text -> [AbiType] -> IO (Text, Either Text Text, VM)
+ EVM.UnitTest: symRun :: UnitTestOptions RealWorld -> VM RealWorld -> Sig -> IO (Text, Either Text Text)
- EVM.UnitTest: unitTest :: UnitTestOptions -> Contracts -> Maybe String -> IO Bool
+ EVM.UnitTest: unitTest :: UnitTestOptions RealWorld -> Contracts -> IO Bool

Files

CHANGELOG.md view
@@ -5,6 +5,119 @@ 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). +## [0.52.0] - 2023-10-26++This is a major breaking release that removes several user facing features and includes non trivial+breakage for library users. These changes mean the code is significantly simpler, more performant,+and allow support for new features like fully symbolic addresses.++In addition to the changes below, this release includes significant work on performance+optimization for symbolic execution.++## Added++The major new user facing feature in this release is support for fully symbolic addresses (including+fully symbolic addresses for deployed contracts). This allows tests to be writen that call+`vm.prank` with a symbolic value, making some tests (e.g. access control, token transfer logic) much+more comprehensive.++Some restrictions around reading balances from and transfering value between symbolic addresses are+currently in place. Currently, if the address is symbolic, then you will only be able to read it's+balance, or transfer value to/from it, if it is the address of a contract that is actually deployed.+This is required to ensure soundness in the face of aliasing between symbolic addresses. We intend+to lift this restriction in a future release.++### Other++- Support for `vm.deal`+- Support for `vm.assume` (this is semantically identical to using `require`, but does simplify the+    process of porting exisitng fuzz tests to be symbolic)+- the `check` prefix now recognized for symbolic tests+- `hevm test` now takes a `--number` argument to specify which block should be used when making rpc queries++## Changed++### Revert Semantics in Solidity Tests++solidity tests no longer consider reverts to be a failure, and check only for the ds-test failed bit+or user defined assertion failures (i.e. `Panic(0x01)`). This makes writing tests much easier as+users no longer have to consider trivial reverts (e.g. arithmetic overflow).++A positive (i.e. `prove`/`check`) test with no rechable assertion violations that does not have any+succesful branches will still be considered a failure.++## Removed++hevm has been around for a while, and over time has accumulated many features. We decided to remove+some of these features in the interest of focusing our attention, increasing our iteration speed and+simplifying maintainance. The following user facing features have been removed from this release:++- The visual debugger has been removed+- All concrete ds-test executors have been removed (i.e. plain, fuzzer, invariant)+- Rpc caching and state serialization has been removed (i.e. all `--cache` / `--state` flags)+- The various `DAPP_TEST` variables are no longer observed+- The `--initial-storage` flag no longer accepts a concrete prestore (valid values are now `Empty` or `Abstract`)++## Fixed++This release also includes many small bugfixes:++- CopySlice wraparound issue especially during CopyCallBytesToMemory+- Contracts deployed during symbolic execution are created with an empty storage (instead of abstract in previous versions)+- EVM.Solidity.toCode to include contractName in error string+- Better cex reconstruction in cases where branches do not refer to all input variables in calldata+- Correctly handle empty bytestring compiled contracts' JSON+- No more false positives when keccak is called with inputs of different sizes+- `test` now falls back to displaying an unecoded bytestring for calldata when the model returned by the solver has a different length the length of the arguments in the test signature.+- we now generate correct counterexamples for branches where only a subset of input variables are referenced by the path conditions+- `vm.prank` now works correctly when passed a symbolic address+- storage layout information will now be parsed from the output of `forge build` if it is available++## API Changes++### Reworked Storage Model / Symbolic Addresses++Adding symbolic addresses required some fairly significant changes to the way that we model storage.+We introduced a new address type to `Expr` (`Expr EAddr`), that allows us to model fully symbolic+addresses. Instead of modelling storage as a global symbolic 2D map (`address -> word -> word`) in+`vm.env`, each contract now has it's own symbolic 1D map (`word -> word`), that is stored in the+`vm.contracts` mapping. `vm.contracts` is now keyed on `Expr EAddr` instead of `Addr`. Addresses+that are keys to the `vm.contracts` mapping are asserted to be distinct in our smt encoding. This+allows us to support symbolic addresses in a fully static manner (i.e. we do not currently need to+make any extra smt queries when looking up storage for a symbolic address).++### Mutable Memory & ST++We now use a mutable representation of memory if it is currently completely concrete. This is a+significant performance improvement, and fixed a particulary egregious memory leak. It does entail+the use of the `ST` monad, and introduces a new type parameter to the `VM` type that tags a given+instance with it's thread local state. Library users will now need to either use the ST moand and+`runST` or `stToIO` to compose and sequence vm executions.++## GHC 9.4++Hevm is now built with ghc9.4. While earlier compiler versions may continue to work for now, they+are no longer explicitly tested or supported.++### Other++- Contract balances can now be fully symbolic+- Contract code can now be fully abstract. Calls into contracts with unknown code will fail with `UnexpectedSymbolicArg`.+- Run expression simplification on branch conditions+- SLoad/SStore simplifications based on assumptions regarding Keccak non-collision&preimage+- Improved Prop simplification+- CopySlice+WriteWord+ConcreteBuf now truncates ConcreteBuf in special cases+- Better simplification of Eq IR elements+- Run a toplevel constant folding reasoning system on branch conditions+- `evalProp` is renamed to `simplifyProp` for consistency+- Mem explosion in `writeWord` function was possible in case `offset` was close to 2^256. Fixed.+- BufLength was not simplified via bufLength function. Fixed.+- `VMOpts` no longer takes an initial store, and instead takes a `baseState`+  which can be either `EmptyBase` or `AbstractBase`. This controls whether+  storage should be inialized as empty or fully abstract. Regardless of this+  setting contracts that are deployed at runtime via a call to+  `CREATE`/`CREATE2` have zero initialized storage.+ ## [0.51.3] - 2023-07-14  ## Fixed@@ -16,6 +129,7 @@ ## Changed  - Removed sha3Crack which has been deprecated for keccakEqs+- Abstraction-refinement for more complicated expressions such as MULMOD  ## Added 
bench/bench.hs view
@@ -3,30 +3,23 @@ import GHC.Natural import Control.Monad import Data.Maybe-import System.Environment (lookupEnv, getEnv)+import System.Environment (getEnv)  import qualified Paths_hevm as Paths  import Test.Tasty (localOption, withResource) import Test.Tasty.Bench-import Data.Functor-import Data.String.Here import Data.ByteString (ByteString) import System.FilePath.Posix-import Control.Monad.State.Strict import qualified Data.Map.Strict as Map import qualified Data.Text as T import qualified System.FilePath.Find as Find import qualified Data.ByteString.Lazy as LazyByteString -import EVM (StorageModel(..)) import EVM.SymExec import EVM.Solidity import EVM.Solvers-import EVM.ABI-import EVM.Dapp-import EVM.Types-import qualified EVM.TTY as TTY+import EVM.Format (hexByteString) import qualified EVM.Stepper as Stepper import qualified EVM.Fetch as Fetch @@ -34,10 +27,10 @@  main :: IO () main = defaultMain-  [ mkbench erc20 "erc20" Nothing [1]-  , mkbench (pure vat) "vat" Nothing [4]-  , mkbench (pure deposit) "deposit" (Just 32) [4]-  , mkbench (pure uniV2Pair) "uniV2" (Just 10) [4]+  [ mkbench erc20 "erc20" 0 [1]+  , mkbench (pure vat) "vat" 0 [4]+  , mkbench (pure deposit) "deposit" 32 [4]+  , mkbench (pure uniV2Pair) "uniV2" 10 [4]   , withResource bcjsons (pure . const ()) blockchainTests   ] @@ -59,7 +52,7 @@     parseSuite path = do       contents <- LazyByteString.readFile path       case BCTests.parseBCSuite contents of-        Left e -> pure (path, mempty)+        Left _ -> pure (path, mempty)         Right tests -> pure (path, tests)  -- | executes all provided bc tests in sequence and accumulates a boolean value representing their success.@@ -67,7 +60,7 @@ blockchainTests :: IO (Map.Map FilePath (Map.Map String BCTests.Case)) -> Benchmark blockchainTests ts = bench "blockchain-tests" $ nfIO $ do   tests <- ts-  putStrLn "\n    executing tests:"+  putStrLn "    executing blockchain tests"   let cases = concat . Map.elems . (fmap Map.toList) $ tests       ignored = Map.keys BCTests.commonProblematicTests   foldM (\acc (n, c) ->@@ -75,7 +68,6 @@       then pure True       else do         res <- runBCTest c-        putStrLn $ "      " <> n         pure $ acc && res     ) True cases @@ -83,8 +75,8 @@ runBCTest :: BCTests.Case -> IO Bool runBCTest x =  do-  let vm0 = BCTests.vmForCase x-  result <- execStateT (Stepper.interpret (Fetch.zero 0 (Just 0)) . void $ Stepper.execFully) vm0+  vm0 <- BCTests.vmForCase x+  result <- Stepper.interpret (Fetch.zero 0 Nothing) vm0 Stepper.runFully   maybeReason <- BCTests.checkExpectation False x result   pure $ isNothing maybeReason @@ -92,17 +84,12 @@ --- Helpers ----------------------------------------------------------------------------------------  -debugContract :: ByteString -> IO ()-debugContract c = withSolvers CVC5 4 Nothing $ \solvers -> do-  let prestate = abstractVM (mkCalldata Nothing []) c Nothing SymbolicS-  void $ TTY.runFromVM solvers Nothing Nothing emptyDapp prestate--findPanics :: Solver -> Natural -> Maybe Integer -> ByteString -> IO ()+findPanics :: Solver -> Natural -> Integer -> ByteString -> IO () findPanics solver count iters c = do-  (_, res) <- withSolvers solver count Nothing $ \s -> do+  _ <- withSolvers solver count Nothing $ \s -> do     let opts = defaultVeriOpts-          { maxIter = iters-          , askSmtIters = (+ 1) <$> iters+          { maxIter = Just iters+          , askSmtIters = iters + 1           }     checkAssert s allPanicCodes c Nothing [] opts   putStrLn "done"@@ -112,7 +99,7 @@ -- assertion violations takes an iteration bound, as well as a list of solver -- counts to benchmark, allowing us to construct benchmarks that compare the -- performance impact of increasing solver parallelisation-mkbench :: IO ByteString -> String -> Maybe Integer -> [Natural] -> Benchmark+mkbench :: IO ByteString -> String -> Integer -> [Natural] -> Benchmark mkbench c name iters counts = localOption WallTime $ env c (bgroup name . bmarks)   where     bmarks c' = concat $ [
+ bench/contracts/deposit.sol view
@@ -0,0 +1,182 @@+/**+ *Submitted for verification at Etherscan.io on 2020-10-14+*/++// ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━+// ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓+// ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛+// ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━+// ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓+// ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛+// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━+// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━++// SPDX-License-Identifier: CC0-1.0++pragma solidity 0.6.11;++// This interface is designed to be compatible with the Vyper version.+/// @notice This is the Ethereum 2.0 deposit contract interface.+/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs+interface IDepositContract {+    /// @notice A processed deposit event.+    event DepositEvent(+        bytes pubkey,+        bytes withdrawal_credentials,+        bytes amount,+        bytes signature,+        bytes index+    );++    /// @notice Submit a Phase 0 DepositData object.+    /// @param pubkey A BLS12-381 public key.+    /// @param withdrawal_credentials Commitment to a public key for withdrawals.+    /// @param signature A BLS12-381 signature.+    /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.+    /// Used as a protection against malformed input.+    function deposit(+        bytes calldata pubkey,+        bytes calldata withdrawal_credentials,+        bytes calldata signature,+        bytes32 deposit_data_root+    ) external payable;++    /// @notice Query the current deposit root hash.+    /// @return The deposit root hash.+    function get_deposit_root() external view returns (bytes32);++    /// @notice Query the current deposit count.+    /// @return The deposit count encoded as a little endian 64-bit number.+    function get_deposit_count() external view returns (bytes memory);+}++// Based on official specification in https://eips.ethereum.org/EIPS/eip-165+interface ERC165 {+    /// @notice Query if a contract implements an interface+    /// @param interfaceId The interface identifier, as specified in ERC-165+    /// @dev Interface identification is specified in ERC-165. This function+    ///  uses less than 30,000 gas.+    /// @return `true` if the contract implements `interfaceId` and+    ///  `interfaceId` is not 0xffffffff, `false` otherwise+    function supportsInterface(bytes4 interfaceId) external pure returns (bool);+}++// This is a rewrite of the Vyper Eth2.0 deposit contract in Solidity.+// It tries to stay as close as possible to the original source code.+/// @notice This is the Ethereum 2.0 deposit contract interface.+/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs+contract DepositContract is IDepositContract, ERC165 {+    uint constant DEPOSIT_CONTRACT_TREE_DEPTH = 32;+    // NOTE: this also ensures `deposit_count` will fit into 64-bits+    uint constant MAX_DEPOSIT_COUNT = 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1;++    bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] branch;+    uint256 deposit_count;++    bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] zero_hashes;++    constructor() public {+        // Compute hashes in empty sparse Merkle tree+        for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH - 1; height++)+            zero_hashes[height + 1] = sha256(abi.encodePacked(zero_hashes[height], zero_hashes[height]));+    }++    function get_deposit_root() override external view returns (bytes32) {+        bytes32 node;+        uint size = deposit_count;+        for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {+            if ((size & 1) == 1)+                node = sha256(abi.encodePacked(branch[height], node));+            else+                node = sha256(abi.encodePacked(node, zero_hashes[height]));+            size /= 2;+        }+        return sha256(abi.encodePacked(+            node,+            to_little_endian_64(uint64(deposit_count)),+            bytes24(0)+        ));+    }++    function get_deposit_count() override external view returns (bytes memory) {+        return to_little_endian_64(uint64(deposit_count));+    }++    function deposit(+        bytes calldata pubkey,+        bytes calldata withdrawal_credentials,+        bytes calldata signature,+        bytes32 deposit_data_root+    ) override external payable {+        // Extended ABI length checks since dynamic types are used.+        require(pubkey.length == 48, "DepositContract: invalid pubkey length");+        require(withdrawal_credentials.length == 32, "DepositContract: invalid withdrawal_credentials length");+        require(signature.length == 96, "DepositContract: invalid signature length");++        // Check deposit amount+        require(msg.value >= 1 ether, "DepositContract: deposit value too low");+        require(msg.value % 1 gwei == 0, "DepositContract: deposit value not multiple of gwei");+        uint deposit_amount = msg.value / 1 gwei;+        require(deposit_amount <= type(uint64).max, "DepositContract: deposit value too high");++        // Emit `DepositEvent` log+        bytes memory amount = to_little_endian_64(uint64(deposit_amount));+        emit DepositEvent(+            pubkey,+            withdrawal_credentials,+            amount,+            signature,+            to_little_endian_64(uint64(deposit_count))+        );++        // Compute deposit data root (`DepositData` hash tree root)+        bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));+        bytes32 signature_root = sha256(abi.encodePacked(+            sha256(abi.encodePacked(signature[:64])),+            sha256(abi.encodePacked(signature[64:], bytes32(0)))+        ));+        bytes32 node = sha256(abi.encodePacked(+            sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),+            sha256(abi.encodePacked(amount, bytes24(0), signature_root))+        ));++        // Verify computed and expected deposit data roots match+        require(node == deposit_data_root, "DepositContract: reconstructed DepositData does not match supplied deposit_data_root");++        // Avoid overflowing the Merkle tree (and prevent edge case in computing `branch`)+        require(deposit_count < MAX_DEPOSIT_COUNT, "DepositContract: merkle tree full");++        // Add deposit data root to Merkle tree (update a single `branch` node)+        deposit_count += 1;+        uint size = deposit_count;+        for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {+            if ((size & 1) == 1) {+                branch[height] = node;+                return;+            }+            node = sha256(abi.encodePacked(branch[height], node));+            size /= 2;+        }+        // As the loop should always end prematurely with the `return` statement,+        // this code should be unreachable. We assert `false` just to be safe.+        assert(false);+    }++    function supportsInterface(bytes4 interfaceId) override external pure returns (bool) {+        return interfaceId == type(ERC165).interfaceId || interfaceId == type(IDepositContract).interfaceId;+    }++    function to_little_endian_64(uint64 value) internal pure returns (bytes memory ret) {+        ret = new bytes(8);+        bytes8 bytesValue = bytes8(value);+        // Byteswapping during copying to bytes.+        ret[0] = bytesValue[7];+        ret[1] = bytesValue[6];+        ret[2] = bytesValue[5];+        ret[3] = bytesValue[4];+        ret[4] = bytesValue[3];+        ret[5] = bytesValue[2];+        ret[6] = bytesValue[1];+        ret[7] = bytesValue[0];+    }+}
+ bench/contracts/vat.sol view
@@ -0,0 +1,246 @@+// SPDX-License-Identifier: AGPL-3.0-or-later++/// vat.sol -- Dai CDP database++// Copyright (C) 2018 Rain <rainbreak@riseup.net>+//+// This program is free software: you can redistribute it and/or modify+// it under the terms of the GNU Affero General Public License as published by+// the Free Software Foundation, either version 3 of the License, or+// (at your option) any later version.+//+// This program is distributed in the hope that it will be useful,+// but WITHOUT ANY WARRANTY; without even the implied warranty of+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+// GNU Affero General Public License for more details.+//+// You should have received a copy of the GNU Affero General Public License+// along with this program.  If not, see <https://www.gnu.org/licenses/>.++pragma solidity ^0.6.12;++// FIXME: This contract was altered compared to the production version.+// It doesn't use LibNote anymore.+// New deployments of this contract will need to include custom events (TO DO).++contract Vat {+    // --- Auth ---+    mapping (address => uint) public wards;+    function rely(address usr) external auth { require(live == 1, "Vat/not-live"); wards[usr] = 1; }+    function deny(address usr) external auth { require(live == 1, "Vat/not-live"); wards[usr] = 0; }+    modifier auth {+        require(wards[msg.sender] == 1, "Vat/not-authorized");+        _;+    }++    mapping(address => mapping (address => uint)) public can;+    function hope(address usr) external { can[msg.sender][usr] = 1; }+    function nope(address usr) external { can[msg.sender][usr] = 0; }+    function wish(address bit, address usr) internal view returns (bool) {+        return either(bit == usr, can[bit][usr] == 1);+    }++    // --- Data ---+    struct Ilk {+        uint256 Art;   // Total Normalised Debt     [wad]+        uint256 rate;  // Accumulated Rates         [ray]+        uint256 spot;  // Price with Safety Margin  [ray]+        uint256 line;  // Debt Ceiling              [rad]+        uint256 dust;  // Urn Debt Floor            [rad]+    }+    struct Urn {+        uint256 ink;   // Locked Collateral  [wad]+        uint256 art;   // Normalised Debt    [wad]+    }++    mapping (bytes32 => Ilk)                       public ilks;+    mapping (bytes32 => mapping (address => Urn )) public urns;+    mapping (bytes32 => mapping (address => uint)) public gem;  // [wad]+    mapping (address => uint256)                   public dai;  // [rad]+    mapping (address => uint256)                   public sin;  // [rad]++    uint256 public debt;  // Total Dai Issued    [rad]+    uint256 public vice;  // Total Unbacked Dai  [rad]+    uint256 public Line;  // Total Debt Ceiling  [rad]+    uint256 public live;  // Active Flag++    // --- Init ---+    constructor() public {+        wards[msg.sender] = 1;+        live = 1;+    }++    // --- Math ---+    function _add(uint x, int y) internal pure returns (uint z) {+        z = x + uint(y);+        require(y >= 0 || z <= x);+        require(y <= 0 || z >= x);+    }+    function _sub(uint x, int y) internal pure returns (uint z) {+        z = x - uint(y);+        require(y <= 0 || z <= x);+        require(y >= 0 || z >= x);+    }+    function _mul(uint x, int y) internal pure returns (int z) {+        z = int(x) * y;+        require(int(x) >= 0);+        require(y == 0 || z / y == int(x));+    }+    function _add(uint x, uint y) internal pure returns (uint z) {+        require((z = x + y) >= x);+    }+    function _sub(uint x, uint y) internal pure returns (uint z) {+        require((z = x - y) <= x);+    }+    function _mul(uint x, uint y) internal pure returns (uint z) {+        require(y == 0 || (z = x * y) / y == x);+    }++    // --- Administration ---+    function init(bytes32 ilk) external auth {+        require(ilks[ilk].rate == 0, "Vat/ilk-already-init");+        ilks[ilk].rate = 10 ** 27;+    }+    function file(bytes32 what, uint data) external auth {+        require(live == 1, "Vat/not-live");+        if (what == "Line") Line = data;+        else revert("Vat/file-unrecognized-param");+    }+    function file(bytes32 ilk, bytes32 what, uint data) external auth {+        require(live == 1, "Vat/not-live");+        if (what == "spot") ilks[ilk].spot = data;+        else if (what == "line") ilks[ilk].line = data;+        else if (what == "dust") ilks[ilk].dust = data;+        else revert("Vat/file-unrecognized-param");+    }+    function cage() external auth {+        live = 0;+    }++    // --- Fungibility ---+    function slip(bytes32 ilk, address usr, int256 wad) external auth {+        gem[ilk][usr] = _add(gem[ilk][usr], wad);+    }+    function flux(bytes32 ilk, address src, address dst, uint256 wad) external {+        require(wish(src, msg.sender), "Vat/not-allowed");+        gem[ilk][src] = _sub(gem[ilk][src], wad);+        gem[ilk][dst] = _add(gem[ilk][dst], wad);+    }+    function move(address src, address dst, uint256 rad) external {+        require(wish(src, msg.sender), "Vat/not-allowed");+        dai[src] = _sub(dai[src], rad);+        dai[dst] = _add(dai[dst], rad);+    }++    function either(bool x, bool y) internal pure returns (bool z) {+        assembly{ z := or(x, y)}+    }+    function both(bool x, bool y) internal pure returns (bool z) {+        assembly{ z := and(x, y)}+    }++    // --- CDP Manipulation ---+    function frob(bytes32 i, address u, address v, address w, int dink, int dart) external {+        // system is live+        require(live == 1, "Vat/not-live");++        Urn memory urn = urns[i][u];+        Ilk memory ilk = ilks[i];+        // ilk has been initialised+        require(ilk.rate != 0, "Vat/ilk-not-init");++        urn.ink = _add(urn.ink, dink);+        urn.art = _add(urn.art, dart);+        ilk.Art = _add(ilk.Art, dart);++        int dtab = _mul(ilk.rate, dart);+        uint tab = _mul(ilk.rate, urn.art);+        debt     = _add(debt, dtab);++        // either debt has decreased, or debt ceilings are not exceeded+        require(either(dart <= 0, both(_mul(ilk.Art, ilk.rate) <= ilk.line, debt <= Line)), "Vat/ceiling-exceeded");+        // urn is either less risky than before, or it is safe+        require(either(both(dart <= 0, dink >= 0), tab <= _mul(urn.ink, ilk.spot)), "Vat/not-safe");++        // urn is either more safe, or the owner consents+        require(either(both(dart <= 0, dink >= 0), wish(u, msg.sender)), "Vat/not-allowed-u");+        // collateral src consents+        require(either(dink <= 0, wish(v, msg.sender)), "Vat/not-allowed-v");+        // debt dst consents+        require(either(dart >= 0, wish(w, msg.sender)), "Vat/not-allowed-w");++        // urn has no debt, or a non-dusty amount+        require(either(urn.art == 0, tab >= ilk.dust), "Vat/dust");++        gem[i][v] = _sub(gem[i][v], dink);+        dai[w]    = _add(dai[w],    dtab);++        urns[i][u] = urn;+        ilks[i]    = ilk;+    }+    // --- CDP Fungibility ---+    function fork(bytes32 ilk, address src, address dst, int dink, int dart) external {+        Urn storage u = urns[ilk][src];+        Urn storage v = urns[ilk][dst];+        Ilk storage i = ilks[ilk];++        u.ink = _sub(u.ink, dink);+        u.art = _sub(u.art, dart);+        v.ink = _add(v.ink, dink);+        v.art = _add(v.art, dart);++        uint utab = _mul(u.art, i.rate);+        uint vtab = _mul(v.art, i.rate);++        // both sides consent+        require(both(wish(src, msg.sender), wish(dst, msg.sender)), "Vat/not-allowed");++        // both sides safe+        require(utab <= _mul(u.ink, i.spot), "Vat/not-safe-src");+        require(vtab <= _mul(v.ink, i.spot), "Vat/not-safe-dst");++        // both sides non-dusty+        require(either(utab >= i.dust, u.art == 0), "Vat/dust-src");+        require(either(vtab >= i.dust, v.art == 0), "Vat/dust-dst");+    }+    // --- CDP Confiscation ---+    function grab(bytes32 i, address u, address v, address w, int dink, int dart) external auth {+        Urn storage urn = urns[i][u];+        Ilk storage ilk = ilks[i];++        urn.ink = _add(urn.ink, dink);+        urn.art = _add(urn.art, dart);+        ilk.Art = _add(ilk.Art, dart);++        int dtab = _mul(ilk.rate, dart);++        gem[i][v] = _sub(gem[i][v], dink);+        sin[w]    = _sub(sin[w],    dtab);+        vice      = _sub(vice,      dtab);+    }++    // --- Settlement ---+    function heal(uint rad) external {+        address u = msg.sender;+        sin[u] = _sub(sin[u], rad);+        dai[u] = _sub(dai[u], rad);+        vice   = _sub(vice,   rad);+        debt   = _sub(debt,   rad);+    }+    function suck(address u, address v, uint rad) external auth {+        sin[u] = _add(sin[u], rad);+        dai[v] = _add(dai[v], rad);+        vice   = _add(vice,   rad);+        debt   = _add(debt,   rad);+    }++    // --- Rates ---+    function fold(bytes32 i, address u, int rate) external auth {+        require(live == 1, "Vat/not-live");+        Ilk storage ilk = ilks[i];+        ilk.rate = _add(ilk.rate, rate);+        int rad  = _mul(ilk.Art, rate);+        dai[u]   = _add(dai[u], rad);+        debt     = _add(debt,   rad);+    }+}
+ cli/cli.hs view
@@ -0,0 +1,599 @@+-- Main file of the hevm CLI program++{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE TemplateHaskell #-}++module Main where++import Control.Monad (when, forM_, unless)+import Control.Monad.ST (RealWorld, stToIO)+import Data.ByteString (ByteString)+import Data.DoubleWord (Word256)+import Data.List (intersperse)+import Data.Maybe (fromMaybe, mapMaybe, fromJust)+import Data.Text qualified as T+import Data.Text.IO qualified as T+import Data.Version (showVersion)+import Data.Word (Word64)+import GHC.Conc (getNumProcessors)+import Numeric.Natural (Natural)+import Optics.Core ((&), set)+import Witch (unsafeInto)+import Options.Generic as Options+import Paths_hevm qualified as Paths+import System.IO (stderr)+import System.Directory (withCurrentDirectory, getCurrentDirectory, doesDirectoryExist)+import System.FilePath ((</>))+import System.Exit (exitFailure, exitWith, ExitCode(..))++import EVM (initialContract, abstractContract, makeVm)+import EVM.ABI (Sig(..))+import EVM.Dapp (dappInfo, DappInfo, emptyDapp)+import EVM.Expr qualified as Expr+import EVM.Concrete qualified as Concrete+import GitHash+import EVM.FeeSchedule (feeSchedule)+import EVM.Fetch qualified as Fetch+import EVM.Format (hexByteString, strip0x, showTraceTree, formatExpr)+import EVM.Solidity+import EVM.Solvers+import EVM.Stepper qualified+import EVM.SymExec+import EVM.Transaction qualified+import EVM.Types hiding (word)+import EVM.UnitTest++-- This record defines the program's command-line options+-- automatically via the `optparse-generic` package.+data Command w+  = Symbolic -- Symbolically explore an abstract program, or specialized with specified env & calldata+  -- vm opts+      { code          :: w ::: Maybe ByteString <?> "Program bytecode"+      , calldata      :: w ::: Maybe ByteString <?> "Tx: calldata"+      , address       :: w ::: Maybe Addr       <?> "Tx: address"+      , caller        :: w ::: Maybe Addr       <?> "Tx: caller"+      , origin        :: w ::: Maybe Addr       <?> "Tx: origin"+      , coinbase      :: w ::: Maybe Addr       <?> "Block: coinbase"+      , value         :: w ::: Maybe W256       <?> "Tx: Eth amount"+      , nonce         :: w ::: Maybe Word64     <?> "Nonce of origin"+      , gas           :: w ::: Maybe Word64     <?> "Tx: gas amount"+      , number        :: w ::: Maybe W256       <?> "Block: number"+      , timestamp     :: w ::: Maybe W256       <?> "Block: timestamp"+      , basefee       :: w ::: Maybe W256       <?> "Block: base fee"+      , priorityFee   :: w ::: Maybe W256       <?> "Tx: priority fee"+      , gaslimit      :: w ::: Maybe Word64     <?> "Tx: gas limit"+      , gasprice      :: w ::: Maybe W256       <?> "Tx: gas price"+      , create        :: w ::: Bool             <?> "Tx: creation"+      , maxcodesize   :: w ::: Maybe W256       <?> "Block: max code size"+      , prevRandao    :: w ::: Maybe W256       <?> "Block: prevRandao"+      , chainid       :: w ::: Maybe W256       <?> "Env: chainId"+  -- remote state opts+      , rpc           :: w ::: Maybe URL        <?> "Fetch state from a remote node"+      , block         :: w ::: Maybe W256       <?> "Block state is be fetched from"++  -- symbolic execution opts+      , root          :: w ::: Maybe String       <?> "Path to  project root directory (default: . )"+      , projectType   :: w ::: Maybe ProjectType  <?> "Is this a Foundry or DappTools project (default: Foundry)"+      , initialStorage :: w ::: Maybe (InitialStorage) <?> "Starting state for storage: Empty, Abstract (default Abstract)"+      , sig           :: w ::: Maybe Text         <?> "Signature of types to decode / encode"+      , arg           :: w ::: [String]           <?> "Values to encode"+      , getModels     :: w ::: Bool               <?> "Print example testcase for each execution path"+      , showTree      :: w ::: Bool               <?> "Print branches explored in tree view"+      , showReachableTree :: w ::: Bool           <?> "Print only reachable branches explored in tree view"+      , smttimeout    :: w ::: Maybe Natural      <?> "Timeout given to SMT solver in seconds (default: 300)"+      , maxIterations :: w ::: Maybe Integer      <?> "Number of times we may revisit a particular branching point"+      , solver        :: w ::: Maybe Text         <?> "Used SMT solver: z3 (default) or cvc5"+      , smtdebug      :: w ::: Bool               <?> "Print smt queries sent to the solver"+      , assertions    :: w ::: Maybe [Word256]    <?> "Comma separated list of solc panic codes to check for (default: user defined assertion violations only)"+      , askSmtIterations :: w ::: Integer         <!> "1" <?> "Number of times we may revisit a particular branching point before we consult the smt solver to check reachability (default: 1)"+      , numSolvers    :: w ::: Maybe Natural      <?> "Number of solver instances to use (default: number of cpu cores)"+      , loopDetectionHeuristic :: w ::: LoopHeuristic <!> "StackBased" <?> "Which heuristic should be used to determine if we are in a loop: StackBased (default) or Naive"+      , abstractArithmetic    :: w ::: Bool             <?> "Use abstraction-refinement for complicated arithmetic functions such as MulMod. This runs the solver first with abstraction turned on, and if it returns a potential counterexample, the counterexample is refined to make sure it is a counterexample for the actual (not the abstracted) problem"+      , abstractMemory    :: w ::: Bool                      <?> "Use abstraction-refinement for Memory. This runs the solver first with abstraction turned on, and if it returns a potential counterexample, the counterexample is refined to make sure it is a counterexample for the actual (not the abstracted) problem"+      }+  | Equivalence -- prove equivalence between two programs+      { codeA         :: w ::: ByteString       <?> "Bytecode of the first program"+      , codeB         :: w ::: ByteString       <?> "Bytecode of the second program"+      , sig           :: w ::: Maybe Text       <?> "Signature of types to decode / encode"+      , arg           :: w ::: [String]         <?> "Values to encode"+      , calldata      :: w ::: Maybe ByteString <?> "Tx: calldata"+      , smttimeout    :: w ::: Maybe Natural    <?> "Timeout given to SMT solver in seconds (default: 300)"+      , maxIterations :: w ::: Maybe Integer    <?> "Number of times we may revisit a particular branching point"+      , solver        :: w ::: Maybe Text       <?> "Used SMT solver: z3 (default) or cvc5"+      , smtoutput     :: w ::: Bool             <?> "Print verbose smt output"+      , smtdebug      :: w ::: Bool             <?> "Print smt queries sent to the solver"+      , askSmtIterations :: w ::: Integer       <!> "1" <?> "Number of times we may revisit a particular branching point before we consult the smt solver to check reachability (default: 1)"+      , loopDetectionHeuristic :: w ::: LoopHeuristic <!> "StackBased" <?> "Which heuristic should be used to determine if we are in a loop: StackBased (default) or Naive"+      , abstractArithmetic    :: w ::: Bool             <?> "Use abstraction-refinement for complicated arithmetic functions such as MulMod. This runs the solver first with abstraction turned on, and if it returns a potential counterexample, the counterexample is refined to make sure it is a counterexample for the actual (not the abstracted) problem"+      , abstractMemory    :: w ::: Bool                      <?> "Use abstraction-refinement for Memory. This runs the solver first with abstraction turned on, and if it returns a potential counterexample, the counterexample is refined to make sure it is a counterexample for the actual (not the abstracted) problem"+      }+  | Exec -- Execute a given program with specified env & calldata+      { code        :: w ::: Maybe ByteString  <?> "Program bytecode"+      , calldata    :: w ::: Maybe ByteString  <?> "Tx: calldata"+      , address     :: w ::: Maybe Addr        <?> "Tx: address"+      , caller      :: w ::: Maybe Addr        <?> "Tx: caller"+      , origin      :: w ::: Maybe Addr        <?> "Tx: origin"+      , coinbase    :: w ::: Maybe Addr        <?> "Block: coinbase"+      , value       :: w ::: Maybe W256        <?> "Tx: Eth amount"+      , nonce       :: w ::: Maybe Word64      <?> "Nonce of origin"+      , gas         :: w ::: Maybe Word64      <?> "Tx: gas amount"+      , number      :: w ::: Maybe W256        <?> "Block: number"+      , timestamp   :: w ::: Maybe W256        <?> "Block: timestamp"+      , basefee     :: w ::: Maybe W256        <?> "Block: base fee"+      , priorityFee :: w ::: Maybe W256        <?> "Tx: priority fee"+      , gaslimit    :: w ::: Maybe Word64      <?> "Tx: gas limit"+      , gasprice    :: w ::: Maybe W256        <?> "Tx: gas price"+      , create      :: w ::: Bool              <?> "Tx: creation"+      , maxcodesize :: w ::: Maybe W256        <?> "Block: max code size"+      , prevRandao  :: w ::: Maybe W256        <?> "Block: prevRandao"+      , chainid     :: w ::: Maybe W256        <?> "Env: chainId"+      , trace       :: w ::: Bool              <?> "Dump trace"+      , rpc         :: w ::: Maybe URL         <?> "Fetch state from a remote node"+      , block       :: w ::: Maybe W256        <?> "Block state is be fetched from"+      , root        :: w ::: Maybe String      <?> "Path to  project root directory (default: . )"+      , projectType :: w ::: Maybe ProjectType <?> "Is this a Foundry or DappTools project (default: Foundry)"+      }+  | Test -- Run DSTest unit tests+      { root        :: w ::: Maybe String               <?> "Path to  project root directory (default: . )"+      , projectType   :: w ::: Maybe ProjectType        <?> "Is this a Foundry or DappTools project (default: Foundry)"+      , rpc           :: w ::: Maybe URL                <?> "Fetch state from a remote node"+      , number        :: w ::: Maybe W256               <?> "Block: number"+      , verbose       :: w ::: Maybe Int                <?> "Append call trace: {1} failures {2} all"+      , coverage      :: w ::: Bool                     <?> "Coverage analysis"+      , match         :: w ::: Maybe String             <?> "Test case filter - only run methods matching regex"+      , solver        :: w ::: Maybe Text               <?> "Used SMT solver: z3 (default) or cvc5"+      , smtdebug      :: w ::: Bool                     <?> "Print smt queries sent to the solver"+      , ffi           :: w ::: Bool                     <?> "Allow the usage of the hevm.ffi() cheatcode (WARNING: this allows test authors to execute arbitrary code on your machine)"+      , smttimeout    :: w ::: Maybe Natural            <?> "Timeout given to SMT solver in seconds (default: 300)"+      , maxIterations :: w ::: Maybe Integer            <?> "Number of times we may revisit a particular branching point"+      , loopDetectionHeuristic :: w ::: LoopHeuristic   <!> "StackBased" <?> "Which heuristic should be used to determine if we are in a loop: StackBased (default) or Naive"+      , abstractArithmetic    :: w ::: Bool             <?> "Use abstraction-refinement for complicated arithmetic functions such as MulMod. This runs the solver first with abstraction turned on, and if it returns a potential counterexample, the counterexample is refined to make sure it is a counterexample for the actual (not the abstracted) problem"+      , abstractMemory    :: w ::: Bool                      <?> "Use abstraction-refinement for Memory. This runs the solver first with abstraction turned on, and if it returns a potential counterexample, the counterexample is refined to make sure it is a counterexample for the actual (not the abstracted) problem"+      , askSmtIterations :: w ::: Integer               <!> "1" <?> "Number of times we may revisit a particular branching point before we consult the smt solver to check reachability (default: 1)"+      }+  | Version++  deriving (Options.Generic)++type URL = Text+++-- For some reason haskell can't derive a+-- parseField instance for (Text, ByteString)+instance Options.ParseField (Text, ByteString)++deriving instance Options.ParseField Word256+deriving instance Options.ParseField [Word256]++instance Options.ParseRecord (Command Options.Wrapped) where+  parseRecord =+    Options.parseRecordWithModifiers Options.lispCaseModifiers++data InitialStorage+  = Empty+  | Abstract+  deriving (Show, Read, Options.ParseField)++getFullVersion :: [Char]+getFullVersion = showVersion Paths.version <> " [" <> gitVersion <> "]"+  where+    gitInfo = $$tGitInfoCwdTry+    gitVersion = case gitInfo of+      Right val -> "git rev " <> giBranch val <>  "@" <> giHash val+      Left _ -> "no git revision present"++main :: IO ()+main = do+  cmd <- Options.unwrapRecord "hevm -- Ethereum evaluator"+  case cmd of+    Version {} ->putStrLn getFullVersion+    Symbolic {} -> do+      root <- getRoot cmd+      withCurrentDirectory root $ assert cmd+    Equivalence {} -> equivalence cmd+    Exec {} ->+      launchExec cmd+    Test {} -> do+      root <- getRoot cmd+      withCurrentDirectory root $ do+        cores <- unsafeInto <$> getNumProcessors+        solver <- getSolver cmd+        withSolvers solver cores cmd.smttimeout $ \solvers -> do+          buildOut <- readBuildOutput root (getProjectType cmd)+          case buildOut of+            Left e -> do+              putStrLn $ "Error: " <> e+              exitFailure+            Right out -> do+              -- TODO: which functions here actually require a BuildOutput, and which can take it as a Maybe?+              testOpts <- unitTestOptions cmd solvers (Just out)+              res <- unitTest testOpts out.contracts+              unless res exitFailure++equivalence :: Command Options.Unwrapped -> IO ()+equivalence cmd = do+  let bytecodeA = hexByteString "--code" . strip0x $ cmd.codeA+      bytecodeB = hexByteString "--code" . strip0x $ cmd.codeB+      veriOpts = VeriOpts { simp = True+                          , debug = False+                          , maxIter = cmd.maxIterations+                          , askSmtIters = cmd.askSmtIterations+                          , loopHeuristic = cmd.loopDetectionHeuristic+                          , abstRefineConfig = AbstRefineConfig cmd.abstractArithmetic cmd.abstractMemory+                          , rpcInfo = Nothing+                          }+  calldata <- buildCalldata cmd+  solver <- getSolver cmd+  withSolvers solver 3 Nothing $ \s -> do+    res <- equivalenceCheck s bytecodeA bytecodeB veriOpts calldata+    case any isCex res of+      False -> do+        putStrLn "No discrepancies found"+        when (any isTimeout res) $ do+          putStrLn "But timeout(s) occurred"+          exitFailure+      True -> do+        let cexs = mapMaybe getCex res+        T.putStrLn . T.unlines $+          [ "Not equivalent. The following inputs result in differing behaviours:"+          , "" , "-----", ""+          ] <> (intersperse (T.unlines [ "", "-----" ]) $ fmap (formatCex (AbstractBuf "txdata") Nothing) cexs)+        exitFailure++getSolver :: Command Options.Unwrapped -> IO Solver+getSolver cmd = case cmd.solver of+                  Nothing -> pure Z3+                  Just s -> case T.unpack s of+                              "z3" -> pure Z3+                              "cvc5" -> pure CVC5+                              input -> do+                                putStrLn $ "unrecognised solver: " <> input+                                exitFailure++getSrcInfo :: Command Options.Unwrapped -> IO DappInfo+getSrcInfo cmd = do+  root <- getRoot cmd+  withCurrentDirectory root $ do+    outExists <- doesDirectoryExist (root </> "out")+    if outExists+    then do+      buildOutput <- readBuildOutput root (getProjectType cmd)+      case buildOutput of+        Left _ -> pure emptyDapp+        Right o -> pure $ dappInfo root o+    else pure emptyDapp++getProjectType :: Command Options.Unwrapped -> ProjectType+getProjectType cmd = fromMaybe Foundry cmd.projectType++getRoot :: Command Options.Unwrapped -> IO FilePath+getRoot cmd = maybe getCurrentDirectory pure (cmd.root)+++-- | Builds a buffer representing calldata based on the given cli arguments+buildCalldata :: Command Options.Unwrapped -> IO (Expr Buf, [Prop])+buildCalldata cmd = case (cmd.calldata, cmd.sig) of+  -- fully abstract calldata+  (Nothing, Nothing) -> pure $ mkCalldata Nothing []+  -- fully concrete calldata+  (Just c, Nothing) -> pure (ConcreteBuf (hexByteString "bytes" . strip0x $ c), [])+  -- calldata according to given abi with possible specializations from the `arg` list+  (Nothing, Just sig') -> do+    method' <- functionAbi sig'+    pure $ mkCalldata (Just (Sig method'.methodSignature (snd <$> method'.inputs))) cmd.arg+  -- both args provided+  (_, _) -> do+    putStrLn "incompatible options provided: --calldata and --sig"+    exitFailure+++-- If function signatures are known, they should always be given for best results.+assert :: Command Options.Unwrapped -> IO ()+assert cmd = do+  let block'  = maybe Fetch.Latest Fetch.BlockNumber cmd.block+      rpcinfo = (,) block' <$> cmd.rpc+  calldata <- buildCalldata cmd+  preState <- symvmFromCommand cmd calldata+  let errCodes = fromMaybe defaultPanicCodes cmd.assertions+  cores <- unsafeInto <$> getNumProcessors+  let solverCount = fromMaybe cores cmd.numSolvers+  solver <- getSolver cmd+  withSolvers solver solverCount cmd.smttimeout $ \solvers -> do+    let opts = VeriOpts { simp = True+                        , debug = cmd.smtdebug+                        , maxIter = cmd.maxIterations+                        , askSmtIters = cmd.askSmtIterations+                        , loopHeuristic = cmd.loopDetectionHeuristic+                        , abstRefineConfig = AbstRefineConfig cmd.abstractArithmetic cmd.abstractMemory+                        , rpcInfo = rpcinfo+    }+    (expr, res) <- verify solvers opts preState (Just $ checkAssertions errCodes)+    case res of+      [Qed _] -> do+        putStrLn "\nQED: No reachable property violations discovered\n"+        showExtras solvers cmd calldata expr+      _ -> do+        let cexs = snd <$> mapMaybe getCex res+            timeouts = mapMaybe getTimeout res+            counterexamples+              | null cexs = []+              | otherwise =+                 [ ""+                 , "Discovered the following counterexamples:"+                 , ""+                 ] <> fmap (formatCex (fst calldata) Nothing) cexs+            unknowns+              | null timeouts = []+              | otherwise =+                 [ ""+                 , "Could not determine reachability of the following end states:"+                 , ""+                 ] <> fmap (formatExpr) timeouts+        T.putStrLn $ T.unlines (counterexamples <> unknowns)+        showExtras solvers cmd calldata expr+        exitFailure++showExtras :: SolverGroup -> Command Options.Unwrapped -> (Expr Buf, [Prop]) -> Expr End -> IO ()+showExtras solvers cmd calldata expr = do+  when cmd.showTree $ do+    putStrLn "=== Expression ===\n"+    T.putStrLn $ formatExpr expr+    putStrLn ""+  when cmd.showReachableTree $ do+    reached <- reachable solvers expr+    putStrLn "=== Reachable Expression ===\n"+    T.putStrLn (formatExpr . snd $ reached)+    putStrLn ""+  when cmd.getModels $ do+    putStrLn $ "=== Models for " <> show (Expr.numBranches expr) <> " branches ===\n"+    ms <- produceModels solvers expr+    forM_ ms (showModel (fst calldata))++isTestOrLib :: Text -> Bool+isTestOrLib file = T.isSuffixOf ".t.sol" file || areAnyPrefixOf ["src/test/", "src/tests/", "lib/"] file++areAnyPrefixOf :: [Text] -> Text -> Bool+areAnyPrefixOf prefixes t = any (flip T.isPrefixOf t) prefixes++launchExec :: Command Options.Unwrapped -> IO ()+launchExec cmd = do+  dapp <- getSrcInfo cmd+  vm <- vmFromCommand cmd+  let+    block = maybe Fetch.Latest Fetch.BlockNumber cmd.block+    rpcinfo = (,) block <$> cmd.rpc++  -- TODO: we shouldn't need solvers to execute this code+  withSolvers Z3 0 Nothing $ \solvers -> do+    vm' <- EVM.Stepper.interpret (Fetch.oracle solvers rpcinfo) vm EVM.Stepper.runFully+    when cmd.trace $ T.hPutStr stderr (showTraceTree dapp vm')+    case vm'.result of+      Just (VMFailure (Revert msg)) -> do+        let res = case msg of+                    ConcreteBuf bs -> bs+                    _ -> "<symbolic>"+        putStrLn $ "Revert: " <> (show $ ByteStringS res)+        exitWith (ExitFailure 2)+      Just (VMFailure err) -> do+        putStrLn $ "Error: " <> show err+        exitWith (ExitFailure 2)+      Just (Unfinished p) -> do+        putStrLn $ "Could not continue execution: " <> show p+        exitWith (ExitFailure 2)+      Just (VMSuccess buf) -> do+        let msg = case buf of+              ConcreteBuf msg' -> msg'+              _ -> "<symbolic>"+        print $ "Return: " <> (show $ ByteStringS msg)+      _ ->+        internalError "no EVM result"++-- | Creates a (concrete) VM from command line options+vmFromCommand :: Command Options.Unwrapped -> IO (VM RealWorld)+vmFromCommand cmd = do+  (miner,ts,baseFee,blockNum,prevRan) <- case cmd.rpc of+    Nothing -> pure (LitAddr 0,Lit 0,0,0,0)+    Just url -> Fetch.fetchBlockFrom block url >>= \case+      Nothing -> error "Error: Could not fetch block"+      Just Block{..} -> pure ( coinbase+                             , timestamp+                             , baseFee+                             , number+                             , prevRandao+                             )++  contract <- case (cmd.rpc, cmd.address, cmd.code) of+    (Just url, Just addr', Just c) -> do+      Fetch.fetchContractFrom block url addr' >>= \case+        Nothing ->+          error $ "Error: contract not found: " <> show address+        Just contract ->+          -- if both code and url is given,+          -- fetch the contract and overwrite the code+          pure $+            initialContract  (mkCode $ hexByteString "--code" $ strip0x c)+              & set #balance  (contract.balance)+              & set #nonce    (contract.nonce)+              & set #external (contract.external)++    (Just url, Just addr', Nothing) ->+      Fetch.fetchContractFrom block url addr' >>= \case+        Nothing ->+          error $ "Error: contract not found: " <> show address+        Just contract -> pure contract++    (_, _, Just c)  ->+      pure $+        initialContract (mkCode $ hexByteString "--code" $ strip0x c)++    (_, _, Nothing) ->+      error "Error: must provide at least (rpc + address) or code"++  let ts' = case maybeLitWord ts of+        Just t -> t+        Nothing -> internalError "unexpected symbolic timestamp when executing vm test"++  vm <- stToIO $ vm0 baseFee miner ts' blockNum prevRan contract+  pure $ EVM.Transaction.initTx vm+    where+        block   = maybe Fetch.Latest Fetch.BlockNumber cmd.block+        value   = word (.value) 0+        caller  = addr (.caller) (LitAddr 0)+        origin  = addr (.origin) (LitAddr 0)+        calldata = ConcreteBuf $ bytes (.calldata) ""+        decipher = hexByteString "bytes" . strip0x+        mkCode bs = if cmd.create+                    then InitCode bs mempty+                    else RuntimeCode (ConcreteRuntimeCode bs)+        address = if cmd.create+                  then addr (.address) (Concrete.createAddress (fromJust $ maybeLitAddr origin) (W64 $ word64 (.nonce) 0))+                  else addr (.address) (LitAddr 0xacab)++        vm0 baseFee miner ts blockNum prevRan c = makeVm $ VMOpts+          { contract       = c+          , otherContracts = []+          , calldata       = (calldata, [])+          , value          = Lit value+          , address        = address+          , caller         = caller+          , origin         = origin+          , gas            = word64 (.gas) 0xffffffffffffffff+          , baseFee        = baseFee+          , priorityFee    = word (.priorityFee) 0+          , gaslimit       = word64 (.gaslimit) 0xffffffffffffffff+          , coinbase       = addr (.coinbase) miner+          , number         = word (.number) blockNum+          , timestamp      = Lit $ word (.timestamp) ts+          , blockGaslimit  = word64 (.gaslimit) 0xffffffffffffffff+          , gasprice       = word (.gasprice) 0+          , maxCodeSize    = word (.maxcodesize) 0xffffffff+          , prevRandao     = word (.prevRandao) prevRan+          , schedule       = feeSchedule+          , chainId        = word (.chainid) 1+          , create         = (.create) cmd+          , baseState      = EmptyBase+          , txAccessList   = mempty -- TODO: support me soon+          , allowFFI       = False+          }+        word f def = fromMaybe def (f cmd)+        word64 f def = fromMaybe def (f cmd)+        addr f def = maybe def LitAddr (f cmd)+        bytes f def = maybe def decipher (f cmd)++symvmFromCommand :: Command Options.Unwrapped -> (Expr Buf, [Prop]) -> IO (VM RealWorld)+symvmFromCommand cmd calldata = do+  (miner,blockNum,baseFee,prevRan) <- case cmd.rpc of+    Nothing -> pure (SymAddr "miner",0,0,0)+    Just url -> Fetch.fetchBlockFrom block url >>= \case+      Nothing -> error "Error: Could not fetch block"+      Just Block{..} -> pure ( coinbase+                             , number+                             , baseFee+                             , prevRandao+                             )++  let+    caller = SymAddr "caller"+    ts = maybe Timestamp Lit cmd.timestamp+    callvalue = maybe TxValue Lit cmd.value++  contract <- case (cmd.rpc, cmd.address, cmd.code) of+    (Just url, Just addr', _) ->+      Fetch.fetchContractFrom block url addr' >>= \case+        Nothing ->+          error "Error: contract not found."+        Just contract' -> pure contract''+          where+            contract'' = case cmd.code of+              Nothing -> contract'+              -- if both code and url is given,+              -- fetch the contract and overwrite the code+              Just c -> initialContract (mkCode $ decipher c)+                        & set #origStorage (contract'.origStorage)+                        & set #balance     (contract'.balance)+                        & set #nonce       (contract'.nonce)+                        & set #external    (contract'.external)++    (_, _, Just c)  ->+      pure ((`abstractContract` address) . mkCode $ decipher c)+    (_, _, Nothing) ->+      error "Error: must provide at least (rpc + address) or code"++  vm <- stToIO $ vm0 baseFee miner ts blockNum prevRan calldata callvalue caller contract+  pure $ EVM.Transaction.initTx vm++  where+    decipher = hexByteString "bytes" . strip0x+    block = maybe Fetch.Latest Fetch.BlockNumber cmd.block+    origin = eaddr (.origin) (SymAddr "origin")+    mkCode bs = if cmd.create+                   then InitCode bs mempty+                   else RuntimeCode (ConcreteRuntimeCode bs)+    address = eaddr (.address) (SymAddr "entrypoint")+    vm0 baseFee miner ts blockNum prevRan cd callvalue caller c = makeVm $ VMOpts+      { contract       = c+      , otherContracts = []+      , calldata       = cd+      , value          = callvalue+      , address        = address+      , caller         = caller+      , origin         = origin+      , gas            = word64 (.gas) 0xffffffffffffffff+      , gaslimit       = word64 (.gaslimit) 0xffffffffffffffff+      , baseFee        = baseFee+      , priorityFee    = word (.priorityFee) 0+      , coinbase       = eaddr (.coinbase) miner+      , number         = word (.number) blockNum+      , timestamp      = ts+      , blockGaslimit  = word64 (.gaslimit) 0xffffffffffffffff+      , gasprice       = word (.gasprice) 0+      , maxCodeSize    = word (.maxcodesize) 0xffffffff+      , prevRandao     = word (.prevRandao) prevRan+      , schedule       = feeSchedule+      , chainId        = word (.chainid) 1+      , create         = (.create) cmd+      , baseState      = maybe AbstractBase parseInitialStorage (cmd.initialStorage)+      , txAccessList   = mempty+      , allowFFI       = False+      }+    word f def = fromMaybe def (f cmd)+    word64 f def = fromMaybe def (f cmd)+    eaddr f def = maybe def LitAddr (f cmd)++unitTestOptions :: Command Options.Unwrapped -> SolverGroup -> Maybe BuildOutput -> IO (UnitTestOptions RealWorld)+unitTestOptions cmd solvers buildOutput = do+  root <- getRoot cmd+  let srcInfo = maybe emptyDapp (dappInfo root) buildOutput++  let rpcinfo = case (cmd.number, cmd.rpc) of+          (Just block, Just url) -> Just (Fetch.BlockNumber block, url)+          (Nothing, Just url) -> Just (Fetch.Latest, url)+          _ -> Nothing+  params <- paramsFromRpc rpcinfo++  let+    testn = params.number+    block' = if 0 == testn+       then Fetch.Latest+       else Fetch.BlockNumber testn++  pure UnitTestOptions+    { solvers = solvers+    , rpcInfo = case cmd.rpc of+         Just url -> Just (block', url)+         Nothing  -> Nothing+    , maxIter = cmd.maxIterations+    , askSmtIters = cmd.askSmtIterations+    , smtDebug = cmd.smtdebug+    , smtTimeout = cmd.smttimeout+    , solver = cmd.solver+    , verbose = cmd.verbose+    , match = T.pack $ fromMaybe ".*" cmd.match+    , testParams = params+    , dapp = srcInfo+    , ffiAllowed = cmd.ffi+    }+parseInitialStorage :: InitialStorage -> BaseState+parseInitialStorage Empty = EmptyBase+parseInitialStorage Abstract = AbstractBase
− hevm-cli/hevm-cli.hs
@@ -1,716 +0,0 @@--- Main file of the hevm CLI program--{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE TemplateHaskell #-}--module Main where--import Control.Monad (void, when, forM_, unless)-import Control.Monad.State.Strict (liftIO)-import Data.Bifunctor (second)-import Data.ByteString (ByteString)-import Data.ByteString qualified as ByteString-import Data.ByteString.Char8 qualified as Char8-import Data.ByteString.Lazy qualified as LazyByteString-import Data.DoubleWord (Word256)-import Data.List (intersperse)-import Data.Map qualified as Map-import Data.Maybe (fromMaybe, mapMaybe)-import Data.Text (pack)-import Data.Text qualified as T-import Data.Text.IO qualified as T-import Data.Version (showVersion)-import Data.Word (Word64)-import GHC.Conc (getNumProcessors)-import Numeric.Natural (Natural)-import Optics.Core ((&), (%), set)-import Witch (unsafeInto)-import Options.Generic as Options-import Paths_hevm qualified as Paths-import System.IO (stderr)-import System.Directory (withCurrentDirectory, getCurrentDirectory, doesDirectoryExist)-import System.FilePath ((</>))-import System.Exit (exitFailure, exitWith, ExitCode(..))--import EVM (initialContract, makeVm)-import EVM.Concrete (createAddress)-import EVM.Dapp (findUnitTests, dappInfo, DappInfo, emptyDapp)-import EVM.Debug (Mode(..))-import EVM.Expr qualified as Expr-import EVM.Facts qualified as Facts-import EVM.Facts.Git qualified as Git-import GitHash-import EVM.FeeSchedule qualified as FeeSchedule-import EVM.Fetch qualified-import EVM.Format (hexByteString, strip0x, showTraceTree, formatExpr)-import EVM.Solidity-import EVM.Solvers-import EVM.Stepper qualified-import EVM.SymExec-import EVM.Transaction qualified-import EVM.TTY qualified as TTY-import EVM.Types hiding (word)-import EVM.UnitTest---- This record defines the program's command-line options--- automatically via the `optparse-generic` package.-data Command w-  = Symbolic -- Symbolically explore an abstract program, or specialized with specified env & calldata-  -- vm opts-      { code          :: w ::: Maybe ByteString <?> "Program bytecode"-      , calldata      :: w ::: Maybe ByteString <?> "Tx: calldata"-      , address       :: w ::: Maybe Addr       <?> "Tx: address"-      , caller        :: w ::: Maybe Addr       <?> "Tx: caller"-      , origin        :: w ::: Maybe Addr       <?> "Tx: origin"-      , coinbase      :: w ::: Maybe Addr       <?> "Block: coinbase"-      , value         :: w ::: Maybe W256       <?> "Tx: Eth amount"-      , nonce         :: w ::: Maybe W256       <?> "Nonce of origin"-      , gas           :: w ::: Maybe Word64     <?> "Tx: gas amount"-      , number        :: w ::: Maybe W256       <?> "Block: number"-      , timestamp     :: w ::: Maybe W256       <?> "Block: timestamp"-      , basefee       :: w ::: Maybe W256       <?> "Block: base fee"-      , priorityFee   :: w ::: Maybe W256       <?> "Tx: priority fee"-      , gaslimit      :: w ::: Maybe Word64     <?> "Tx: gas limit"-      , gasprice      :: w ::: Maybe W256       <?> "Tx: gas price"-      , create        :: w ::: Bool             <?> "Tx: creation"-      , maxcodesize   :: w ::: Maybe W256       <?> "Block: max code size"-      , prevRandao    :: w ::: Maybe W256       <?> "Block: prevRandao"-      , chainid       :: w ::: Maybe W256       <?> "Env: chainId"-  -- remote state opts-      , rpc           :: w ::: Maybe URL        <?> "Fetch state from a remote node"-      , block         :: w ::: Maybe W256       <?> "Block state is be fetched from"-      , state         :: w ::: Maybe String     <?> "Path to state repository"-      , cache         :: w ::: Maybe String     <?> "Path to rpc cache repository"--  -- symbolic execution opts-      , root          :: w ::: Maybe String       <?> "Path to  project root directory (default: . )"-      , projectType   :: w ::: Maybe ProjectType  <?> "Is this a Foundry or DappTools project (default: Foundry)"-      , initialStorage :: w ::: Maybe (InitialStorage) <?> "Starting state for storage: Empty, Abstract, Concrete <STORE> (default Abstract)"-      , sig           :: w ::: Maybe Text         <?> "Signature of types to decode / encode"-      , arg           :: w ::: [String]           <?> "Values to encode"-      , debug         :: w ::: Bool               <?> "Run interactively"-      , getModels     :: w ::: Bool               <?> "Print example testcase for each execution path"-      , showTree      :: w ::: Bool               <?> "Print branches explored in tree view"-      , showReachableTree :: w ::: Bool           <?> "Print only reachable branches explored in tree view"-      , smttimeout    :: w ::: Maybe Natural      <?> "Timeout given to SMT solver in seconds (default: 300)"-      , maxIterations :: w ::: Maybe Integer      <?> "Number of times we may revisit a particular branching point"-      , solver        :: w ::: Maybe Text         <?> "Used SMT solver: z3 (default) or cvc5"-      , smtdebug      :: w ::: Bool               <?> "Print smt queries sent to the solver"-      , assertions    :: w ::: Maybe [Word256]    <?> "Comma seperated list of solc panic codes to check for (default: everything except arithmetic overflow)"-      , askSmtIterations :: w ::: Integer         <!> "1" <?> "Number of times we may revisit a particular branching point before we consult the smt solver to check reachability (default: 1)"-      , numSolvers    :: w ::: Maybe Natural      <?> "Number of solver instances to use (default: number of cpu cores)"-      , loopDetectionHeuristic :: w ::: LoopHeuristic <!> "StackBased" <?> "Which heuristic should be used to determine if we are in a loop: StackBased (default) or Naive"-      }-  | Equivalence -- prove equivalence between two programs-      { codeA         :: w ::: ByteString       <?> "Bytecode of the first program"-      , codeB         :: w ::: ByteString       <?> "Bytecode of the second program"-      , sig           :: w ::: Maybe Text       <?> "Signature of types to decode / encode"-      , arg           :: w ::: [String]         <?> "Values to encode"-      , calldata      :: w ::: Maybe ByteString <?> "Tx: calldata"-      , smttimeout    :: w ::: Maybe Natural    <?> "Timeout given to SMT solver in seconds (default: 300)"-      , maxIterations :: w ::: Maybe Integer    <?> "Number of times we may revisit a particular branching point"-      , solver        :: w ::: Maybe Text       <?> "Used SMT solver: z3 (default) or cvc5"-      , smtoutput     :: w ::: Bool             <?> "Print verbose smt output"-      , smtdebug      :: w ::: Bool             <?> "Print smt queries sent to the solver"-      , askSmtIterations :: w ::: Integer       <!> "1" <?> "Number of times we may revisit a particular branching point before we consult the smt solver to check reachability (default: 1)"-      , loopDetectionHeuristic :: w ::: LoopHeuristic <!> "StackBased" <?> "Which heuristic should be used to determine if we are in a loop: StackBased (default) or Naive"-      }-  | Exec -- Execute a given program with specified env & calldata-      { code        :: w ::: Maybe ByteString  <?> "Program bytecode"-      , calldata    :: w ::: Maybe ByteString  <?> "Tx: calldata"-      , address     :: w ::: Maybe Addr        <?> "Tx: address"-      , caller      :: w ::: Maybe Addr        <?> "Tx: caller"-      , origin      :: w ::: Maybe Addr        <?> "Tx: origin"-      , coinbase    :: w ::: Maybe Addr        <?> "Block: coinbase"-      , value       :: w ::: Maybe W256        <?> "Tx: Eth amount"-      , nonce       :: w ::: Maybe W256        <?> "Nonce of origin"-      , gas         :: w ::: Maybe Word64      <?> "Tx: gas amount"-      , number      :: w ::: Maybe W256        <?> "Block: number"-      , timestamp   :: w ::: Maybe W256        <?> "Block: timestamp"-      , basefee     :: w ::: Maybe W256        <?> "Block: base fee"-      , priorityFee :: w ::: Maybe W256        <?> "Tx: priority fee"-      , gaslimit    :: w ::: Maybe Word64      <?> "Tx: gas limit"-      , gasprice    :: w ::: Maybe W256        <?> "Tx: gas price"-      , create      :: w ::: Bool              <?> "Tx: creation"-      , maxcodesize :: w ::: Maybe W256        <?> "Block: max code size"-      , prevRandao  :: w ::: Maybe W256        <?> "Block: prevRandao"-      , chainid     :: w ::: Maybe W256        <?> "Env: chainId"-      , debug       :: w ::: Bool              <?> "Run interactively"-      , jsontrace   :: w ::: Bool              <?> "Print json trace output at every step"-      , trace       :: w ::: Bool              <?> "Dump trace"-      , state       :: w ::: Maybe String      <?> "Path to state repository"-      , cache       :: w ::: Maybe String      <?> "Path to rpc cache repository"-      , rpc         :: w ::: Maybe URL         <?> "Fetch state from a remote node"-      , block       :: w ::: Maybe W256        <?> "Block state is be fetched from"-      , root        :: w ::: Maybe String      <?> "Path to  project root directory (default: . )"-      , projectType :: w ::: Maybe ProjectType <?> "Is this a Foundry or DappTools project (default: Foundry)"-      }-  | Test -- Run DSTest unit tests-      { root        :: w ::: Maybe String               <?> "Path to  project root directory (default: . )"-      , projectType   :: w ::: Maybe ProjectType        <?> "Is this a Foundry or DappTools project (default: Foundry)"-      , debug         :: w ::: Bool                     <?> "Run interactively"-      , jsontrace     :: w ::: Bool                     <?> "Print json trace output at every step"-      , fuzzRuns      :: w ::: Maybe Int                <?> "Number of times to run fuzz tests"-      , depth         :: w ::: Maybe Int                <?> "Number of transactions to explore"-      , replay        :: w ::: Maybe (Text, ByteString) <?> "Custom fuzz case to run/debug"-      , rpc           :: w ::: Maybe URL                <?> "Fetch state from a remote node"-      , verbose       :: w ::: Maybe Int                <?> "Append call trace: {1} failures {2} all"-      , coverage      :: w ::: Bool                     <?> "Coverage analysis"-      , state         :: w ::: Maybe String             <?> "Path to state repository"-      , cache         :: w ::: Maybe String             <?> "Path to rpc cache repository"-      , match         :: w ::: Maybe String             <?> "Test case filter - only run methods matching regex"-      , covMatch      :: w ::: Maybe String             <?> "Coverage filter - only print coverage for files matching regex"-      , solver        :: w ::: Maybe Text               <?> "Used SMT solver: z3 (default) or cvc5"-      , smtdebug      :: w ::: Bool                     <?> "Print smt queries sent to the solver"-      , ffi           :: w ::: Bool                     <?> "Allow the usage of the hevm.ffi() cheatcode (WARNING: this allows test authors to execute arbitrary code on your machine)"-      , smttimeout    :: w ::: Maybe Natural            <?> "Timeout given to SMT solver in seconds (default: 300)"-      , maxIterations :: w ::: Maybe Integer            <?> "Number of times we may revisit a particular branching point"-      , loopDetectionHeuristic :: w ::: LoopHeuristic   <!> "StackBased" <?> "Which heuristic should be used to determine if we are in a loop: StackBased (default) or Naive"-      , askSmtIterations :: w ::: Integer               <!> "1" <?> "Number of times we may revisit a particular branching point before we consult the smt solver to check reachability (default: 1)"-      }-  | Version--  deriving (Options.Generic)--type URL = Text----- For some reason haskell can't derive a--- parseField instance for (Text, ByteString)-instance Options.ParseField (Text, ByteString)--deriving instance Options.ParseField Word256-deriving instance Options.ParseField [Word256]--instance Options.ParseRecord (Command Options.Wrapped) where-  parseRecord =-    Options.parseRecordWithModifiers Options.lispCaseModifiers--data InitialStorage-  = Empty-  | Concrete [(W256, [(W256, W256)])]-  | Abstract-  deriving (Show, Read, Options.ParseField)--optsMode :: Command Options.Unwrapped -> Mode-optsMode x-  | x.debug = Debug-  | x.jsontrace = JsonTrace-  | otherwise = Run--applyCache :: (Maybe String, Maybe String) -> IO (VM -> VM)-applyCache (state, cache) =-  let applyState = flip Facts.apply-      applyCache' = flip Facts.applyCache-  in case (state, cache) of-    (Nothing, Nothing) -> do-      pure id-    (Nothing, Just cachePath) -> do-      facts <- Git.loadFacts (Git.RepoAt cachePath)-      pure $ applyCache' facts-    (Just statePath, Nothing) -> do-      facts <- Git.loadFacts (Git.RepoAt statePath)-      pure $ applyState facts-    (Just statePath, Just cachePath) -> do-      cacheFacts <- Git.loadFacts (Git.RepoAt cachePath)-      stateFacts <- Git.loadFacts (Git.RepoAt statePath)-      pure $ (applyState stateFacts) . (applyCache' cacheFacts)--unitTestOptions :: Command Options.Unwrapped -> SolverGroup -> Maybe BuildOutput -> IO UnitTestOptions-unitTestOptions cmd solvers buildOutput = do-  root <- getRoot cmd-  let srcInfo = maybe emptyDapp (dappInfo root) buildOutput--  vmModifier <- applyCache (cmd.state, cmd.cache)--  params <- getParametersFromEnvironmentVariables cmd.rpc--  let-    testn = params.number-    block' = if 0 == testn-       then EVM.Fetch.Latest-       else EVM.Fetch.BlockNumber testn--  pure UnitTestOptions-    { solvers = solvers-    , rpcInfo = case cmd.rpc of-         Just url -> Just (block', url)-         Nothing  -> Nothing-    , maxIter = cmd.maxIterations-    , askSmtIters = cmd.askSmtIterations-    , smtDebug = cmd.smtdebug-    , smtTimeout = cmd.smttimeout-    , solver = cmd.solver-    , covMatch = pack <$> cmd.covMatch-    , verbose = cmd.verbose-    , match = pack $ fromMaybe ".*" cmd.match-    , maxDepth = cmd.depth-    , fuzzRuns = fromMaybe 100 cmd.fuzzRuns-    , replay = do-        arg' <- cmd.replay-        pure (fst arg', LazyByteString.fromStrict (hexByteString "--replay" $ strip0x $ snd arg'))-    , vmModifier = vmModifier-    , testParams = params-    , dapp = srcInfo-    , ffiAllowed = cmd.ffi-    }--getFullVersion :: [Char]-getFullVersion = showVersion Paths.version <> " [" <> gitVersion <> "]"-  where-    gitInfo = $$tGitInfoCwdTry-    gitVersion = case gitInfo of-      Right val -> "git rev " <> giBranch val <>  "@" <> giHash val-      Left _ -> "no git revision present"--main :: IO ()-main = do-  cmd <- Options.unwrapRecord "hevm -- Ethereum evaluator"-  case cmd of-    Version {} ->putStrLn getFullVersion-    Symbolic {} -> do-      root <- getRoot cmd-      withCurrentDirectory root $ assert cmd-    Equivalence {} -> equivalence cmd-    Exec {} ->-      launchExec cmd-    Test {} -> do-      root <- getRoot cmd-      withCurrentDirectory root $ do-        cores <- unsafeInto <$> getNumProcessors-        solver <- getSolver cmd-        withSolvers solver cores cmd.smttimeout $ \solvers -> do-          buildOut <- readBuildOutput root (getProjectType cmd)-          case buildOut of-            Left e -> do-              putStrLn $ "Error: " <> e-              exitFailure-            Right out -> do-              -- TODO: which functions here actually require a BuildOutput, and which can take it as a Maybe?-              testOpts <- unitTestOptions cmd solvers (Just out)-              case (cmd.coverage, optsMode cmd) of-                (False, Run) -> do-                  res <- unitTest testOpts out.contracts cmd.cache-                  unless res exitFailure-                (False, Debug) -> liftIO $ TTY.main testOpts root (Just out)-                (False, JsonTrace) -> internalError "json traces not implemented for dappTest"-                (True, _) -> liftIO $ dappCoverage testOpts (optsMode cmd) out---equivalence :: Command Options.Unwrapped -> IO ()-equivalence cmd = do-  let bytecodeA = hexByteString "--code" . strip0x $ cmd.codeA-      bytecodeB = hexByteString "--code" . strip0x $ cmd.codeB-      veriOpts = VeriOpts { simp = True-                          , debug = False-                          , maxIter = cmd.maxIterations-                          , askSmtIters = cmd.askSmtIterations-                          , loopHeuristic = cmd.loopDetectionHeuristic-                          , rpcInfo = Nothing-                          }-  calldata <- buildCalldata cmd-  solver <- getSolver cmd-  withSolvers solver 3 Nothing $ \s -> do-    res <- equivalenceCheck s bytecodeA bytecodeB veriOpts calldata-    case any isCex res of-      False -> do-        putStrLn "No discrepancies found"-        when (any isTimeout res) $ do-          putStrLn "But timeout(s) occurred"-          exitFailure-      True -> do-        let cexs = mapMaybe getCex res-        T.putStrLn . T.unlines $-          [ "Not equivalent. The following inputs result in differing behaviours:"-          , "" , "-----", ""-          ] <> (intersperse (T.unlines [ "", "-----" ]) $ fmap (formatCex (AbstractBuf "txdata")) cexs)-        exitFailure--getSolver :: Command Options.Unwrapped -> IO Solver-getSolver cmd = case cmd.solver of-                  Nothing -> pure Z3-                  Just s -> case T.unpack s of-                              "z3" -> pure Z3-                              "cvc5" -> pure CVC5-                              input -> do-                                putStrLn $ "unrecognised solver: " <> input-                                exitFailure--getSrcInfo :: Command Options.Unwrapped -> IO DappInfo-getSrcInfo cmd = do-  root <- getRoot cmd-  withCurrentDirectory root $ do-    outExists <- doesDirectoryExist (root </> "out")-    if outExists-    then do-      buildOutput <- readBuildOutput root (getProjectType cmd)-      case buildOutput of-        Left _ -> pure emptyDapp-        Right o -> pure $ dappInfo root o-    else pure emptyDapp--getProjectType :: Command Options.Unwrapped -> ProjectType-getProjectType cmd = fromMaybe Foundry cmd.projectType--getRoot :: Command Options.Unwrapped -> IO FilePath-getRoot cmd = maybe getCurrentDirectory pure (cmd.root)----- | Builds a buffer representing calldata based on the given cli arguments-buildCalldata :: Command Options.Unwrapped -> IO (Expr Buf, [Prop])-buildCalldata cmd = case (cmd.calldata, cmd.sig) of-  -- fully abstract calldata-  (Nothing, Nothing) -> pure $ mkCalldata Nothing []-  -- fully concrete calldata-  (Just c, Nothing) -> pure (ConcreteBuf (hexByteString "bytes" . strip0x $ c), [])-  -- calldata according to given abi with possible specializations from the `arg` list-  (Nothing, Just sig') -> do-    method' <- functionAbi sig'-    pure $ mkCalldata (Just (Sig method'.methodSignature (snd <$> method'.inputs))) cmd.arg-  -- both args provided-  (_, _) -> do-    putStrLn "incompatible options provided: --calldata and --sig"-    exitFailure----- If function signatures are known, they should always be given for best results.-assert :: Command Options.Unwrapped -> IO ()-assert cmd = do-  let block'  = maybe EVM.Fetch.Latest EVM.Fetch.BlockNumber cmd.block-      rpcinfo = (,) block' <$> cmd.rpc-  calldata <- buildCalldata cmd-  preState <- symvmFromCommand cmd calldata-  let errCodes = fromMaybe defaultPanicCodes cmd.assertions-  cores <- unsafeInto <$> getNumProcessors-  let solverCount = fromMaybe cores cmd.numSolvers-  solver <- getSolver cmd-  withSolvers solver solverCount cmd.smttimeout $ \solvers -> do-    if cmd.debug then do-      srcInfo <- getSrcInfo cmd-      void $ TTY.runFromVM-        solvers-        rpcinfo-        cmd.maxIterations-        srcInfo-        preState-    else do-      let opts = VeriOpts {-        simp = True,-        debug = cmd.smtdebug,-        maxIter = cmd.maxIterations,-        askSmtIters = cmd.askSmtIterations,-        loopHeuristic = cmd.loopDetectionHeuristic,-        rpcInfo = rpcinfo-      }-      (expr, res) <- verify solvers opts preState (Just $ checkAssertions errCodes)-      case res of-        [Qed _] -> do-          putStrLn "\nQED: No reachable property violations discovered\n"-          showExtras solvers cmd calldata expr-        _ -> do-          let cexs = snd <$> mapMaybe getCex res-              timeouts = mapMaybe getTimeout res-              counterexamples-                | null cexs = []-                | otherwise =-                   [ ""-                   , "Discovered the following counterexamples:"-                   , ""-                   ] <> fmap (formatCex (fst calldata)) cexs-              unknowns-                | null timeouts = []-                | otherwise =-                   [ ""-                   , "Could not determine reachability of the following end states:"-                   , ""-                   ] <> fmap (formatExpr) timeouts-          T.putStrLn $ T.unlines (counterexamples <> unknowns)-          showExtras solvers cmd calldata expr-          exitFailure--showExtras :: SolverGroup -> Command Options.Unwrapped -> (Expr Buf, [Prop]) -> Expr End -> IO ()-showExtras solvers cmd calldata expr = do-  when cmd.showTree $ do-    putStrLn "=== Expression ===\n"-    T.putStrLn $ formatExpr expr-    putStrLn ""-  when cmd.showReachableTree $ do-    reached <- reachable solvers expr-    putStrLn "=== Reachable Expression ===\n"-    T.putStrLn (formatExpr . snd $ reached)-    putStrLn ""-  when cmd.getModels $ do-    putStrLn $ "=== Models for " <> show (Expr.numBranches expr) <> " branches ===\n"-    ms <- produceModels solvers expr-    forM_ ms (showModel (fst calldata))--getCex :: ProofResult a b c -> Maybe b-getCex (Cex c) = Just c-getCex _ = Nothing--getTimeout :: ProofResult a b c -> Maybe c-getTimeout (Timeout c) = Just c-getTimeout _ = Nothing--dappCoverage :: UnitTestOptions -> Mode -> BuildOutput -> IO ()-dappCoverage opts _ bo@(BuildOutput (Contracts cs) cache) = do-  let unitTests = findUnitTests opts.match $ Map.elems cs-  covs <- mconcat <$> mapM-    (coverageForUnitTestContract opts cs cache) unitTests-  let-    dapp = dappInfo "." bo-    f (k, vs) = do-      when (shouldPrintCoverage opts.covMatch (T.pack k)) $ do-        putStr ("\x1b[0m" ++ "————— hevm coverage for ") -- Prefixed with color reset-        putStrLn (k ++ " —————")-        putStrLn ""-        forM_ vs $ \(n, bs) -> do-          case ByteString.find (\x -> x /= 0x9 && x /= 0x20 && x /= 0x7d) bs of-            Nothing -> putStr "\x1b[38;5;240m" -- Gray (Coverage status isn't relevant)-            Just _ ->-              case n of-                -1 -> putStr "\x1b[38;5;240m" -- Gray (Coverage status isn't relevant)-                0  -> putStr "\x1b[31m" -- Red (Uncovered)-                _  -> putStr "\x1b[32m" -- Green (Covered)-          Char8.putStrLn bs-        putStrLn ""-  mapM_ f (Map.toList (coverageReport dapp covs))--shouldPrintCoverage :: Maybe Text -> Text -> Bool-shouldPrintCoverage (Just covMatch) file = regexMatches covMatch file-shouldPrintCoverage Nothing file = not (isTestOrLib file)--isTestOrLib :: Text -> Bool-isTestOrLib file = T.isSuffixOf ".t.sol" file || areAnyPrefixOf ["src/test/", "src/tests/", "lib/"] file--areAnyPrefixOf :: [Text] -> Text -> Bool-areAnyPrefixOf prefixes t = any (flip T.isPrefixOf t) prefixes--launchExec :: Command Options.Unwrapped -> IO ()-launchExec cmd = do-  dapp <- getSrcInfo cmd-  vm <- vmFromCommand cmd-  -- TODO: we shouldn't need solvers to execute this code-  withSolvers Z3 0 Nothing $ \solvers -> do-    case optsMode cmd of-      Run -> do-        vm' <- EVM.Stepper.interpret (EVM.Fetch.oracle solvers rpcinfo) vm EVM.Stepper.runFully-        when cmd.trace $ T.hPutStr stderr (showTraceTree dapp vm')-        case vm'.result of-          Just (VMFailure (Revert msg)) -> do-            let res = case msg of-                        ConcreteBuf bs -> bs-                        _ -> "<symbolic>"-            putStrLn $ "Revert: " <> (show $ ByteStringS res)-            exitWith (ExitFailure 2)-          Just (VMFailure err) -> do-            putStrLn $ "Error: " <> show err-            exitWith (ExitFailure 2)-          Just (Unfinished p) -> do-            putStrLn $ "Could not continue execution: " <> show p-            exitWith (ExitFailure 2)-          Just (VMSuccess buf) -> do-            let msg = case buf of-                  ConcreteBuf msg' -> msg'-                  _ -> "<symbolic>"-            print $ "Return: " <> (show $ ByteStringS msg)-            case cmd.state of-              Nothing -> pure ()-              Just path ->-                Git.saveFacts (Git.RepoAt path) (Facts.vmFacts vm')-            case cmd.cache of-              Nothing -> pure ()-              Just path ->-                Git.saveFacts (Git.RepoAt path) (Facts.cacheFacts vm'.cache)-          _ ->-            internalError "no EVM result"--      Debug -> void $ TTY.runFromVM solvers rpcinfo Nothing dapp vm-      --JsonTrace -> void $ execStateT (interpretWithTrace fetcher EVM.Stepper.runFully) vm-      _ -> internalError "TODO"-     where block = maybe EVM.Fetch.Latest EVM.Fetch.BlockNumber cmd.block-           rpcinfo = (,) block <$> cmd.rpc---- | Creates a (concrete) VM from command line options-vmFromCommand :: Command Options.Unwrapped -> IO VM-vmFromCommand cmd = do-  withCache <- applyCache (cmd.state, cmd.cache)--  (miner,ts,baseFee,blockNum,prevRan) <- case cmd.rpc of-    Nothing -> pure (0,Lit 0,0,0,0)-    Just url -> EVM.Fetch.fetchBlockFrom block url >>= \case-      Nothing -> error "Error: Could not fetch block"-      Just Block{..} -> pure ( coinbase-                             , timestamp-                             , baseFee-                             , number-                             , prevRandao-                             )--  contract <- case (cmd.rpc, cmd.address, cmd.code) of-    (Just url, Just addr', Just c) -> do-      EVM.Fetch.fetchContractFrom block url addr' >>= \case-        Nothing ->-          error $ "Error: contract not found: " <> show address-        Just contract ->-          -- if both code and url is given,-          -- fetch the contract and overwrite the code-          pure $-            initialContract  (mkCode $ hexByteString "--code" $ strip0x c)-              & set #balance  (contract.balance)-              & set #nonce    (contract.nonce)-              & set #external (contract.external)--    (Just url, Just addr', Nothing) ->-      EVM.Fetch.fetchContractFrom block url addr' >>= \case-        Nothing ->-          error $ "Error: contract not found: " <> show address-        Just contract -> pure contract--    (_, _, Just c)  ->-      pure $-        initialContract (mkCode $ hexByteString "--code" $ strip0x c)--    (_, _, Nothing) ->-      error "Error: must provide at least (rpc + address) or code"--  let ts' = case maybeLitWord ts of-        Just t -> t-        Nothing -> internalError "unexpected symbolic timestamp when executing vm test"--  pure $ EVM.Transaction.initTx $ withCache (vm0 baseFee miner ts' blockNum prevRan contract)-    where-        block   = maybe EVM.Fetch.Latest EVM.Fetch.BlockNumber cmd.block-        value   = word (.value) 0-        caller  = addr (.caller) 0-        origin  = addr (.origin) 0-        calldata = ConcreteBuf $ bytes (.calldata) ""-        decipher = hexByteString "bytes" . strip0x-        mkCode bs = if cmd.create-                    then InitCode bs mempty-                    else RuntimeCode (ConcreteRuntimeCode bs)-        address = if cmd.create-              then addr (.address) (createAddress origin (word (.nonce) 0))-              else addr (.address) 0xacab--        vm0 baseFee miner ts blockNum prevRan c = makeVm $ VMOpts-          { contract      = c-          , calldata      = (calldata, [])-          , value         = Lit value-          , address       = address-          , caller        = Expr.litAddr caller-          , origin        = origin-          , gas           = word64 (.gas) 0xffffffffffffffff-          , baseFee       = baseFee-          , priorityFee   = word (.priorityFee) 0-          , gaslimit      = word64 (.gaslimit) 0xffffffffffffffff-          , coinbase      = addr (.coinbase) miner-          , number        = word (.number) blockNum-          , timestamp     = Lit $ word (.timestamp) ts-          , blockGaslimit = word64 (.gaslimit) 0xffffffffffffffff-          , gasprice      = word (.gasprice) 0-          , maxCodeSize   = word (.maxcodesize) 0xffffffff-          , prevRandao    = word (.prevRandao) prevRan-          , schedule      = FeeSchedule.berlin-          , chainId       = word (.chainid) 1-          , create        = (.create) cmd-          , initialStorage = EmptyStore-          , txAccessList  = mempty -- TODO: support me soon-          , allowFFI      = False-          }-        word f def = fromMaybe def (f cmd)-        word64 f def = fromMaybe def (f cmd)-        addr f def = fromMaybe def (f cmd)-        bytes f def = maybe def decipher (f cmd)--symvmFromCommand :: Command Options.Unwrapped -> (Expr Buf, [Prop]) -> IO (VM)-symvmFromCommand cmd calldata = do-  (miner,blockNum,baseFee,prevRan) <- case cmd.rpc of-    Nothing -> pure (0,0,0,0)-    Just url -> EVM.Fetch.fetchBlockFrom block url >>= \case-      Nothing -> error "Error: Could not fetch block"-      Just Block{..} -> pure ( coinbase-                             , number-                             , baseFee-                             , prevRandao-                             )--  let-    caller = Caller 0-    ts = maybe Timestamp Lit cmd.timestamp-    callvalue = maybe (CallValue 0) Lit cmd.value-  -- TODO: rework this, ConcreteS not needed anymore-  let store = maybe AbstractStore parseInitialStorage (cmd.initialStorage)-  withCache <- applyCache (cmd.state, cmd.cache)--  contract <- case (cmd.rpc, cmd.address, cmd.code) of-    (Just url, Just addr', _) ->-      EVM.Fetch.fetchContractFrom block url addr' >>= \case-        Nothing ->-          error "Error: contract not found."-        Just contract' -> pure contract''-          where-            contract'' = case cmd.code of-              Nothing -> contract'-              -- if both code and url is given,-              -- fetch the contract and overwrite the code-              Just c -> initialContract (mkCode $ decipher c)-                        -- TODO: fix this-                        -- & set EVM.origStorage (view EVM.origStorage contract')-                        & set #balance     (contract'.balance)-                        & set #nonce       (contract'.nonce)-                        & set #external    (contract'.external)--    (_, _, Just c)  ->-      pure (initialContract . mkCode $ decipher c)-    (_, _, Nothing) ->-      error "Error: must provide at least (rpc + address) or code"--  pure $ (EVM.Transaction.initTx $ withCache $ vm0 baseFee miner ts blockNum prevRan calldata callvalue caller contract)-    & set (#env % #storage) store--  where-    decipher = hexByteString "bytes" . strip0x-    block   = maybe EVM.Fetch.Latest EVM.Fetch.BlockNumber cmd.block-    origin  = addr (.origin) 0-    mkCode bs = if cmd.create-                   then InitCode bs mempty-                   else RuntimeCode (ConcreteRuntimeCode bs)-    address = if cmd.create-          then addr (.address) (createAddress origin (word (.nonce) 0))-          else addr (.address) 0xacab-    vm0 baseFee miner ts blockNum prevRan cd callvalue caller c = makeVm $ VMOpts-      { contract      = c-      , calldata      = cd-      , value         = callvalue-      , address       = address-      , caller        = caller-      , origin        = origin-      , gas           = word64 (.gas) 0xffffffffffffffff-      , gaslimit      = word64 (.gaslimit) 0xffffffffffffffff-      , baseFee       = baseFee-      , priorityFee   = word (.priorityFee) 0-      , coinbase      = addr (.coinbase) miner-      , number        = word (.number) blockNum-      , timestamp     = ts-      , blockGaslimit = word64 (.gaslimit) 0xffffffffffffffff-      , gasprice      = word (.gasprice) 0-      , maxCodeSize   = word (.maxcodesize) 0xffffffff-      , prevRandao    = word (.prevRandao) prevRan-      , schedule      = FeeSchedule.berlin-      , chainId       = word (.chainid) 1-      , create        = (.create) cmd-      , initialStorage = maybe AbstractStore parseInitialStorage (cmd.initialStorage)-      , txAccessList  = mempty-      , allowFFI      = False-      }-    word f def = fromMaybe def (f cmd)-    addr f def = fromMaybe def (f cmd)-    word64 f def = fromMaybe def (f cmd)--parseInitialStorage :: InitialStorage -> Expr Storage-parseInitialStorage = \case-  Empty -> EmptyStore-  Concrete s -> ConcreteStore (Map.fromList $ fmap (second Map.fromList) s)-  Abstract -> AbstractStore
hevm.cabal view
@@ -2,44 +2,24 @@ name:   hevm version:-  0.51.3+  0.52.0 synopsis:-  Ethereum virtual machine evaluator-description:-  Hevm implements the Ethereum virtual machine semantics.-  .-  It can be used as a library, and it also comes with an executable-  that can run unit test suites, optionally with a visual TTY debugger.+  Symbolic EVM Evaluator homepage:-  https://github.com/dapphub/dapptools+  https://github.com/ethereum/hevm license:   AGPL-3.0-only author:-  Mikael Brockman, Martin Lundfall, dxo+  dxo, Martin Lundfall, Mikael Brockman maintainer:-  mikael@brockman.se, martin.lundfall@gmail.com, git@d-xo.org+  git@d-xo.org category:   Ethereum build-type:   Simple extra-source-files:   CHANGELOG.md-  bench/contracts/erc20.sol-  test/contracts/lib/test.sol-  test/contracts/lib/erc20.sol-  test/contracts/pass/trivial.sol-  test/contracts/pass/abstract.sol-  test/contracts/pass/cheatCodes.sol-  test/contracts/pass/constantinople.sol-  test/contracts/pass/dsProvePass.sol-  test/contracts/pass/invariants.sol-  test/contracts/pass/libraries.sol-  test/contracts/pass/loops.sol-  test/contracts/pass/rpc.sol-  test/contracts/fail/trivial.sol-  test/contracts/fail/cheatCodes.sol-  test/contracts/fail/dsProveFail.sol-  test/contracts/fail/invariantFail.sol+  bench/contracts/*.sol   test/scripts/convert_trace_to_json.sh  flag ci@@ -78,6 +58,7 @@     -Wno-unticked-promoted-constructors     -Wno-orphans     -Wno-ambiguous-fields+    -optc-Wno-ignored-attributes   default-language: GHC2021   default-extensions:     DuplicateRecordFields@@ -89,6 +70,7 @@     RecordWildCards     TypeFamilies     ViewPatterns+    DataKinds  library   import: shared@@ -98,25 +80,18 @@     EVM.Assembler,     EVM.Concrete,     EVM.Dapp,-    EVM.Debug,-    EVM.Dev,     EVM.Expr,     EVM.SMT,     EVM.Solvers,     EVM.Exec,-    EVM.Facts,-    EVM.Facts.Git,     EVM.Format,     EVM.Fetch,     EVM.FeeSchedule,-    EVM.Hexdump,     EVM.Op,-    EVM.Patricia,     EVM.Precompiled,     EVM.RLP,     EVM.Solidity,     EVM.Stepper,-    EVM.StorageLayout,     EVM.SymExec,     EVM.Traversals,     EVM.CSE,@@ -125,16 +100,21 @@     EVM.Types,     EVM.UnitTest,     EVM.Sign,-  if !os(windows)-    exposed-modules:-      EVM.TTY,-      EVM.TTYCenteredList   other-modules:     Paths_hevm   autogen-modules:     Paths_hevm-  if os(linux) || os(windows)+  if impl(ghc >= 9.4) && !os(darwin)+    -- darwin is skipped because it produces this error when building+    -- > ghc: loadArchive: Neither an archive, nor a fat archive: `/nix/store/l3lkdfm7sg1wwc850451cikqds766h15-clang-wrapper-11.1.0/bin/clang++'+    build-depends: system-cxx-std-lib+  elif !os(darwin)     extra-libraries: stdc+++  else+    -- extra-libraries: c+++    -- https://gitlab.haskell.org/ghc/ghc/-/issues/11829+    ld-options: -Wl,-keep_dwarf_unwind+    ghc-options: -fcompact-unwind   extra-libraries:     secp256k1, ff, gmp   c-sources:@@ -161,7 +141,6 @@     text                              >= 1.2.3 && < 2.1,     unordered-containers              >= 0.2.10 && < 0.3,     vector                            >= 0.12.1 && < 0.14,-    ansi-wl-pprint                    >= 0.6.9 && < 0.7,     base16                            >= 0.3.2.0 && < 0.3.3.0,     megaparsec                        >= 9.0.0 && < 10.0,     mtl                               >= 2.2.2 && < 2.3,@@ -183,6 +162,7 @@     multiset                          >= 0.3.4 && < 0.4,     operational                       >= 0.2.3 && < 0.3,     optparse-generic                  >= 1.3.1 && < 1.5,+    pretty-hex                        >= 1.1 && < 1.2,     quickcheck-text                   >= 0.1.2 && < 0.2,     restless-git                      >= 0.7 && < 0.8,     rosezipper                        >= 0.2 && < 0.3,@@ -192,7 +172,6 @@     regex-tdfa                        >= 1.2.3 && < 1.4,     base                              >= 4.9 && < 5,     here                              >= 1.2.13 && < 1.3,-    tuple                             >= 0.3.0.2 && < 0.4,     smt2-parser                       >= 0.1.0.1,     word-wrap                         >= 0.5 && < 0.6,     spool                             >= 0.1 && < 0.2,@@ -210,22 +189,17 @@ executable hevm   import: shared   hs-source-dirs:-    hevm-cli+    cli   main-is:-    hevm-cli.hs+    cli.hs   ghc-options: -threaded -with-rtsopts=-N   other-modules:     Paths_hevm   if os(darwin)     extra-libraries: c++-    ld-options: -Wl,-keep_dwarf_unwind-    ghc-options: -fcompact-unwind-  else-    extra-libraries: stdc++   build-depends:     QuickCheck,     aeson,-    ansi-wl-pprint,     async,     base,     base16,@@ -266,8 +240,6 @@   import: shared   hs-source-dirs:     test-  extra-libraries:-    secp256k1   other-modules:     Paths_hevm   autogen-modules:@@ -336,11 +308,6 @@     buildable: False   if os(darwin)     extra-libraries: c++-    -- https://gitlab.haskell.org/ghc/ghc/-/issues/11829-    ld-options: -Wl,-keep_dwarf_unwind-    ghc-options: -fcompact-unwind-  else-    extra-libraries: stdc++  --- Test Suites --- @@ -383,9 +350,7 @@   ghc-options:     -O2   if os(darwin)-     extra-libraries: c++-  else-     extra-libraries: stdc+++    extra-libraries: c++   other-modules:     Paths_hevm   autogen-modules:
src/EVM.hs view
@@ -12,2319 +12,2630 @@ import Optics.Operators.Unsafe  import EVM.ABI-import EVM.Concrete (createAddress, create2Address)-import EVM.Expr (readStorage, writeStorage, readByte, readWord, writeWord,-  writeByte, bufLength, indexWord, litAddr, readBytes, word256At, copySlice)-import EVM.Expr qualified as Expr-import EVM.FeeSchedule (FeeSchedule (..))-import EVM.Op-import EVM.Precompiled qualified-import EVM.Solidity-import EVM.Types-import EVM.Sign qualified--import Control.Monad.State.Strict hiding (state)-import Data.Bits (FiniteBits, countLeadingZeros, finiteBitSize)-import Data.ByteArray qualified as BA-import Data.ByteString (ByteString)-import Data.ByteString qualified as BS-import Data.ByteString.Lazy (fromStrict)-import Data.ByteString.Lazy qualified as LS-import Data.ByteString.Char8 qualified as Char8-import Data.Foldable (toList)-import Data.List (find)-import Data.Map.Strict (Map)-import Data.Map.Strict qualified as Map-import Data.Maybe (fromMaybe, fromJust)-import Data.Set (insert, member, fromList)-import Data.Sequence (Seq)-import Data.Sequence qualified as Seq-import Data.Text (unpack)-import Data.Text.Encoding (decodeUtf8, encodeUtf8)-import Data.Tree-import Data.Tree.Zipper qualified as Zipper-import Data.Tuple.Curry-import Data.Typeable-import Data.Vector qualified as V-import Data.Vector.Storable qualified as SV-import Data.Vector.Storable.Mutable qualified as SV-import Data.Word (Word8, Word32, Word64)-import Witch (into, unsafeInto)--import Crypto.Hash (Digest, SHA256, RIPEMD160)-import Crypto.Hash qualified as Crypto-import Crypto.Number.ModArithmetic (expFast)--blankState :: FrameState-blankState = FrameState-  { contract     = 0-  , codeContract = 0-  , code         = RuntimeCode (ConcreteRuntimeCode "")-  , pc           = 0-  , stack        = mempty-  , memory       = mempty-  , memorySize   = 0-  , calldata     = mempty-  , callvalue    = Lit 0-  , caller       = Lit 0-  , gas          = 0-  , returndata   = mempty-  , static       = False-  }---- | An "external" view of a contract's bytecode, appropriate for--- e.g. @EXTCODEHASH@.-bytecode :: Getter Contract (Expr Buf)-bytecode = #contractcode % to f-  where f (InitCode _ _) = mempty-        f (RuntimeCode (ConcreteRuntimeCode bs)) = ConcreteBuf bs-        f (RuntimeCode (SymbolicRuntimeCode ops)) = Expr.fromList ops---- * Data accessors--currentContract :: VM -> Maybe Contract-currentContract vm =-  Map.lookup vm.state.codeContract vm.env.contracts---- * Data constructors--makeVm :: VMOpts -> VM-makeVm o =-  let txaccessList = o.txAccessList-      txorigin = o.origin-      txtoAddr = o.address-      initialAccessedAddrs = fromList $ [txorigin, txtoAddr, o.coinbase] ++ [1..9] ++ (Map.keys txaccessList)-      initialAccessedStorageKeys = fromList $ foldMap (uncurry (map . (,))) (Map.toList txaccessList)-      touched = if o.create then [txorigin] else [txorigin, txtoAddr]-  in-  VM-  { result = Nothing-  , frames = mempty-  , tx = TxState-    { gasprice = o.gasprice-    , gaslimit = o.gaslimit-    , priorityFee = o.priorityFee-    , origin = txorigin-    , toAddr = txtoAddr-    , value = o.value-    , substate = SubState mempty touched initialAccessedAddrs initialAccessedStorageKeys mempty-    --, _accessList = txaccessList-    , isCreate = o.create-    , txReversion = Map.fromList-      [(o.address , o.contract )]-    }-  , logs = []-  , traces = Zipper.fromForest []-  , block = Block-    { coinbase = o.coinbase-    , timestamp = o.timestamp-    , number = o.number-    , prevRandao = o.prevRandao-    , maxCodeSize = o.maxCodeSize-    , gaslimit = o.blockGaslimit-    , baseFee = o.baseFee-    , schedule = o.schedule-    }-  , state = FrameState-    { pc = 0-    , stack = mempty-    , memory = mempty-    , memorySize = 0-    , code = o.contract.contractcode-    , contract = o.address-    , codeContract = o.address-    , calldata = fst o.calldata-    , callvalue = o.value-    , caller = o.caller-    , gas = o.gas-    , returndata = mempty-    , static = False-    }-  , env = Env-    { chainId = o.chainId-    , storage = o.initialStorage-    , origStorage = mempty-    , contracts = Map.fromList-      [(o.address, o.contract )]-    }-  , cache = Cache mempty mempty mempty-  , burned = 0-  , constraints = snd o.calldata-  , keccakEqs = mempty-  , iterations = mempty-  , allowFFI = o.allowFFI-  , overrideCaller = Nothing-  }---- | Initialize empty contract with given code-initialContract :: ContractCode -> Contract-initialContract contractCode = Contract-  { contractcode = contractCode-  , codehash = hashcode contractCode-  , balance  = 0-  , nonce    = if creation then 1 else 0-  , opIxMap  = mkOpIxMap contractCode-  , codeOps  = mkCodeOps contractCode-  , external = False-  } where-      creation = case contractCode of-        InitCode _ _  -> True-        RuntimeCode _ -> False---- * Opcode dispatch (exec1)---- | Update program counter-next :: (?op :: Word8) => EVM ()-next = modifying (#state % #pc) (+ (opSize ?op))---- | Executes the EVM one step-exec1 :: EVM ()-exec1 = do-  vm <- get--  let-    -- Convenient aliases-    mem  = vm.state.memory-    stk  = vm.state.stack-    self = vm.state.contract-    this = fromMaybe (internalError "state contract") (Map.lookup self vm.env.contracts)--    fees@FeeSchedule {..} = vm.block.schedule--    doStop = finishFrame (FrameReturned mempty)--  if self > 0x0 && self <= 0x9 then do-    -- call to precompile-    let ?op = 0x00 -- dummy value-    case bufLength vm.state.calldata of-      Lit calldatasize -> do-          copyBytesToMemory vm.state.calldata (Lit calldatasize) (Lit 0) (Lit 0)-          executePrecompile self vm.state.gas 0 calldatasize 0 0 []-          vmx <- get-          case vmx.state.stack of-            x:_ -> case x of-              Lit 0 ->-                fetchAccount self $ \_ -> do-                  touchAccount self-                  vmError PrecompileFailure-              Lit _ ->-                fetchAccount self $ \_ -> do-                  touchAccount self-                  out <- use (#state % #returndata)-                  finishFrame (FrameReturned out)-              e -> partial $-                     UnexpectedSymbolicArg vmx.state.pc "precompile returned a symbolic value" (wrap [e])-            _ ->-              underrun-      e -> partial $-             UnexpectedSymbolicArg vm.state.pc "cannot call precompiles with symbolic data" (wrap [e])--  else if vm.state.pc >= opslen vm.state.code-    then doStop--    else do-      let ?op = case vm.state.code of-                  InitCode conc _ -> BS.index conc vm.state.pc-                  RuntimeCode (ConcreteRuntimeCode bs) -> BS.index bs vm.state.pc-                  RuntimeCode (SymbolicRuntimeCode ops) ->-                    fromMaybe (internalError "could not analyze symbolic code") $-                      maybeLitByte $ ops V.! vm.state.pc--      case getOp (?op) of--        OpPush0 -> do-          limitStack 1 $-            burn g_base $ do-              next-              pushSym (Lit 0)--        OpPush n' -> do-          let n = into n'-              !xs = case vm.state.code of-                InitCode conc _ -> Lit $ word $ padRight n $ BS.take n (BS.drop (1 + vm.state.pc) conc)-                RuntimeCode (ConcreteRuntimeCode bs) -> Lit $ word $ BS.take n $ BS.drop (1 + vm.state.pc) bs-                RuntimeCode (SymbolicRuntimeCode ops) ->-                  let bytes = V.take n $ V.drop (1 + vm.state.pc) ops-                  in readWord (Lit 0) $ Expr.fromList $ padLeft' 32 bytes-          limitStack 1 $-            burn g_verylow $ do-              next-              pushSym xs--        OpDup i ->-          case preview (ix (into i - 1)) stk of-            Nothing -> underrun-            Just y ->-              limitStack 1 $-                burn g_verylow $ do-                  next-                  pushSym y--        OpSwap i ->-          if length stk < (into i) + 1-            then underrun-            else-              burn g_verylow $ do-                next-                zoom (#state % #stack) $ do-                  assign (ix 0) (stk ^?! ix (into i))-                  assign (ix (into i)) (stk ^?! ix 0)--        OpLog n ->-          notStatic $-          case stk of-            (xOffset':xSize':xs) ->-              if length xs < (into n)-              then underrun-              else-                forceConcrete2 (xOffset', xSize') "LOG" $ \(xOffset, xSize) -> do-                    let (topics, xs') = splitAt (into n) xs-                        bytes         = readMemory xOffset' xSize' vm-                        logs'         = (LogEntry (litAddr self) bytes topics) : vm.logs-                    burn (g_log + g_logdata * (unsafeInto xSize) + into n * g_logtopic) $-                      accessMemoryRange xOffset xSize $ do-                        traceTopLog logs'-                        next-                        assign (#state % #stack) xs'-                        assign #logs logs'-            _ ->-              underrun--        OpStop -> doStop--        OpAdd -> stackOp2 g_verylow (uncurry Expr.add)-        OpMul -> stackOp2 g_low (uncurry Expr.mul)-        OpSub -> stackOp2 g_verylow (uncurry Expr.sub)--        OpDiv -> stackOp2 g_low (uncurry Expr.div)--        OpSdiv -> stackOp2 g_low (uncurry Expr.sdiv)--        OpMod -> stackOp2 g_low (uncurry Expr.mod)--        OpSmod -> stackOp2 g_low (uncurry Expr.smod)-        OpAddmod -> stackOp3 g_mid (uncurryN Expr.addmod)-        OpMulmod -> stackOp3 g_mid (uncurryN Expr.mulmod)--        OpLt -> stackOp2 g_verylow (uncurry Expr.lt)-        OpGt -> stackOp2 g_verylow (uncurry Expr.gt)-        OpSlt -> stackOp2 g_verylow (uncurry Expr.slt)-        OpSgt -> stackOp2 g_verylow (uncurry Expr.sgt)--        OpEq -> stackOp2 g_verylow (uncurry Expr.eq)-        OpIszero -> stackOp1 g_verylow Expr.iszero--        OpAnd -> stackOp2 g_verylow (uncurry Expr.and)-        OpOr -> stackOp2 g_verylow (uncurry Expr.or)-        OpXor -> stackOp2 g_verylow (uncurry Expr.xor)-        OpNot -> stackOp1 g_verylow Expr.not--        OpByte -> stackOp2 g_verylow (\(i, w) -> Expr.padByte $ Expr.indexWord i w)--        OpShl -> stackOp2 g_verylow (uncurry Expr.shl)-        OpShr -> stackOp2 g_verylow (uncurry Expr.shr)-        OpSar -> stackOp2 g_verylow (uncurry Expr.sar)--        -- more accurately refered to as KECCAK-        OpSha3 ->-          case stk of-            xOffset':xSize':xs ->-              forceConcrete xOffset' "sha3 offset must be concrete" $-                \xOffset -> forceConcrete xSize' "sha3 size must be concrete" $ \xSize ->-                  burn (g_sha3 + g_sha3word * ceilDiv (unsafeInto xSize) 32) $-                    accessMemoryRange xOffset xSize $ do-                      hash <- case readMemory xOffset' xSize' vm of-                                          ConcreteBuf bs -> do-                                            let hash' = keccak' bs-                                            eqs <- use #keccakEqs-                                            assign #keccakEqs $-                                              PEq (Lit hash') (Keccak (ConcreteBuf bs)):eqs-                                            pure $ Lit hash'-                                          buf -> pure $ Keccak buf-                      next-                      assign (#state % #stack) (hash : xs)-            _ -> underrun--        OpAddress ->-          limitStack 1 $-            burn g_base (next >> push (into self))--        OpBalance ->-          case stk of-            x':xs -> forceConcrete x' "BALANCE" $ \x ->-              accessAndBurn (unsafeInto x) $-                fetchAccount (unsafeInto x) $ \c -> do-                  next-                  assign (#state % #stack) xs-                  push c.balance-            [] ->-              underrun--        OpOrigin ->-          limitStack 1 . burn g_base $-            next >> push (into vm.tx.origin)--        OpCaller ->-          limitStack 1 . burn g_base $-            next >> pushSym vm.state.caller--        OpCallvalue ->-          limitStack 1 . burn g_base $-            next >> pushSym vm.state.callvalue--        OpCalldataload -> stackOp1 g_verylow $-          \ind -> Expr.readWord ind vm.state.calldata--        OpCalldatasize ->-          limitStack 1 . burn g_base $-            next >> pushSym (bufLength vm.state.calldata)--        OpCalldatacopy ->-          case stk of-            xTo':xFrom:xSize':xs ->-              forceConcrete2 (xTo', xSize') "CALLDATACOPY" $-                \(xTo, xSize) ->-                  burn (g_verylow + g_copy * ceilDiv (unsafeInto xSize) 32) $-                    accessMemoryRange xTo xSize $ do-                      next-                      assign (#state % #stack) xs-                      copyBytesToMemory vm.state.calldata xSize' xFrom xTo'-            _ -> underrun--        OpCodesize ->-          limitStack 1 . burn g_base $-            next >> pushSym (codelen vm.state.code)--        OpCodecopy ->-          case stk of-            memOffset':codeOffset:n':xs ->-              forceConcrete2 (memOffset', n') "CODECOPY" $-                \(memOffset,n) -> do-                  case toWord64 n of-                    Nothing -> vmError IllegalOverflow-                    Just n'' ->-                      if n'' <= ( (maxBound :: Word64) - g_verylow ) `div` g_copy * 32 then-                        burn (g_verylow + g_copy * ceilDiv (unsafeInto n) 32) $-                          accessMemoryRange memOffset n $ do-                            next-                            assign (#state % #stack) xs-                            copyBytesToMemory (toBuf vm.state.code) n' codeOffset memOffset'-                      else vmError IllegalOverflow-            _ -> underrun--        OpGasprice ->-          limitStack 1 . burn g_base $-            next >> push vm.tx.gasprice--        OpExtcodesize ->-          case stk of-            x':xs -> case x' of-              Lit x -> if x == into cheatCode-                then do-                  next-                  assign (#state % #stack) xs-                  pushSym (Lit 1)-                else-                  accessAndBurn (unsafeInto x) $-                    fetchAccount (unsafeInto x) $ \c -> do-                      next-                      assign (#state % #stack) xs-                      pushSym (bufLength (view bytecode c))-              _ -> do-                assign (#state % #stack) xs-                pushSym (CodeSize x')-                next-            [] ->-              underrun--        OpExtcodecopy ->-          case stk of-            extAccount':memOffset':codeOffset:codeSize':xs ->-              forceConcrete3 (extAccount', memOffset', codeSize') "EXTCODECOPY" $-                \(extAccount, memOffset, codeSize) -> do-                  acc <- accessAccountForGas (unsafeInto extAccount)-                  let cost = if acc then g_warm_storage_read else g_cold_account_access-                  burn (cost + g_copy * ceilDiv (unsafeInto codeSize) 32) $-                    accessMemoryRange memOffset codeSize $-                      fetchAccount (unsafeInto extAccount) $ \c -> do-                        next-                        assign (#state % #stack) xs-                        copyBytesToMemory (view bytecode c) codeSize' codeOffset memOffset'-            _ -> underrun--        OpReturndatasize ->-          limitStack 1 . burn g_base $-            next >> pushSym (bufLength vm.state.returndata)--        OpReturndatacopy ->-          case stk of-            xTo':xFrom:xSize':xs -> forceConcrete2 (xTo', xSize') "RETURNDATACOPY" $-              \(xTo, xSize) ->-                burn (g_verylow + g_copy * ceilDiv (unsafeInto xSize) 32) $-                  accessMemoryRange xTo xSize $ do-                    next-                    assign (#state % #stack) xs--                    let jump True = vmError ReturnDataOutOfBounds-                        jump False = copyBytesToMemory vm.state.returndata xSize' xFrom xTo'--                    case (xFrom, bufLength vm.state.returndata) of-                      (Lit f, Lit l) ->-                        jump $ l < f + xSize || f + xSize < f-                      _ -> do-                        let oob = Expr.lt (bufLength vm.state.returndata) (Expr.add xFrom xSize')-                            overflow = Expr.lt (Expr.add xFrom xSize') (xFrom)-                        loc <- codeloc-                        branch loc (Expr.or oob overflow) jump-            _ -> underrun--        OpExtcodehash ->-          case stk of-            x':xs -> forceConcrete x' "EXTCODEHASH" $ \x ->-              accessAndBurn (unsafeInto x) $ do-                next-                assign (#state % #stack) xs-                fetchAccount (unsafeInto x) $ \c ->-                   if accountEmpty c-                     then push 0-                     else pushSym $ keccak (view bytecode c)-            [] ->-              underrun--        OpBlockhash -> do-          -- We adopt the fake block hash scheme of the VMTests,-          -- so that blockhash(i) is the hash of i as decimal ASCII.-          stackOp1 g_blockhash $ \case-            Lit i -> if i + 256 < vm.block.number || i >= vm.block.number-                     then Lit 0-                     else (into i :: Integer) & show & Char8.pack & keccak' & Lit-            i -> BlockHash i--        OpCoinbase ->-          limitStack 1 . burn g_base $-            next >> push (into vm.block.coinbase)--        OpTimestamp ->-          limitStack 1 . burn g_base $-            next >> pushSym vm.block.timestamp--        OpNumber ->-          limitStack 1 . burn g_base $-            next >> push vm.block.number--        OpPrevRandao -> do-          limitStack 1 . burn g_base $-            next >> push vm.block.prevRandao--        OpGaslimit ->-          limitStack 1 . burn g_base $-            next >> push (into vm.block.gaslimit)--        OpChainid ->-          limitStack 1 . burn g_base $-            next >> push vm.env.chainId--        OpSelfbalance ->-          limitStack 1 . burn g_low $-            next >> push this.balance--        OpBaseFee ->-          limitStack 1 . burn g_base $-            next >> push vm.block.baseFee--        OpPop ->-          case stk of-            _:xs -> burn g_base (next >> assign (#state % #stack) xs)-            _    -> underrun--        OpMload ->-          case stk of-            x':xs -> forceConcrete x' "MLOAD" $ \x ->-              burn g_verylow $-                accessMemoryWord x $ do-                  next-                  assign (#state % #stack) (readWord (Lit x) mem : xs)-            _ -> underrun--        OpMstore ->-          case stk of-            x':y:xs -> forceConcrete x' "MSTORE index" $ \x ->-              burn g_verylow $-                accessMemoryWord x $ do-                  next-                  assign (#state % #memory) (writeWord (Lit x) y mem)-                  assign (#state % #stack) xs-            _ -> underrun--        OpMstore8 ->-          case stk of-            x':y:xs -> forceConcrete x' "MSTORE8" $ \x ->-              burn g_verylow $-                accessMemoryRange x 1 $ do-                  let yByte = indexWord (Lit 31) y-                  next-                  modifying (#state % #memory) (writeByte (Lit x) yByte)-                  assign (#state % #stack) xs-            _ -> underrun--        OpSload ->-          case stk of-            x:xs -> do-              acc <- accessStorageForGas self x-              let cost = if acc then g_warm_storage_read else g_cold_sload-              burn cost $-                accessStorage self x $ \y -> do-                  next-                  assign (#state % #stack) (y:xs)-            _ -> underrun--        OpSstore ->-          notStatic $-          case stk of-            x:new:xs ->-              accessStorage self x $ \current -> do-                availableGas <- use (#state % #gas)--                if availableGas <= g_callstipend then-                  finishFrame (FrameErrored (OutOfGas availableGas g_callstipend))-                else do-                  let-                    original =-                      case readStorage (litAddr self) x (ConcreteStore vm.env.origStorage) of-                        Just (Lit v) -> v-                        _ -> 0-                    storage_cost =-                      case (maybeLitWord current, maybeLitWord new) of-                        (Just current', Just new') ->-                           if (current' == new') then g_sload-                           else if (current' == original) && (original == 0) then g_sset-                           else if (current' == original) then g_sreset-                           else g_sload--                        -- if any of the arguments are symbolic,-                        -- assume worst case scenario-                        _ -> g_sset--                  acc <- accessStorageForGas self x-                  let cold_storage_cost = if acc then 0 else g_cold_sload-                  burn (storage_cost + cold_storage_cost) $ do-                    next-                    assign (#state % #stack) xs-                    modifying (#env % #storage) (writeStorage (litAddr self) x new)--                    case (maybeLitWord current, maybeLitWord new) of-                       (Just current', Just new') ->-                          unless (current' == new') $-                            if current' == original then-                              when (original /= 0 && new' == 0) $-                                refund (g_sreset + g_access_list_storage_key)-                            else do-                              when (original /= 0) $-                                if current' == 0-                                then unRefund (g_sreset + g_access_list_storage_key)-                                else when (new' == 0) $ refund (g_sreset + g_access_list_storage_key)-                              when (original == new') $-                                if original == 0-                                then refund (g_sset - g_sload)-                                else refund (g_sreset - g_sload)-                       -- if any of the arguments are symbolic,-                       -- don't change the refund counter-                       _ -> noop-            _ -> underrun--        OpJump ->-          case stk of-            x:xs ->-              burn g_mid $ forceConcrete x "JUMP: symbolic jumpdest" $ \x' ->-                case toInt x' of-                  Nothing -> vmError BadJumpDestination-                  Just i -> checkJump i xs-            _ -> underrun--        OpJumpi -> do-          case stk of-            (x:y:xs) -> forceConcrete x "JUMPI: symbolic jumpdest" $ \x' ->-                burn g_high $-                  let jump :: Bool -> EVM ()-                      jump False = assign (#state % #stack) xs >> next-                      jump _    = case toInt x' of-                        Nothing -> vmError BadJumpDestination-                        Just i -> checkJump i xs-                  in do-                    loc <- codeloc-                    branch loc y jump-            _ -> underrun--        OpPc ->-          limitStack 1 . burn g_base $-            next >> push (unsafeInto vm.state.pc)--        OpMsize ->-          limitStack 1 . burn g_base $-            next >> push (into vm.state.memorySize)--        OpGas ->-          limitStack 1 . burn g_base $-            next >> push (into (vm.state.gas - g_base))--        OpJumpdest -> burn g_jumpdest next--        OpExp ->-          -- NOTE: this can be done symbolically using unrolling like this:-          --       https://hackage.haskell.org/package/sbv-9.0/docs/src/Data.SBV.Core.Model.html#.%5E-          --       However, it requires symbolic gas, since the gas depends on the exponent-          case stk of-            base:exponent':xs -> forceConcrete exponent' "EXP: symbolic exponent" $ \exponent ->-              let cost = if exponent == 0-                         then g_exp-                         else g_exp + g_expbyte * unsafeInto (ceilDiv (1 + log2 exponent) 8)-              in burn cost $ do-                next-                (#state % #stack) .= Expr.exp base exponent' : xs-            _ -> underrun--        OpSignextend -> stackOp2 g_low (uncurry Expr.sex)--        OpCreate ->-          notStatic $-          case stk of-            xValue':xOffset':xSize':xs -> forceConcrete3 (xValue', xOffset', xSize') "CREATE" $-              \(xValue, xOffset, xSize) -> do-                accessMemoryRange xOffset xSize $ do-                  availableGas <- use (#state % #gas)-                  let-                    newAddr = createAddress self this.nonce-                    (cost, gas') = costOfCreate fees availableGas xSize False-                  _ <- accessAccountForGas newAddr-                  burn cost $ do-                    let initCode = readMemory xOffset' xSize' vm-                    create self this xSize gas' xValue xs newAddr initCode-            _ -> underrun--        OpCall ->-          case stk of-            xGas':xTo:xValue':xInOffset':xInSize':xOutOffset':xOutSize':xs ->-              forceConcrete6 (xGas', xValue', xInOffset', xInSize', xOutOffset', xOutSize') "CALL" $-              \(xGas, xValue, xInOffset, xInSize, xOutOffset, xOutSize) ->-                (if xValue > 0 then notStatic else id) $-                  delegateCall this (unsafeInto xGas) xTo xTo xValue xInOffset xInSize xOutOffset xOutSize xs $ \callee -> do-                    let from' = fromMaybe self vm.overrideCaller-                    zoom #state $ do-                      assign #callvalue (Lit xValue)-                      assign #caller (litAddr from')-                      assign #contract callee-                    assign #overrideCaller Nothing-                    touchAccount from'-                    touchAccount callee-                    transfer from' callee xValue-            _ ->-              underrun--        OpCallcode ->-          case stk of-            xGas':xTo:xValue':xInOffset':xInSize':xOutOffset':xOutSize':xs ->-              forceConcrete6 (xGas', xValue', xInOffset', xInSize', xOutOffset', xOutSize') "CALLCODE" $-              \(xGas, xValue, xInOffset, xInSize, xOutOffset, xOutSize) ->-                delegateCall this (unsafeInto xGas) xTo (litAddr self) xValue xInOffset xInSize xOutOffset xOutSize xs $ \_ -> do-                  zoom #state $ do-                    assign #callvalue (Lit xValue)-                    assign #caller $ litAddr $ fromMaybe self vm.overrideCaller-                  assign #overrideCaller Nothing-                  touchAccount self-            _ ->-              underrun--        OpReturn ->-          case stk of-            xOffset':xSize':_ -> forceConcrete2 (xOffset', xSize') "RETURN" $ \(xOffset, xSize) ->-              accessMemoryRange xOffset xSize $ do-                let-                  output = readMemory xOffset' xSize' vm-                  codesize = fromMaybe (internalError "processing opcode RETURN. Cannot return dynamically sized abstract data")-                               . maybeLitWord . bufLength $ output-                  maxsize = vm.block.maxCodeSize-                  creation = case vm.frames of-                    [] -> vm.tx.isCreate-                    frame:_ -> case frame.context of-                       CreationContext {} -> True-                       CallContext {} -> False-                if creation-                then-                  if codesize > maxsize-                  then-                    finishFrame (FrameErrored (MaxCodeSizeExceeded maxsize codesize))-                  else do-                    let frameReturned = burn (g_codedeposit * unsafeInto codesize) $-                                          finishFrame (FrameReturned output)-                        frameErrored = finishFrame $ FrameErrored InvalidFormat-                    case readByte (Lit 0) output of-                      LitByte 0xef -> frameErrored-                      LitByte _ -> frameReturned-                      y -> do-                        loc <- codeloc-                        branch loc (Expr.eqByte y (LitByte 0xef)) $ \case-                          True -> frameErrored-                          False -> frameReturned-                else-                   finishFrame (FrameReturned output)-            _ -> underrun--        OpDelegatecall ->-          case stk of-            xGas':xTo:xInOffset':xInSize':xOutOffset':xOutSize':xs ->-              forceConcrete5 (xGas', xInOffset', xInSize', xOutOffset', xOutSize') "DELEGATECALL" $-              \(xGas, xInOffset, xInSize, xOutOffset, xOutSize) ->-                delegateCall this (unsafeInto xGas) xTo (litAddr self) 0 xInOffset xInSize xOutOffset xOutSize xs $ \_ -> do-                  touchAccount self-            _ -> underrun--        OpCreate2 -> notStatic $-          case stk of-            xValue':xOffset':xSize':xSalt':xs ->-              forceConcrete4 (xValue', xOffset', xSize', xSalt') "CREATE2" $-              \(xValue, xOffset, xSize, xSalt) ->-                accessMemoryRange xOffset xSize $ do-                  availableGas <- use (#state % #gas)--                  forceConcreteBuf (readMemory xOffset' xSize' vm) "CREATE2" $-                    \initCode -> do-                      let-                        newAddr  = create2Address self xSalt initCode-                        (cost, gas') = costOfCreate fees availableGas xSize True-                      _ <- accessAccountForGas newAddr-                      burn cost $-                        create self this xSize gas' xValue xs newAddr (ConcreteBuf initCode)-            _ -> underrun--        OpStaticcall ->-          case stk of-            xGas':xTo:xInOffset':xInSize':xOutOffset':xOutSize':xs ->-              forceConcrete5 (xGas', xInOffset', xInSize', xOutOffset', xOutSize') "STATICCALL" $-              \(xGas, xInOffset, xInSize, xOutOffset, xOutSize) -> do-                delegateCall this (unsafeInto xGas) xTo xTo 0 xInOffset xInSize xOutOffset xOutSize xs $ \callee -> do-                  zoom #state $ do-                    assign #callvalue (Lit 0)-                    assign #caller $ litAddr $ fromMaybe self (vm.overrideCaller)-                    assign #contract callee-                    assign #static True-                  assign #overrideCaller Nothing-                  touchAccount self-                  touchAccount callee-            _ ->-              underrun--        OpSelfdestruct ->-          notStatic $-          case stk of-            [] -> underrun-            (xTo':_) -> forceConcrete xTo' "SELFDESTRUCT" $ \(unsafeInto -> xTo) -> do-              acc <- accessAccountForGas xTo-              let cost = if acc then 0 else g_cold_account_access-                  funds = this.balance-                  recipientExists = accountExists xTo vm-                  c_new = if not recipientExists && funds /= 0-                          then g_selfdestruct_newaccount-                          else 0-              burn (g_selfdestruct + c_new + cost) $ do-                   selfdestruct self-                   touchAccount xTo--                   if funds /= 0-                   then fetchAccount xTo $ \_ -> do-                          #env % #contracts % ix xTo % #balance %= (+ funds)-                          assign (#env % #contracts % ix self % #balance) 0-                          doStop-                   else doStop--        OpRevert ->-          case stk of-            xOffset':xSize':_ -> forceConcrete2 (xOffset', xSize') "REVERT" $ \(xOffset, xSize) ->-              accessMemoryRange xOffset xSize $ do-                let output = readMemory xOffset' xSize' vm-                finishFrame (FrameReverted output)-            _ -> underrun--        OpUnknown xxx ->-          vmError $ UnrecognizedOpcode xxx--transfer :: Addr -> Addr -> W256 -> EVM ()-transfer _ _ 0 = pure ()-transfer xFrom xTo xValue = do-    sb <- preuse $ #env % #contracts % ix xFrom % #balance-    case sb of-      Just srcBal ->-        if xValue > srcBal-        then vmError $ BalanceTooLow xValue srcBal-        else do-          (#env % #contracts % ix xFrom % #balance) %= (subtract xValue)-          (#env % #contracts % ix xTo % #balance) %= (+ xValue)-      Nothing -> vmError $ BalanceTooLow xValue 0---- | Checks a *CALL for failure; OOG, too many callframes, memory access etc.-callChecks-  :: (?op :: Word8)-  => Contract -> Word64 -> Addr -> Addr -> W256 -> W256 -> W256 -> W256 -> W256 -> [Expr EWord]-   -- continuation with gas available for call-  -> (Word64 -> EVM ())-  -> EVM ()-callChecks this xGas xContext xTo xValue xInOffset xInSize xOutOffset xOutSize xs continue = do-  vm <- get-  let fees = vm.block.schedule-  accessMemoryRange xInOffset xInSize $-    accessMemoryRange xOutOffset xOutSize $ do-      availableGas <- use (#state % #gas)-      let recipientExists = accountExists xContext vm-      (cost, gas') <- costOfCall fees recipientExists xValue availableGas xGas xTo-      burn (cost - gas') $ do-        if xValue > this.balance-        then do-          assign (#state % #stack) (Lit 0 : xs)-          assign (#state % #returndata) mempty-          pushTrace $ ErrorTrace (BalanceTooLow xValue this.balance)-          next-        else if length vm.frames >= 1024-             then do-               assign (#state % #stack) (Lit 0 : xs)-               assign (#state % #returndata) mempty-               pushTrace $ ErrorTrace CallDepthLimitReached-               next-             else continue gas'--precompiledContract-  :: (?op :: Word8)-  => Contract-  -> Word64-  -> Addr-  -> Addr-  -> W256-  -> W256 -> W256 -> W256 -> W256-  -> [Expr EWord]-  -> EVM ()-precompiledContract this xGas precompileAddr recipient xValue inOffset inSize outOffset outSize xs =-  callChecks this xGas recipient precompileAddr xValue inOffset inSize outOffset outSize xs $ \gas' ->-  do-    executePrecompile precompileAddr gas' inOffset inSize outOffset outSize xs-    self <- use (#state % #contract)-    stk <- use (#state % #stack)-    pc' <- use (#state % #pc)-    result' <- use #result-    case result' of-      Nothing -> case stk of-        x:_ -> case maybeLitWord x of-          Just 0 ->-            pure ()-          Just 1 ->-            fetchAccount recipient $ \_ -> do-              transfer self recipient xValue-              touchAccount self-              touchAccount recipient-          _ -> partial $ UnexpectedSymbolicArg pc' "unexpected return value from precompile" (wrap [x])-        _ -> underrun-      _ -> pure ()--executePrecompile-  :: (?op :: Word8)-  => Addr-  -> Word64 -> W256 -> W256 -> W256 -> W256 -> [Expr EWord]-  -> EVM ()-executePrecompile preCompileAddr gasCap inOffset inSize outOffset outSize xs  = do-  vm <- get-  let input = readMemory (Lit inOffset) (Lit inSize) vm-      fees = vm.block.schedule-      cost = costOfPrecompile fees preCompileAddr input-      notImplemented = internalError $ "precompile at address " <> show preCompileAddr <> " not yet implemented"-      precompileFail = burn (gasCap - cost) $ do-                         assign (#state % #stack) (Lit 0 : xs)-                         pushTrace $ ErrorTrace PrecompileFailure-                         next-  if cost > gasCap then-    burn gasCap $ do-      assign (#state % #stack) (Lit 0 : xs)-      next-  else burn cost $-    case preCompileAddr of-      -- ECRECOVER-      0x1 ->-        -- TODO: support symbolic variant-        forceConcreteBuf input "ECRECOVER" $ \input' -> do-          case EVM.Precompiled.execute 0x1 (truncpadlit 128 input') 32 of-            Nothing -> do-              -- return no output for invalid signature-              assign (#state % #stack) (Lit 1 : xs)-              assign (#state % #returndata) mempty-              next-            Just output -> do-              assign (#state % #stack) (Lit 1 : xs)-              assign (#state % #returndata) (ConcreteBuf output)-              copyBytesToMemory (ConcreteBuf output) (Lit outSize) (Lit 0) (Lit outOffset)-              next--      -- SHA2-256-      0x2 ->-        forceConcreteBuf input "SHA2-256" $ \input' -> do-          let-            hash = sha256Buf input'-            sha256Buf x = ConcreteBuf $ BA.convert (Crypto.hash x :: Digest SHA256)-          assign (#state % #stack) (Lit 1 : xs)-          assign (#state % #returndata) hash-          copyBytesToMemory hash (Lit outSize) (Lit 0) (Lit outOffset)-          next--      -- RIPEMD-160-      0x3 ->-        -- TODO: support symbolic variant-        forceConcreteBuf input "RIPEMD160" $ \input' -> do-          let-            padding = BS.pack $ replicate 12 0-            hash' = BA.convert (Crypto.hash input' :: Digest RIPEMD160)-            hash  = ConcreteBuf $ padding <> hash'-          assign (#state % #stack) (Lit 1 : xs)-          assign (#state % #returndata) hash-          copyBytesToMemory hash (Lit outSize) (Lit 0) (Lit outOffset)-          next--      -- IDENTITY-      0x4 -> do-          assign (#state % #stack) (Lit 1 : xs)-          assign (#state % #returndata) input-          copyCallBytesToMemory input (Lit outSize) (Lit 0) (Lit outOffset)-          next--      -- MODEXP-      0x5 ->-        -- TODO: support symbolic variant-        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 (Lit outSize) (Lit 0) (Lit outOffset)-          next--      -- ECADD-      0x6 ->-        -- TODO: support symbolic variant-        forceConcreteBuf input "ECADD" $ \input' ->-          case EVM.Precompiled.execute 0x6 (truncpadlit 128 input') 64 of-            Nothing -> precompileFail-            Just output -> do-              let truncpaddedOutput = ConcreteBuf $ truncpadlit 64 output-              assign (#state % #stack) (Lit 1 : xs)-              assign (#state % #returndata) truncpaddedOutput-              copyBytesToMemory truncpaddedOutput (Lit outSize) (Lit 0) (Lit outOffset)-              next--      -- ECMUL-      0x7 ->-        -- TODO: support symbolic variant-        forceConcreteBuf input "ECMUL" $ \input' ->-          case EVM.Precompiled.execute 0x7 (truncpadlit 96 input') 64 of-          Nothing -> precompileFail-          Just output -> do-            let truncpaddedOutput = ConcreteBuf $ truncpadlit 64 output-            assign (#state % #stack) (Lit 1 : xs)-            assign (#state % #returndata) truncpaddedOutput-            copyBytesToMemory truncpaddedOutput (Lit outSize) (Lit 0) (Lit outOffset)-            next--      -- ECPAIRING-      0x8 ->-        -- TODO: support symbolic variant-        forceConcreteBuf input "ECPAIR" $ \input' ->-          case EVM.Precompiled.execute 0x8 input' 32 of-          Nothing -> precompileFail-          Just output -> do-            let truncpaddedOutput = ConcreteBuf $ truncpadlit 32 output-            assign (#state % #stack) (Lit 1 : xs)-            assign (#state % #returndata) truncpaddedOutput-            copyBytesToMemory truncpaddedOutput (Lit outSize) (Lit 0) (Lit outOffset)-            next--      -- BLAKE2-      0x9 ->-        -- TODO: support symbolic variant-        forceConcreteBuf input "BLAKE2" $ \input' -> do-          case (BS.length input', 1 >= BS.last input') of-            (213, True) -> case EVM.Precompiled.execute 0x9 input' 64 of-              Just output -> do-                let truncpaddedOutput = ConcreteBuf $ truncpadlit 64 output-                assign (#state % #stack) (Lit 1 : xs)-                assign (#state % #returndata) truncpaddedOutput-                copyBytesToMemory truncpaddedOutput (Lit outSize) (Lit 0) (Lit outOffset)-                next-              Nothing -> precompileFail-            _ -> precompileFail--      _ -> notImplemented--truncpadlit :: Int -> ByteString -> ByteString-truncpadlit n xs = if m > n then BS.take n xs-                   else BS.append xs (BS.replicate (n - m) 0)-  where m = BS.length xs--lazySlice :: W256 -> W256 -> ByteString -> LS.ByteString-lazySlice offset size bs =-  let bs' = LS.take (unsafeInto size) (LS.drop (unsafeInto offset) (fromStrict bs))-  in bs' <> LS.replicate (unsafeInto size - LS.length bs') 0--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-  in (lenb, lene, lenm)----- checks if a range of ByteString bs starting at offset and length size is all zeros.-isZero :: W256 -> W256 -> ByteString -> Bool-isZero offset size bs =-  LS.all (== 0) $-    LS.take (unsafeInto size) $-      LS.drop (unsafeInto offset) $-        fromStrict bs--asInteger :: LS.ByteString -> Integer-asInteger xs = if xs == mempty then 0-  else 256 * asInteger (LS.init xs)-      + into (LS.last xs)---- * Opcode helper actions--noop :: Monad m => m ()-noop = pure ()--pushTo :: MonadState s m => Lens s s [a] [a] -> a -> m ()-pushTo f x = f %= (x :)--pushToSequence :: MonadState s m => Setter s s (Seq a) (Seq a) -> a -> m ()-pushToSequence f x = f %= (Seq.|> x)--getCodeLocation :: VM -> CodeLocation-getCodeLocation vm = (vm.state.contract, vm.state.pc)--query :: Query -> EVM ()-query = assign #result . Just . HandleEffect . Query--choose :: Choose -> EVM ()-choose = assign #result . Just . HandleEffect . Choose--branch :: CodeLocation -> Expr EWord -> (Bool -> EVM ()) -> EVM ()-branch loc cond continue = do-  pathconds <- use #constraints-  query $ PleaseAskSMT cond pathconds choosePath-  where-    choosePath (Case v) = do-      assign #result Nothing-      pushTo #constraints $ if v then (cond ./= Lit 0) else (cond .== Lit 0)-      (iteration, _) <- use (#iterations % at loc % non (0,[]))-      stack <- use (#state % #stack)-      assign (#cache % #path % at (loc, iteration)) (Just v)-      assign (#iterations % at loc) (Just (iteration + 1, stack))-      continue v-    -- Both paths are possible; we ask for more input-    choosePath Unknown =-      choose . PleaseChoosePath cond $ choosePath . Case---- | Construct RPC Query and halt execution until resolved-fetchAccount :: Addr -> (Contract -> EVM ()) -> EVM ()-fetchAccount addr continue =-  use (#env % #contracts % at addr) >>= \case-    Just c -> continue c-    Nothing ->-      use (#cache % #fetchedContracts % at addr) >>= \case-        Just c -> do-          assign (#env % #contracts % at addr) (Just c)-          continue c-        Nothing -> do-          assign (#result) . Just . HandleEffect . Query $-            PleaseFetchContract addr-              (\c -> do assign (#cache % #fetchedContracts % at addr) (Just c)-                        assign (#env % #contracts % at addr) (Just c)-                        assign #result Nothing-                        continue c)--accessStorage-  :: Addr-  -> Expr EWord-  -> (Expr EWord -> EVM ())-  -> EVM ()-accessStorage addr slot continue = do-  store <- (.env.storage) <$> get-  use (#env % #contracts % at addr) >>= \case-    Just c ->-      case readStorage (litAddr addr) slot store of-        -- Notice that if storage is symbolic, we always continue straight away-        Just x ->-          continue x-        Nothing ->-          if c.external then-            forceConcrete slot "cannot read symbolic slots via RPC" $ \litSlot -> do-              -- check if the slot is cached-              cachedStore <- (.cache.fetchedStorage) <$> get-              case Map.lookup (into addr) cachedStore >>= Map.lookup litSlot of-                Nothing -> mkQuery litSlot-                Just val -> continue (Lit val)-          else do-            -- TODO: is this actually needed?-            modifying (#env % #storage) (writeStorage (litAddr addr) slot (Lit 0))-            continue $ Lit 0-    Nothing ->-      fetchAccount addr $ \_ ->-        accessStorage addr slot continue-  where-      mkQuery s = query $-                    PleaseFetchSlot addr s-                      (\x -> do-                          modifying (#cache % #fetchedStorage % ix (into addr)) (Map.insert s x)-                          modifying (#env % #storage) (writeStorage (litAddr addr) slot (Lit x))-                          assign #result Nothing-                          continue (Lit x))--accountExists :: Addr -> VM -> Bool-accountExists addr vm =-  case Map.lookup addr vm.env.contracts of-    Just c -> not (accountEmpty c)-    Nothing -> False---- EIP 161-accountEmpty :: Contract -> Bool-accountEmpty c =-  case c.contractcode of-    RuntimeCode (ConcreteRuntimeCode "") -> True-    RuntimeCode (SymbolicRuntimeCode b) -> null b-    _ -> False-  && c.nonce == 0-  && c.balance  == 0---- * How to finalize a transaction-finalize :: EVM ()-finalize = do-  let-    revertContracts  = use (#tx % #txReversion) >>= assign (#env % #contracts)-    revertSubstate   = assign (#tx % #substate) (SubState mempty mempty mempty mempty mempty)--  use #result >>= \case-    Just (VMFailure (Revert _)) -> do-      revertContracts-      revertSubstate-    Just (VMFailure _) -> do-      -- burn remaining gas-      assign (#state % #gas) 0-      revertContracts-      revertSubstate-    Just (VMSuccess output) -> do-      -- deposit the code from a creation tx-      pc' <- use (#state % #pc)-      creation <- use (#tx % #isCreate)-      createe  <- use (#state % #contract)-      createeExists <- (Map.member createe) <$> use (#env % #contracts)-      let onContractCode contractCode =-            when (creation && createeExists) $ replaceCode createe contractCode-      case output of-        ConcreteBuf bs ->-          onContractCode $ RuntimeCode (ConcreteRuntimeCode bs)-        _ ->-          case Expr.toList output of-            Nothing ->-              partial $-                UnexpectedSymbolicArg pc' "runtime code cannot have an abstract lentgh" (wrap [output])-            Just ops ->-              onContractCode $ RuntimeCode (SymbolicRuntimeCode ops)-    _ ->-      internalError "Finalising an unfinished tx."--  -- compute and pay the refund to the caller and the-  -- corresponding payment to the miner-  block        <- use #block-  tx           <- use #tx-  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-    minerPay     = tx.priorityFee * (into gasUsed)--  modifying (#env % #contracts)-     (Map.adjust (over #balance (+ originPay)) tx.origin)-  modifying (#env % #contracts)-     (Map.adjust (over #balance (+ minerPay)) block.coinbase)-  touchAccount block.coinbase--  -- perform state trie clearing (EIP 161), of selfdestructs-  -- and touched accounts. addresses are cleared if they have-  --    a) selfdestructed, or-  --    b) been touched and-  --    c) are empty.-  -- (see Yellow Paper "Accrued Substate")-  ---  -- remove any destructed addresses-  destroyedAddresses <- use (#tx % #substate % #selfdestructs)-  modifying (#env % #contracts)-    (Map.filterWithKey (\k _ -> (k `notElem` destroyedAddresses)))-  -- then, clear any remaining empty and touched addresses-  touchedAddresses <- use (#tx % #substate % #touchedAccounts)-  modifying (#env % #contracts)-    (Map.filterWithKey-      (\k a -> not ((k `elem` touchedAddresses) && accountEmpty a)))---- | Loads the selected contract as the current contract to execute-loadContract :: Addr -> EVM ()-loadContract target =-  preuse (#env % #contracts % ix target % #contractcode) >>=-    \case-      Nothing ->-        internalError "Call target doesn't exist"-      Just targetCode -> do-        assign (#state % #contract) target-        assign (#state % #code)     targetCode-        assign (#state % #codeContract) target--limitStack :: Int -> EVM () -> EVM ()-limitStack n continue = do-  stk <- use (#state % #stack)-  if length stk + n > 1024-    then vmError StackLimitExceeded-    else continue--notStatic :: EVM () -> EVM ()-notStatic continue = do-  bad <- use (#state % #static)-  if bad-    then vmError StateChangeWhileStatic-    else continue---- | Burn gas, failing if insufficient gas is available-burn :: Word64 -> EVM () -> EVM ()-burn n continue = do-  available <- use (#state % #gas)-  if n <= available-    then do-      #state % #gas %= (subtract n)-      #burned %= (+ n)-      continue-    else-      vmError (OutOfGas available n)--forceConcrete :: Expr EWord -> String -> (W256 -> EVM ()) -> EVM ()-forceConcrete n msg continue = case maybeLitWord n of-  Nothing -> do-    vm <- get-    partial $ UnexpectedSymbolicArg vm.state.pc msg (wrap [n])-  Just c -> continue c--forceConcrete2 :: (Expr EWord, Expr EWord) -> String -> ((W256, W256) -> EVM ()) -> EVM ()-forceConcrete2 (n,m) msg continue = case (maybeLitWord n, maybeLitWord m) of-  (Just c, Just d) -> continue (c, d)-  _ -> do-    vm <- get-    partial $ UnexpectedSymbolicArg vm.state.pc msg (wrap [n, m])--forceConcrete3 :: (Expr EWord, Expr EWord, Expr EWord) -> String -> ((W256, W256, W256) -> EVM ()) -> EVM ()-forceConcrete3 (k,n,m) msg continue = case (maybeLitWord k, maybeLitWord n, maybeLitWord m) of-  (Just c, Just d, Just f) -> continue (c, d, f)-  _ -> do-    vm <- get-    partial $ UnexpectedSymbolicArg vm.state.pc msg (wrap [k, n, m])--forceConcrete4 :: (Expr EWord, Expr EWord, Expr EWord, Expr EWord) -> String -> ((W256, W256, W256, W256) -> EVM ()) -> EVM ()-forceConcrete4 (k,l,n,m) msg continue = case (maybeLitWord k, maybeLitWord l, maybeLitWord n, maybeLitWord m) of-  (Just b, Just c, Just d, Just f) -> continue (b, c, d, f)-  _ -> do-    vm <- get-    partial $ UnexpectedSymbolicArg vm.state.pc msg (wrap [k, l, n, m])--forceConcrete5 :: (Expr EWord, Expr EWord, Expr EWord, Expr EWord, Expr EWord) -> String -> ((W256, W256, W256, W256, W256) -> EVM ()) -> EVM ()-forceConcrete5 (k,l,m,n,o) msg continue = case (maybeLitWord k, maybeLitWord l, maybeLitWord m, maybeLitWord n, maybeLitWord o) of-  (Just a, Just b, Just c, Just d, Just e) -> continue (a, b, c, d, e)-  _ -> do-    vm <- get-    partial $ UnexpectedSymbolicArg vm.state.pc msg (wrap [k, l, m, n, o])--forceConcrete6 :: (Expr EWord, Expr EWord, Expr EWord, Expr EWord, Expr EWord, Expr EWord) -> String -> ((W256, W256, W256, W256, W256, W256) -> EVM ()) -> EVM ()-forceConcrete6 (k,l,m,n,o,p) msg continue = case (maybeLitWord k, maybeLitWord l, maybeLitWord m, maybeLitWord n, maybeLitWord o, maybeLitWord p) of-  (Just a, Just b, Just c, Just d, Just e, Just f) -> continue (a, b, c, d, e, f)-  _ -> do-    vm <- get-    partial $ UnexpectedSymbolicArg vm.state.pc msg (wrap [k, l, m, n, o, p])--forceConcreteBuf :: Expr Buf -> String -> (ByteString -> EVM ()) -> EVM ()-forceConcreteBuf (ConcreteBuf b) _ continue = continue b-forceConcreteBuf b msg _ = do-    vm <- get-    partial $ UnexpectedSymbolicArg vm.state.pc msg (wrap [b])---- * Substate manipulation-refund :: Word64 -> EVM ()-refund n = do-  self <- use (#state % #contract)-  pushTo (#tx % #substate % #refunds) (self, n)--unRefund :: Word64 -> EVM ()-unRefund n = do-  self <- use (#state % #contract)-  refs <- use (#tx % #substate % #refunds)-  assign (#tx % #substate % #refunds)-    (filter (\(a,b) -> not (a == self && b == n)) refs)--touchAccount :: Addr -> EVM()-touchAccount = pushTo ((#tx % #substate) % #touchedAccounts)--selfdestruct :: Addr -> EVM()-selfdestruct = pushTo ((#tx % #substate) % #selfdestructs)--accessAndBurn :: Addr -> EVM () -> EVM ()-accessAndBurn x cont = do-  FeeSchedule {..} <- use (#block % #schedule)-  acc <- accessAccountForGas x-  let cost = if acc then g_warm_storage_read else g_cold_account_access-  burn cost cont---- | returns a wrapped boolean- if true, this address has been touched before in the txn (warm gas cost as in EIP 2929)--- otherwise cold-accessAccountForGas :: Addr -> EVM Bool-accessAccountForGas addr = do-  accessedAddrs <- use (#tx % #substate % #accessedAddresses)-  let accessed = member addr accessedAddrs-  assign (#tx % #substate % #accessedAddresses) (insert addr accessedAddrs)-  pure accessed---- | returns a wrapped boolean- if true, this slot has been touched before in the txn (warm gas cost as in EIP 2929)--- otherwise cold-accessStorageForGas :: Addr -> Expr EWord -> EVM Bool-accessStorageForGas addr key = do-  accessedStrkeys <- use (#tx % #substate % #accessedStorageKeys)-  case maybeLitWord key of-    Just litword -> do-      let accessed = member (addr, litword) accessedStrkeys-      assign (#tx % #substate % #accessedStorageKeys) (insert (addr, litword) accessedStrkeys)-      pure accessed-    _ -> return False---- * Cheat codes---- The cheat code is 7109709ecfa91a80626ff3989d68f67f5b1dd12d.--- Call this address using one of the cheatActions below to do--- special things, e.g. changing the block timestamp. Beware that--- these are necessarily hevm specific.-cheatCode :: Addr-cheatCode = unsafeInto (keccak' "hevm cheat code")--cheat-  :: (?op :: Word8)-  => (W256, W256) -> (W256, W256)-  -> EVM ()-cheat (inOffset, inSize) (outOffset, outSize) = do-  mem <- use (#state % #memory)-  vm <- get-  let-    abi = readBytes 4 (Lit inOffset) mem-    input = readMemory (Lit $ inOffset + 4) (Lit $ inSize - 4) vm-  pushTrace $ FrameTrace (CallContext cheatCode cheatCode inOffset inSize (Lit 0) (maybeLitWord abi) input (vm.env.contracts, vm.env.storage) vm.tx.substate)-  case maybeLitWord abi of-    Nothing -> partial $ UnexpectedSymbolicArg vm.state.pc "symbolic cheatcode selector" (wrap [abi])-    Just (unsafeInto -> abi') ->-      case Map.lookup abi' cheatActions of-        Nothing ->-          vmError (BadCheatCode abi')-        Just action -> do-            action (Lit outOffset) (Lit outSize) input-            popTrace-            next-            push 1--type CheatAction = Expr EWord -> Expr EWord -> Expr Buf -> EVM ()--cheatActions :: Map FunctionSelector CheatAction-cheatActions =-  Map.fromList-    [ action "ffi(string[])" $-        \sig outOffset outSize input -> do-          vm <- get-          if vm.allowFFI then-            case decodeBuf [AbiArrayDynamicType AbiStringType] input of-              CAbi valsArr -> case valsArr of-                [AbiArrayDynamic AbiStringType strsV] ->-                  let-                    cmd = fmap-                            (\case-                              (AbiString a) -> unpack $ decodeUtf8 a-                              _ -> "")-                            (V.toList strsV)-                    cont bs = do-                      let encoded = ConcreteBuf bs-                      assign (#state % #returndata) encoded-                      copyBytesToMemory encoded outSize (Lit 0) outOffset-                      assign #result Nothing-                  in query (PleaseDoFFI cmd cont)-                _ -> vmError (BadCheatCode sig)-              _ -> vmError (BadCheatCode sig)-          else-            let msg = encodeUtf8 "ffi disabled: run again with --ffi if you want to allow tests to call external scripts"-            in vmError . Revert . ConcreteBuf $-              abiMethod "Error(string)" (AbiTuple . V.fromList $ [AbiString msg]),--      action "warp(uint256)" $-        \sig _ _ input -> case decodeStaticArgs 0 1 input of-          [x]  -> assign (#block % #timestamp) x-          _ -> vmError (BadCheatCode sig),--      action "roll(uint256)" $-        \sig _ _ input -> case decodeStaticArgs 0 1 input of-          [x] -> forceConcrete x "cannot roll to a symbolic block number" (assign (#block % #number))-          _ -> vmError (BadCheatCode sig),--      action "store(address,bytes32,bytes32)" $-        \sig _ _ input -> case decodeStaticArgs 0 3 input of-          [a, slot, new] ->-            forceConcrete a "cannot store at a symbolic address" $ \(unsafeInto -> a') ->-              fetchAccount a' $ \_ -> do-                modifying (#env % #storage) (writeStorage (litAddr a') slot new)-          _ -> vmError (BadCheatCode sig),--      action "load(address,bytes32)" $-        \sig outOffset _ input -> case decodeStaticArgs 0 2 input of-          [a, slot] ->-            forceConcrete a "cannot load from a symbolic address" $ \(unsafeInto -> a') ->-              accessStorage a' slot $ \res -> do-                assign (#state % #returndata % word256At (Lit 0)) res-                assign (#state % #memory % word256At outOffset) res-          _ -> vmError (BadCheatCode sig),--      action "sign(uint256,bytes32)" $-        \sig outOffset _ input -> case decodeStaticArgs 0 2 input of-          [sk, hash] ->-            forceConcrete2 (sk, hash) "cannot sign symbolic data" $ \(sk', hash') -> do-              let (v,r,s) = EVM.Sign.sign hash' (toInteger sk')-                  encoded = encodeAbiValue $-                    AbiTuple (V.fromList-                      [ AbiUInt 8 $ into v-                      , AbiBytes 32 (word256Bytes r)-                      , AbiBytes 32 (word256Bytes s)-                      ])-              assign (#state % #returndata) (ConcreteBuf encoded)-              copyBytesToMemory (ConcreteBuf encoded) (Lit . unsafeInto . BS.length $ encoded) (Lit 0) outOffset-          _ -> vmError (BadCheatCode sig),--      action "addr(uint256)" $-        \sig outOffset _ input -> case decodeStaticArgs 0 1 input of-          [sk] -> forceConcrete sk "cannot derive address for a symbolic key" $ \sk' -> do-            let a = EVM.Sign.deriveAddr $ into sk'-            case a of-              Nothing -> vmError (BadCheatCode sig)-              Just address -> do-                let expAddr = litAddr address-                assign (#state % #returndata % word256At (Lit 0)) expAddr-                assign (#state % #memory % word256At outOffset) expAddr-          _ -> vmError (BadCheatCode sig),--      action "prank(address)" $-        \sig _ _ input -> case decodeStaticArgs 0 1 input of-          [addr]  -> assign #overrideCaller (Expr.exprToAddr addr)-          _ -> vmError (BadCheatCode sig)--    ]-  where-    action s f = (abiKeccak s, f (abiKeccak s))---- * General call implementation ("delegateCall")--- note that the continuation is ignored in the precompile case-delegateCall-  :: (?op :: Word8)-  => Contract -> Word64 -> Expr EWord -> Expr EWord -> W256 -> W256 -> W256 -> W256 -> W256-  -> [Expr EWord]-  -> (Addr -> EVM ())-  -> EVM ()-delegateCall this gasGiven xTo xContext xValue xInOffset xInSize xOutOffset xOutSize xs continue =-  forceConcrete2 (xTo, xContext) "cannot delegateCall with symbolic target or context" $-    \((unsafeInto -> xTo'), (unsafeInto -> xContext')) ->-      if xTo' > 0 && xTo' <= 9-      then precompiledContract this gasGiven xTo' xContext' xValue xInOffset xInSize xOutOffset xOutSize xs-      else if xTo' == cheatCode then-        do-          assign (#state % #stack) xs-          cheat (xInOffset, xInSize) (xOutOffset, xOutSize)-      else-        callChecks this gasGiven xContext' xTo' xValue xInOffset xInSize xOutOffset xOutSize xs $-        \xGas -> do-          vm0 <- get-          fetchAccount xTo' $ \target ->-                burn xGas $ do-                  let newContext = CallContext-                                    { target    = xTo'-                                    , context   = xContext'-                                    , offset    = xOutOffset-                                    , size      = xOutSize-                                    , codehash  = target.codehash-                                    , callreversion = (vm0.env.contracts, vm0.env.storage)-                                    , subState  = vm0.tx.substate-                                    , abi =-                                        if xInSize >= 4-                                        then (maybeLitWord $ readBytes 4 (Lit xInOffset) vm0.state.memory)-                                        else Nothing-                                    , calldata = (readMemory (Lit xInOffset) (Lit xInSize) vm0)-                                    }--                  pushTrace (FrameTrace newContext)-                  next-                  vm1 <- get--                  pushTo #frames $ Frame-                    { state = vm1.state { stack = xs }-                    , context = newContext-                    }--                  let clearInitCode = \case-                        (InitCode _ _) -> InitCode mempty mempty-                        a -> a--                  zoom #state $ do-                    assign #gas xGas-                    assign #pc 0-                    assign #code (clearInitCode target.contractcode)-                    assign #codeContract xTo'-                    assign #stack mempty-                    assign #memory mempty-                    assign #memorySize 0-                    assign #returndata mempty-                    assign #calldata (copySlice (Lit xInOffset) (Lit 0) (Lit xInSize) vm0.state.memory mempty)--                  continue xTo'---- -- * Contract creation---- EIP 684-collision :: Maybe Contract -> Bool-collision c' = case c' of-  Just c -> c.nonce /= 0 || case c.contractcode of-    RuntimeCode (ConcreteRuntimeCode "") -> False-    RuntimeCode (SymbolicRuntimeCode b) -> not $ null b-    _ -> True-  Nothing -> False--create :: (?op :: Word8)-  => Addr -> Contract-  -> W256 -> Word64 -> W256 -> [Expr EWord] -> Addr -> Expr Buf -> EVM ()-create self this xSize xGas xValue xs newAddr initCode = do-  vm0 <- get-  if this.nonce == into (maxBound :: Word64)-  then do-    assign (#state % #stack) (Lit 0 : xs)-    assign (#state % #returndata) mempty-    pushTrace $ ErrorTrace NonceOverflow-    next-  else if xValue > this.balance-  then do-    assign (#state % #stack) (Lit 0 : xs)-    assign (#state % #returndata) mempty-    pushTrace $ ErrorTrace $ BalanceTooLow xValue this.balance-    next-  else if xSize > vm0.block.maxCodeSize * 2-  then do-    assign (#state % #stack) (Lit 0 : xs)-    assign (#state % #returndata) mempty-    vmError $ MaxInitCodeSizeExceeded (vm0.block.maxCodeSize * 2) xSize-  else if length vm0.frames >= 1024-  then do-    assign (#state % #stack) (Lit 0 : xs)-    assign (#state % #returndata) mempty-    pushTrace $ ErrorTrace CallDepthLimitReached-    next-  else if collision $ Map.lookup newAddr vm0.env.contracts-  then burn xGas $ do-    assign (#state % #stack) (Lit 0 : xs)-    assign (#state % #returndata) mempty-    modifying (#env % #contracts % ix self % #nonce) succ-    next-  else burn xGas $ do-    touchAccount self-    touchAccount newAddr-    let-    -- unfortunately we have to apply some (pretty hacky)-    -- heuristics here to parse the unstructured buffer read-    -- from memory into a code and data section-    let contract' = do-          prefixLen <- Expr.concPrefix initCode-          prefix <- Expr.toList $ Expr.take (unsafeInto prefixLen) initCode-          let sym = Expr.drop (unsafeInto prefixLen) initCode-          conc <- mapM maybeLitByte prefix-          pure $ InitCode (BS.pack $ V.toList conc) sym-    case contract' of-      Nothing ->-        partial $ UnexpectedSymbolicArg vm0.state.pc "initcode must have a concrete prefix" []-      Just c -> do-        let-          newContract = initialContract c-          newContext  =-            CreationContext { address   = newAddr-                            , codehash  = newContract.codehash-                            , createreversion = vm0.env.contracts-                            , substate  = vm0.tx.substate-                            }--        zoom (#env % #contracts) $ do-          oldAcc <- use (at newAddr)-          let oldBal = maybe 0 (.balance) oldAcc--          assign (at newAddr) (Just (newContract & #balance .~ oldBal))-          modifying (ix self % #nonce) succ--        let resetStorage = \case-              ConcreteStore s -> ConcreteStore (Map.delete (into newAddr) s)-              AbstractStore -> AbstractStore-              EmptyStore -> EmptyStore-              SStore {} -> internalError "trying to reset symbolic storage with writes in create"-              GVar _  -> internalError "unexpected global variable"--        modifying (#env % #storage) resetStorage-        modifying (#env % #origStorage) (Map.delete (into newAddr))--        transfer self newAddr xValue--        pushTrace (FrameTrace newContext)-        next-        vm1 <- get-        pushTo #frames $ Frame-          { context = newContext-          , state   = vm1.state { stack = xs }-          }--        assign #state $-          blankState-            & set #contract     newAddr-            & set #codeContract newAddr-            & set #code         c-            & set #callvalue    (Lit xValue)-            & set #caller       (litAddr self)-            & set #gas          xGas---- | Replace a contract's code, like when CREATE returns--- from the constructor code.-replaceCode :: Addr -> ContractCode -> EVM ()-replaceCode target newCode =-  zoom (#env % #contracts % at target) $-    get >>= \case-      Just now -> case now.contractcode of-        InitCode _ _ ->-          put . Just $-            (initialContract newCode)-              { balance = now.balance-              , nonce = now.nonce-              }-        RuntimeCode _ ->-          internalError $ "can't replace code of deployed contract " <> show target-      Nothing ->-        internalError "can't replace code of nonexistent contract"--replaceCodeOfSelf :: ContractCode -> EVM ()-replaceCodeOfSelf newCode = do-  vm <- get-  replaceCode vm.state.contract newCode--resetState :: EVM ()-resetState =-  modify' $ \vm -> vm { result = Nothing-                      , frames = []-                      , state  = blankState }---- * VM error implementation--vmError :: EvmError -> EVM ()-vmError e = finishFrame (FrameErrored e)--partial :: PartialExec -> EVM ()-partial e = assign #result (Just (Unfinished e))--wrap :: Typeable a => [Expr a] -> [SomeExpr]-wrap = fmap SomeExpr--underrun :: EVM ()-underrun = vmError StackUnderrun---- | A stack frame can be popped in three ways.-data FrameResult-  = FrameReturned (Expr Buf) -- ^ STOP, RETURN, or no more code-  | FrameReverted (Expr Buf) -- ^ REVERT-  | FrameErrored EvmError -- ^ Any other error-  deriving Show---- | This function defines how to pop the current stack frame in either of--- the ways specified by 'FrameResult'.------ It also handles the case when the current stack frame is the only one;--- in this case, we set the final '_result' of the VM execution.-finishFrame :: FrameResult -> EVM ()-finishFrame how = do-  oldVm <- get--  case oldVm.frames of-    -- Is the current frame the only one?-    [] -> do-      case how of-          FrameReturned output -> assign #result . Just $ VMSuccess output-          FrameReverted buffer -> assign #result . Just $ VMFailure (Revert buffer)-          FrameErrored e       -> assign #result . Just $ VMFailure e-      finalize--    -- Are there some remaining frames?-    nextFrame : remainingFrames -> do--      -- Insert a debug trace.-      insertTrace $-        case how of-          FrameErrored e ->-            ErrorTrace e-          FrameReverted e ->-            ErrorTrace (Revert e)-          FrameReturned output ->-            ReturnTrace output nextFrame.context-      -- Pop to the previous level of the debug trace stack.-      popTrace--      -- Pop the top frame.-      assign #frames remainingFrames-      -- Install the state of the frame to which we shall return.-      assign #state nextFrame.state--      -- When entering a call, the gas allowance is counted as burned-      -- in advance; this unburns the remainder and adds it to the-      -- parent frame.-      let remainingGas = oldVm.state.gas-          reclaimRemainingGasAllowance = do-            modifying #burned (subtract remainingGas)-            modifying (#state % #gas) (+ remainingGas)--      -- Now dispatch on whether we were creating or calling,-      -- and whether we shall return, revert, or internalError(six cases).-      case nextFrame.context of--        -- Were we calling?-        CallContext _ _ (Lit -> outOffset) (Lit -> outSize) _ _ _ reversion substate' -> do--          -- Excerpt K.1. from the yellow paper:-          -- K.1. Deletion of an Account Despite Out-of-gas.-          -- At block 2675119, in the transaction 0xcf416c536ec1a19ed1fb89e4ec7ffb3cf73aa413b3aa9b77d60e4fd81a4296ba,-          -- an account at address 0x03 was called and an out-of-gas occurred during the call.-          -- Against the equation (197), this added 0x03 in the set of touched addresses, and this transaction turned σ[0x03] into ∅.--          -- In other words, we special case address 0x03 and keep it in the set of touched accounts during revert-          touched <- use (#tx % #substate % #touchedAccounts)--          let-            substate'' = over #touchedAccounts (maybe id cons (find (3 ==) touched)) substate'-            (contractsReversion, storageReversion) = reversion-            revertContracts = assign (#env % #contracts) contractsReversion-            revertStorage = assign (#env % #storage) storageReversion-            revertSubstate  = assign (#tx % #substate) substate''--          case how of-            -- Case 1: Returning from a call?-            FrameReturned output -> do-              assign (#state % #returndata) output-              copyCallBytesToMemory output outSize (Lit 0) outOffset-              reclaimRemainingGasAllowance-              push 1--            -- Case 2: Reverting during a call?-            FrameReverted output -> do-              revertContracts-              revertStorage-              revertSubstate-              assign (#state % #returndata) output-              copyCallBytesToMemory output outSize (Lit 0) outOffset-              reclaimRemainingGasAllowance-              push 0--            -- Case 3: Error during a call?-            FrameErrored _ -> do-              revertContracts-              revertStorage-              revertSubstate-              assign (#state % #returndata) mempty-              push 0-        -- Or were we creating?-        CreationContext _ _ reversion substate' -> do-          creator <- use (#state % #contract)-          let-            createe = oldVm.state.contract-            revertContracts = assign (#env % #contracts) reversion'-            revertSubstate  = assign (#tx % #substate) substate'--            -- persist the nonce through the reversion-            reversion' = (Map.adjust (over #nonce (+ 1)) creator) reversion--          case how of-            -- Case 4: Returning during a creation?-            FrameReturned output -> do-              let onContractCode contractCode = do-                    replaceCode createe contractCode-                    assign (#state % #returndata) mempty-                    reclaimRemainingGasAllowance-                    push (into createe)-              case output of-                ConcreteBuf bs ->-                  onContractCode $ RuntimeCode (ConcreteRuntimeCode bs)-                _ ->-                  case Expr.toList output of-                    Nothing -> partial $-                      UnexpectedSymbolicArg-                        oldVm.state.pc-                        "runtime code cannot have an abstract length"-                        (wrap [output])-                    Just newCode -> do-                      onContractCode $ RuntimeCode (SymbolicRuntimeCode newCode)--            -- Case 5: Reverting during a creation?-            FrameReverted output -> do-              revertContracts-              revertSubstate-              assign (#state % #returndata) output-              reclaimRemainingGasAllowance-              push 0--            -- Case 6: Error during a creation?-            FrameErrored _ -> do-              revertContracts-              revertSubstate-              assign (#state % #returndata) mempty-              push 0----- * Memory helpers--accessUnboundedMemoryRange-  :: Word64-  -> Word64-  -> EVM ()-  -> EVM ()-accessUnboundedMemoryRange _ 0 continue = continue-accessUnboundedMemoryRange f l continue = do-  m0 <- use (#state % #memorySize)-  fees <- gets (.block.schedule)-  let m1 = 32 * ceilDiv (max m0 (f + l)) 32-  burn (memoryCost fees m1 - memoryCost fees m0) $ do-    assign (#state % #memorySize) m1-    continue--accessMemoryRange-  :: W256-  -> W256-  -> EVM ()-  -> EVM ()-accessMemoryRange _ 0 continue = continue-accessMemoryRange f l continue =-  case (,) <$> toWord64 f <*> toWord64 l of-    Nothing -> vmError IllegalOverflow-    Just (f64, l64) ->-      if f64 + l64 < l64-        then vmError IllegalOverflow-        else accessUnboundedMemoryRange f64 l64 continue--accessMemoryWord-  :: W256 -> EVM () -> EVM ()-accessMemoryWord x = accessMemoryRange x 32--copyBytesToMemory-  :: Expr Buf -> Expr EWord -> Expr EWord -> Expr EWord -> EVM ()-copyBytesToMemory bs size xOffset yOffset =-  if size == Lit 0 then noop-  else do-    mem <- use (#state % #memory)-    assign (#state % #memory) $-      copySlice xOffset yOffset size bs mem--copyCallBytesToMemory-  :: Expr Buf -> Expr EWord -> Expr EWord -> Expr EWord -> EVM ()-copyCallBytesToMemory bs size xOffset yOffset =-  if size == Lit 0 then noop-  else do-    mem <- use (#state % #memory)-    assign (#state % #memory) $-      copySlice xOffset yOffset (Expr.min size (bufLength bs)) bs mem--readMemory :: Expr EWord -> Expr EWord -> VM -> Expr Buf-readMemory offset size vm = copySlice offset (Lit 0) size vm.state.memory mempty---- * Tracing--withTraceLocation :: TraceData -> EVM Trace-withTraceLocation x = do-  vm <- get-  let this = fromJust $ currentContract vm-  pure Trace-    { tracedata = x-    , contract = this-    , opIx = fromMaybe 0 $ this.opIxMap SV.!? vm.state.pc-    }--pushTrace :: TraceData -> EVM ()-pushTrace x = do-  trace <- withTraceLocation x-  modifying #traces $-    \t -> Zipper.children $ Zipper.insert (Node trace []) t--insertTrace :: TraceData -> EVM ()-insertTrace x = do-  trace <- withTraceLocation x-  modifying #traces $-    \t -> Zipper.nextSpace $ Zipper.insert (Node trace []) t--popTrace :: EVM ()-popTrace =-  modifying #traces $-    \t -> case Zipper.parent t of-            Nothing -> internalError "internal internalError(trace root)"-            Just t' -> Zipper.nextSpace t'--zipperRootForest :: Zipper.TreePos Zipper.Empty a -> Forest a-zipperRootForest z =-  case Zipper.parent z of-    Nothing -> Zipper.toForest z-    Just z' -> zipperRootForest (Zipper.nextSpace z')--traceForest :: VM -> Forest Trace-traceForest vm = zipperRootForest vm.traces--traceForest' :: Expr End -> Forest Trace-traceForest' (Success _ (Traces f _) _ _) = f-traceForest' (Partial _ (Traces f _) _) = f-traceForest' (Failure _ (Traces f _) _) = f-traceForest' (ITE {}) = internalError"Internal Error: ITE does not contain a trace"-traceForest' (GVar {}) = internalError"Internal Error: Unexpected GVar"--traceContext :: Expr End -> Map Addr Contract-traceContext (Success _ (Traces _ c) _ _) = c-traceContext (Partial _ (Traces _ c) _) = c-traceContext (Failure _ (Traces _ c) _) = c-traceContext (ITE {}) = internalError"Internal Error: ITE does not contain a trace"-traceContext (GVar {}) = internalError"Internal Error: Unexpected GVar"--traceTopLog :: [Expr Log] -> EVM ()-traceTopLog [] = noop-traceTopLog ((LogEntry addr bytes topics) : _) = do-  trace <- withTraceLocation (EventTrace addr bytes topics)-  modifying #traces $-    \t -> Zipper.nextSpace (Zipper.insert (Node trace []) t)-traceTopLog ((GVar _) : _) = internalError "unexpected global variable"---- * Stack manipulation--push :: W256 -> EVM ()-push = pushSym . Lit--pushSym :: Expr EWord -> EVM ()-pushSym x = #state % #stack %= (x :)--stackOp1-  :: (?op :: Word8)-  => Word64-  -> ((Expr EWord) -> (Expr EWord))-  -> EVM ()-stackOp1 cost f =-  use (#state % #stack) >>= \case-    x:xs ->-      burn cost $ do-        next-        let !y = f x-        (#state % #stack) .= y : xs-    _ ->-      underrun--stackOp2-  :: (?op :: Word8)-  => Word64-  -> (((Expr EWord), (Expr EWord)) -> (Expr EWord))-  -> EVM ()-stackOp2 cost f =-  use (#state % #stack) >>= \case-    x:y:xs ->-      burn cost $ do-        next-        (#state % #stack) .= f (x, y) : xs-    _ ->-      underrun--stackOp3-  :: (?op :: Word8)-  => Word64-  -> (((Expr EWord), (Expr EWord), (Expr EWord)) -> (Expr EWord))-  -> EVM ()-stackOp3 cost f =-  use (#state % #stack) >>= \case-    x:y:z:xs ->-      burn cost $ do-      next-      (#state % #stack) .= f (x, y, z) : xs-    _ ->-      underrun---- * Bytecode data functions--use' :: (VM -> a) -> EVM a-use' f = do-  vm <- get-  pure (f vm)--checkJump :: Int -> [Expr EWord] -> EVM ()-checkJump x xs = do-  vm <- get-  case isValidJumpDest vm x of-    True -> do-      #state % #stack .= xs-      #state % #pc .= x-    False -> vmError BadJumpDestination--isValidJumpDest :: VM -> Int -> Bool-isValidJumpDest vm x = let-    code = vm.state.code-    self = vm.state.codeContract-    contract = fromMaybe-      (internalError "self not found in current contracts")-      (Map.lookup self vm.env.contracts)-    op = case code of-      InitCode ops _ -> BS.indexMaybe ops x-      RuntimeCode (ConcreteRuntimeCode ops) -> BS.indexMaybe ops x-      RuntimeCode (SymbolicRuntimeCode ops) -> ops V.!? x >>= maybeLitByte-  in case op of-       Nothing -> False-       Just b -> 0x5b == b && OpJumpdest == snd (contract.codeOps V.! (contract.opIxMap SV.! x))--opSize :: Word8 -> Int-opSize x | x >= 0x60 && x <= 0x7f = into x - 0x60 + 2-opSize _                          = 1----  i of the resulting vector contains the operation index for--- the program counter value i.  This is needed because source map--- entries are per operation, not per byte.-mkOpIxMap :: ContractCode -> SV.Vector Int-mkOpIxMap (InitCode conc _)-  = SV.create $ SV.new (BS.length conc) >>= \v ->-      -- Loop over the byte string accumulating a vector-mutating action.-      -- This is somewhat obfuscated, but should be fast.-      let (_, _, _, m) = BS.foldl' (go v) (0 :: Word8, 0, 0, pure ()) conc-      in m >> pure v-      where-        -- concrete case-        go v (0, !i, !j, !m) x | x >= 0x60 && x <= 0x7f =-          {- Start of PUSH op. -} (x - 0x60 + 1, i + 1, j,     m >> SV.write v i j)-        go v (1, !i, !j, !m) _ =-          {- End of PUSH op. -}   (0,            i + 1, j + 1, m >> SV.write v i j)-        go v (0, !i, !j, !m) _ =-          {- Other op. -}         (0,            i + 1, j + 1, m >> SV.write v i j)-        go v (n, !i, !j, !m) _ =-          {- PUSH data. -}        (n - 1,        i + 1, j,     m >> SV.write v i j)--mkOpIxMap (RuntimeCode (ConcreteRuntimeCode ops)) =-  mkOpIxMap (InitCode ops mempty) -- a bit hacky--mkOpIxMap (RuntimeCode (SymbolicRuntimeCode ops))-  = SV.create $ SV.new (length ops) >>= \v ->-      let (_, _, _, m) = foldl (go v) (0, 0, 0, pure ()) (stripBytecodeMetadataSym $ V.toList ops)-      in m >> pure v-      where-        go v (0, !i, !j, !m) x = case maybeLitByte x of-          Just x' -> if x' >= 0x60 && x' <= 0x7f-            -- start of PUSH op ---                     then (x' - 0x60 + 1, i + 1, j,     m >> SV.write v i j)-            -- other data ---                     else (0,             i + 1, j + 1, m >> SV.write v i j)-          _ -> internalError $ "cannot analyze symbolic code:\nx: " <> show x <> " i: " <> show i <> " j: " <> show j--        go v (1, !i, !j, !m) _ =-          {- End of PUSH op. -}   (0,            i + 1, j + 1, m >> SV.write v i j)-        go v (n, !i, !j, !m) _ =-          {- PUSH data. -}        (n - 1,        i + 1, j,     m >> SV.write v i j)---vmOp :: VM -> Maybe Op-vmOp vm =-  let i  = vm ^. #state % #pc-      code' = vm ^. #state % #code-      (op, pushdata) = case code' of-        InitCode xs' _ ->-          (BS.index xs' i, fmap LitByte $ BS.unpack $ BS.drop i xs')-        RuntimeCode (ConcreteRuntimeCode xs') ->-          (BS.index xs' i, fmap LitByte $ BS.unpack $ BS.drop i xs')-        RuntimeCode (SymbolicRuntimeCode xs') ->-          ( fromMaybe (internalError "unexpected symbolic code") . maybeLitByte $ xs' V.! i , V.toList $ V.drop i xs')-  in if (opslen code' < i)-     then Nothing-     else Just (readOp op pushdata)--vmOpIx :: VM -> Maybe Int-vmOpIx vm =-  do self <- currentContract vm-     self.opIxMap SV.!? vm.state.pc---- Maps operation indicies into a pair of (bytecode index, operation)-mkCodeOps :: ContractCode -> V.Vector (Int, Op)-mkCodeOps contractCode =-  let l = case contractCode of-            InitCode bytes _ ->-              LitByte <$> (BS.unpack bytes)-            RuntimeCode (ConcreteRuntimeCode ops) ->-              LitByte <$> (BS.unpack $ stripBytecodeMetadata ops)-            RuntimeCode (SymbolicRuntimeCode ops) ->-              stripBytecodeMetadataSym $ V.toList ops-  in V.fromList . toList $ go 0 l-  where-    go !i !xs =-      case uncons xs of-        Nothing ->-          mempty-        Just (x, xs') ->-          let x' = fromMaybe (internalError "unexpected symbolic code argument") $ maybeLitByte x-              j = opSize x'-          in (i, readOp x' xs') Seq.<| go (i + j) (drop j xs)---- * Gas cost calculation helpers---- Gas cost function for CALL, transliterated from the Yellow Paper.-costOfCall-  :: FeeSchedule Word64-  -> Bool -> W256 -> Word64 -> Word64 -> Addr-  -> EVM (Word64, Word64)-costOfCall (FeeSchedule {..}) recipientExists xValue availableGas xGas target = do-  acc <- accessAccountForGas target-  let call_base_gas = if acc then g_warm_storage_read else g_cold_account_access-      c_new = if not recipientExists && xValue /= 0-            then g_newaccount-            else 0-      c_xfer = if xValue /= 0  then g_callvalue else 0-      c_extra = call_base_gas + c_xfer + c_new-      c_gascap =  if availableGas >= c_extra-                  then min xGas (allButOne64th (availableGas - c_extra))-                  else xGas-      c_callgas = if xValue /= 0 then c_gascap + g_callstipend else c_gascap-  pure (c_gascap + c_extra, c_callgas)---- Gas cost of create, including hash cost if needed-costOfCreate-  :: FeeSchedule Word64-  -> Word64 -> W256 -> Bool -> (Word64, Word64)-costOfCreate (FeeSchedule {..}) availableGas size hashNeeded = (createCost, initGas)-  where-    byteCost   = if hashNeeded then g_sha3word + g_initcodeword else g_initcodeword-    createCost = g_create + codeCost-    codeCost   = byteCost * (ceilDiv (unsafeInto size) 32)-    initGas    = allButOne64th (availableGas - createCost)--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---- Gas cost of precompiles-costOfPrecompile :: FeeSchedule Word64 -> Addr -> Expr Buf -> Word64-costOfPrecompile (FeeSchedule {..}) precompileAddr input =-  let errorDynamicSize = internalError "precompile input cannot have a dynamic size"-      inputLen = case input of-                   ConcreteBuf bs -> unsafeInto $ BS.length bs-                   AbstractBuf _ -> errorDynamicSize-                   buf -> case bufLength buf of-                            Lit l -> unsafeInto l -- TODO: overflow-                            _ -> errorDynamicSize-  in case precompileAddr of-    -- ECRECOVER-    0x1 -> 3000-    -- SHA2-256-    0x2 -> (((inputLen + 31) `div` 32) * 12) + 60-    -- RIPEMD-160-    0x3 -> (((inputLen + 31) `div` 32) * 120) + 600-    -- IDENTITY-    0x4 -> (((inputLen + 31) `div` 32) * 3) + 15-    -- MODEXP-    0x5 -> case input of-             ConcreteBuf i -> concreteModexpGasFee i-             _ -> internalError "Unsupported symbolic modexp gas calc "-    -- ECADD-    0x6 -> g_ecadd-    -- ECMUL-    0x7 -> g_ecmul-    -- ECPAIRING-    0x8 -> (inputLen `div` 192) * g_pairing_point + g_pairing_base-    -- BLAKE2-    0x9 -> case input of-             ConcreteBuf i -> g_fround * (unsafeInto $ asInteger $ lazySlice 0 4 i)-             _ -> internalError "Unsupported symbolic blake2 gas calc"-    _ -> internalError $ "unimplemented precompiled contract " ++ show precompileAddr---- Gas cost of memory expansion-memoryCost :: FeeSchedule Word64 -> Word64 -> Word64-memoryCost FeeSchedule{..} byteCount =-  let-    wordCount = ceilDiv byteCount 32-    linearCost = g_memory * wordCount-    quadraticCost = div (wordCount * wordCount) 512-  in-    linearCost + quadraticCost--hashcode :: ContractCode -> Expr EWord-hashcode (InitCode ops args) = keccak $ (ConcreteBuf ops) <> args-hashcode (RuntimeCode (ConcreteRuntimeCode ops)) = keccak (ConcreteBuf ops)-hashcode (RuntimeCode (SymbolicRuntimeCode ops)) = keccak . Expr.fromList $ ops---- | The length of the code ignoring any constructor args.--- This represents the region that can contain executable opcodes-opslen :: ContractCode -> Int-opslen (InitCode ops _) = BS.length ops-opslen (RuntimeCode (ConcreteRuntimeCode ops)) = BS.length ops-opslen (RuntimeCode (SymbolicRuntimeCode ops)) = length ops---- | The length of the code including any constructor args.--- This can return an abstract value-codelen :: ContractCode -> Expr EWord-codelen c@(InitCode {}) = bufLength $ toBuf c-codelen (RuntimeCode (ConcreteRuntimeCode ops)) = Lit . unsafeInto $ BS.length ops-codelen (RuntimeCode (SymbolicRuntimeCode ops)) = Lit . unsafeInto $ length ops--toBuf :: ContractCode -> Expr Buf-toBuf (InitCode ops args) = ConcreteBuf ops <> args-toBuf (RuntimeCode (ConcreteRuntimeCode ops)) = ConcreteBuf ops-toBuf (RuntimeCode (SymbolicRuntimeCode ops)) = Expr.fromList ops--codeloc :: EVM CodeLocation-codeloc = do-  vm <- get-  pure (vm.state.contract, vm.state.pc)---- * Arithmetic--ceilDiv :: (Num a, Integral a) => a -> a -> a-ceilDiv m n = div (m + n - 1) n--allButOne64th :: (Num a, Integral a) => a -> a-allButOne64th n = n - div n 64--log2 :: FiniteBits b => b -> Int-log2 x = finiteBitSize x - 1 - countLeadingZeros x+import EVM.Expr (readStorage, writeStorage, readByte, readWord, writeWord,+  writeByte, bufLength, indexWord, litAddr, readBytes, word256At, copySlice, wordToAddr)+import EVM.Expr qualified as Expr+import EVM.FeeSchedule (FeeSchedule (..))+import EVM.Op+import EVM.Precompiled qualified+import EVM.Solidity+import EVM.Types+import EVM.Sign qualified+import EVM.Concrete qualified as Concrete++import Control.Monad.ST (ST)+import Control.Monad.State.Strict hiding (state)+import Data.Bits (FiniteBits, countLeadingZeros, finiteBitSize)+import Data.ByteArray qualified as BA+import Data.ByteString (ByteString)+import Data.ByteString qualified as BS+import Data.ByteString.Lazy (fromStrict)+import Data.ByteString.Lazy qualified as LS+import Data.ByteString.Char8 qualified as Char8+import Data.Foldable (toList)+import Data.List (find)+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Maybe (fromMaybe, fromJust, isJust)+import Data.Set (insert, member, fromList)+import Data.Sequence (Seq)+import Data.Sequence qualified as Seq+import Data.Text (unpack, pack)+import Data.Text.Encoding (decodeUtf8)+import Data.Tree+import Data.Tree.Zipper qualified as Zipper+import Data.Typeable+import Data.Vector qualified as V+import Data.Vector.Storable qualified as SV+import Data.Vector.Storable.Mutable qualified as SV+import Data.Vector.Unboxed qualified as VUnboxed+import Data.Vector.Unboxed.Mutable qualified as VUnboxed.Mutable+import Data.Word (Word8, Word32, Word64)+import Witch (into, tryFrom, unsafeInto)++import Crypto.Hash (Digest, SHA256, RIPEMD160)+import Crypto.Hash qualified as Crypto+import Crypto.Number.ModArithmetic (expFast)++blankState :: ST s (FrameState s)+blankState = do+  memory <- ConcreteMemory <$> VUnboxed.Mutable.new 0+  pure $ FrameState+    { contract     = LitAddr 0+    , codeContract = LitAddr 0+    , code         = RuntimeCode (ConcreteRuntimeCode "")+    , pc           = 0+    , stack        = mempty+    , memory+    , memorySize   = 0+    , calldata     = mempty+    , callvalue    = Lit 0+    , caller       = LitAddr 0+    , gas          = 0+    , returndata   = mempty+    , static       = False+    }++-- | An "external" view of a contract's bytecode, appropriate for+-- e.g. @EXTCODEHASH@.+bytecode :: Getter Contract (Maybe (Expr Buf))+bytecode = #code % to f+  where f (InitCode _ _) = Just mempty+        f (RuntimeCode (ConcreteRuntimeCode bs)) = Just $ ConcreteBuf bs+        f (RuntimeCode (SymbolicRuntimeCode ops)) = Just $ Expr.fromList ops+        f (UnknownCode _) = Nothing++-- * Data accessors++currentContract :: VM s -> Maybe Contract+currentContract vm =+  Map.lookup vm.state.codeContract vm.env.contracts++-- * Data constructors++makeVm :: VMOpts -> ST s (VM s)+makeVm o = do+  let txaccessList = o.txAccessList+      txorigin = o.origin+      txtoAddr = o.address+      initialAccessedAddrs = fromList $+           [txorigin, txtoAddr, o.coinbase]+        ++ (fmap LitAddr [1..9])+        ++ (Map.keys txaccessList)+      initialAccessedStorageKeys = fromList $ foldMap (uncurry (map . (,))) (Map.toList txaccessList)+      touched = if o.create then [txorigin] else [txorigin, txtoAddr]+  memory <- ConcreteMemory <$> VUnboxed.Mutable.new 0+  pure $ VM+    { result = Nothing+    , frames = mempty+    , tx = TxState+      { gasprice = o.gasprice+      , gaslimit = o.gaslimit+      , priorityFee = o.priorityFee+      , origin = txorigin+      , toAddr = txtoAddr+      , value = o.value+      , substate = SubState mempty touched initialAccessedAddrs initialAccessedStorageKeys mempty+      , isCreate = o.create+      , txReversion = Map.fromList ((o.address,o.contract):o.otherContracts)+      }+    , logs = []+    , traces = Zipper.fromForest []+    , block = Block+      { coinbase = o.coinbase+      , timestamp = o.timestamp+      , number = o.number+      , prevRandao = o.prevRandao+      , maxCodeSize = o.maxCodeSize+      , gaslimit = o.blockGaslimit+      , baseFee = o.baseFee+      , schedule = o.schedule+      }+    , state = FrameState+      { pc = 0+      , stack = mempty+      , memory+      , memorySize = 0+      , code = o.contract.code+      , contract = o.address+      , codeContract = o.address+      , calldata = fst o.calldata+      , callvalue = o.value+      , caller = o.caller+      , gas = o.gas+      , returndata = mempty+      , static = False+      }+    , env = Env+      { chainId = o.chainId+      , contracts = Map.fromList ((o.address,o.contract):o.otherContracts)+      , freshAddresses = 0+      }+    , cache = Cache mempty mempty+    , burned = 0+    , constraints = snd o.calldata+    , keccakEqs = mempty+    , iterations = mempty+    , config = RuntimeConfig+      { allowFFI = o.allowFFI+      , overrideCaller = Nothing+      , baseState = o.baseState+      }+    }++-- | Initialize an abstract contract with unknown code+unknownContract :: Expr EAddr -> Contract+unknownContract addr = Contract+  { code        = UnknownCode addr+  , storage     = AbstractStore addr+  , origStorage = AbstractStore addr+  , balance     = Balance addr+  , nonce       = Nothing+  , codehash    = hashcode (UnknownCode addr)+  , opIxMap     = mempty+  , codeOps     = mempty+  , external    = False+  }++-- | Initialize an abstract contract with known code+abstractContract :: ContractCode -> Expr EAddr -> Contract+abstractContract code addr = Contract+  { code        = code+  , storage     = AbstractStore addr+  , origStorage = AbstractStore addr+  , balance     = Balance addr+  , nonce       = if isCreation code then Just 1 else Just 0+  , codehash    = hashcode code+  , opIxMap     = mkOpIxMap code+  , codeOps     = mkCodeOps code+  , external    = False+  }++-- | Initialize an empty contract without code+emptyContract :: Contract+emptyContract = initialContract (RuntimeCode (ConcreteRuntimeCode ""))++-- | Initialize empty contract with given code+initialContract :: ContractCode -> Contract+initialContract code = Contract+  { code        = code+  , storage     = ConcreteStore mempty+  , origStorage = ConcreteStore mempty+  , balance     = Lit 0+  , nonce       = if isCreation code then Just 1 else Just 0+  , codehash    = hashcode code+  , opIxMap     = mkOpIxMap code+  , codeOps     = mkCodeOps code+  , external    = False+  }++isCreation :: ContractCode -> Bool+isCreation = \case+  InitCode _ _  -> True+  RuntimeCode _ -> False+  UnknownCode _ -> False++-- * Opcode dispatch (exec1)++-- | Update program counter+next :: (?op :: Word8) => EVM s ()+next = modifying (#state % #pc) (+ (opSize ?op))++-- | Executes the EVM one step+exec1 :: EVM s ()+exec1 = do+  vm <- get++  let+    -- Convenient aliases+    stk  = vm.state.stack+    self = vm.state.contract+    this = fromMaybe (internalError "state contract") (Map.lookup self vm.env.contracts)++    fees@FeeSchedule {..} = vm.block.schedule++    doStop = finishFrame (FrameReturned mempty)++    litSelf = maybeLitAddr self++  if isJust litSelf && (fromJust litSelf) > 0x0 && (fromJust litSelf) <= 0x9 then do+    -- call to precompile+    let ?op = 0x00 -- dummy value+    case bufLength vm.state.calldata of+      Lit calldatasize -> do+          copyBytesToMemory vm.state.calldata (Lit calldatasize) (Lit 0) (Lit 0)+          executePrecompile (fromJust litSelf) vm.state.gas 0 calldatasize 0 0 []+          vmx <- get+          case vmx.state.stack of+            x:_ -> case x of+              Lit 0 ->+                fetchAccount self $ \_ -> do+                  touchAccount self+                  vmError PrecompileFailure+              Lit _ ->+                fetchAccount self $ \_ -> do+                  touchAccount self+                  out <- use (#state % #returndata)+                  finishFrame (FrameReturned out)+              e -> partial $+                     UnexpectedSymbolicArg vmx.state.pc "precompile returned a symbolic value" (wrap [e])+            _ ->+              underrun+      e -> partial $+             UnexpectedSymbolicArg vm.state.pc "cannot call precompiles with symbolic data" (wrap [e])++  else if vm.state.pc >= opslen vm.state.code+    then doStop++    else do+      let ?op = case vm.state.code of+                  UnknownCode _ -> internalError "Cannot execute unknown code"+                  InitCode conc _ -> BS.index conc vm.state.pc+                  RuntimeCode (ConcreteRuntimeCode bs) -> BS.index bs vm.state.pc+                  RuntimeCode (SymbolicRuntimeCode ops) ->+                    fromMaybe (internalError "could not analyze symbolic code") $+                      maybeLitByte $ ops V.! vm.state.pc++      case getOp (?op) of++        OpPush0 -> do+          limitStack 1 $+            burn g_base $ do+              next+              pushSym (Lit 0)++        OpPush n' -> do+          let n = into n'+              !xs = case vm.state.code of+                UnknownCode _ -> internalError "Cannot execute unknown code"+                InitCode conc _ -> Lit $ word $ padRight n $ BS.take n (BS.drop (1 + vm.state.pc) conc)+                RuntimeCode (ConcreteRuntimeCode bs) -> Lit $ word $ BS.take n $ BS.drop (1 + vm.state.pc) bs+                RuntimeCode (SymbolicRuntimeCode ops) ->+                  let bytes = V.take n $ V.drop (1 + vm.state.pc) ops+                  in readWord (Lit 0) $ Expr.fromList $ padLeft' 32 bytes+          limitStack 1 $+            burn g_verylow $ do+              next+              pushSym xs++        OpDup i ->+          case preview (ix (into i - 1)) stk of+            Nothing -> underrun+            Just y ->+              limitStack 1 $+                burn g_verylow $ do+                  next+                  pushSym y++        OpSwap i ->+          if length stk < (into i) + 1+            then underrun+            else+              burn g_verylow $ do+                next+                zoom (#state % #stack) $ do+                  assign (ix 0) (stk ^?! ix (into i))+                  assign (ix (into i)) (stk ^?! ix 0)++        OpLog n ->+          notStatic $+          case stk of+            (xOffset':xSize':xs) ->+              if length xs < (into n)+              then underrun+              else+                forceConcrete2 (xOffset', xSize') "LOG" $ \(xOffset, xSize) -> do+                    bytes <- readMemory xOffset' xSize'+                    let (topics, xs') = splitAt (into n) xs+                        logs'         = (LogEntry (WAddr self) bytes topics) : vm.logs+                    case (tryFrom xSize) of+                      (Right sz) ->+                        burn (g_log + g_logdata * sz + (into n) * g_logtopic) $+                          accessMemoryRange xOffset xSize $ do+                            traceTopLog logs'+                            next+                            assign (#state % #stack) xs'+                            assign #logs logs'+                      _ -> vmError IllegalOverflow+            _ ->+              underrun++        OpStop -> doStop++        OpAdd -> stackOp2 g_verylow Expr.add+        OpMul -> stackOp2 g_low Expr.mul+        OpSub -> stackOp2 g_verylow Expr.sub++        OpDiv -> stackOp2 g_low Expr.div++        OpSdiv -> stackOp2 g_low Expr.sdiv++        OpMod -> stackOp2 g_low Expr.mod++        OpSmod -> stackOp2 g_low Expr.smod+        OpAddmod -> stackOp3 g_mid Expr.addmod+        OpMulmod -> stackOp3 g_mid Expr.mulmod++        OpLt -> stackOp2 g_verylow Expr.lt+        OpGt -> stackOp2 g_verylow Expr.gt+        OpSlt -> stackOp2 g_verylow Expr.slt+        OpSgt -> stackOp2 g_verylow Expr.sgt++        OpEq -> stackOp2 g_verylow Expr.eq+        OpIszero -> stackOp1 g_verylow Expr.iszero++        OpAnd -> stackOp2 g_verylow Expr.and+        OpOr -> stackOp2 g_verylow Expr.or+        OpXor -> stackOp2 g_verylow Expr.xor+        OpNot -> stackOp1 g_verylow Expr.not++        OpByte -> stackOp2 g_verylow (\i w -> Expr.padByte $ Expr.indexWord i w)++        OpShl -> stackOp2 g_verylow Expr.shl+        OpShr -> stackOp2 g_verylow Expr.shr+        OpSar -> stackOp2 g_verylow Expr.sar++        -- more accurately refered to as KECCAK+        OpSha3 ->+          case stk of+            xOffset':xSize':xs ->+              forceConcrete xOffset' "sha3 offset must be concrete" $+                \xOffset -> forceConcrete xSize' "sha3 size must be concrete" $ \xSize ->+                  burn (g_sha3 + g_sha3word * ceilDiv (unsafeInto xSize) 32) $+                    accessMemoryRange xOffset xSize $ do+                      hash <- readMemory xOffset' xSize' >>= \case+                        ConcreteBuf bs -> do+                          let hash' = keccak' bs+                          eqs <- use #keccakEqs+                          assign #keccakEqs $+                            PEq (Lit hash') (Keccak (ConcreteBuf bs)):eqs+                          pure $ Lit hash'+                        buf -> pure $ Keccak buf+                      next+                      assign (#state % #stack) (hash : xs)+            _ -> underrun++        OpAddress ->+          limitStack 1 $+            burn g_base (next >> pushAddr self)++        OpBalance ->+          case stk of+            x:xs -> forceAddr x "BALANCE" $ \a ->+              accessAndBurn a $+                fetchAccount a $ \c -> do+                  next+                  assign (#state % #stack) xs+                  pushSym c.balance+            [] ->+              underrun++        OpOrigin ->+          limitStack 1 . burn g_base $+            next >> pushAddr vm.tx.origin++        OpCaller ->+          limitStack 1 . burn g_base $+            next >> pushAddr vm.state.caller++        OpCallvalue ->+          limitStack 1 . burn g_base $+            next >> pushSym vm.state.callvalue++        OpCalldataload -> stackOp1 g_verylow $+          \ind -> Expr.readWord ind vm.state.calldata++        OpCalldatasize ->+          limitStack 1 . burn g_base $+            next >> pushSym (bufLength vm.state.calldata)++        OpCalldatacopy ->+          case stk of+            xTo':xFrom:xSize':xs ->+              forceConcrete2 (xTo', xSize') "CALLDATACOPY" $+                \(xTo, xSize) ->+                  burn (g_verylow + g_copy * ceilDiv (unsafeInto xSize) 32) $+                    accessMemoryRange xTo xSize $ do+                      next+                      assign (#state % #stack) xs+                      copyBytesToMemory vm.state.calldata xSize' xFrom xTo'+            _ -> underrun++        OpCodesize ->+          limitStack 1 . burn g_base $+            next >> pushSym (codelen vm.state.code)++        OpCodecopy ->+          case stk of+            memOffset':codeOffset:n':xs ->+              forceConcrete2 (memOffset', n') "CODECOPY" $+                \(memOffset,n) -> do+                  case toWord64 n of+                    Nothing -> vmError IllegalOverflow+                    Just n'' ->+                      if n'' <= ( (maxBound :: Word64) - g_verylow ) `div` g_copy * 32 then+                        burn (g_verylow + g_copy * ceilDiv (unsafeInto n) 32) $+                          accessMemoryRange memOffset n $ do+                            next+                            assign (#state % #stack) xs+                            case toBuf vm.state.code of+                              Just b -> copyBytesToMemory b n' codeOffset memOffset'+                              Nothing -> internalError "Cannot produce a buffer from UnknownCode"+                      else vmError IllegalOverflow+            _ -> underrun++        OpGasprice ->+          limitStack 1 . burn g_base $+            next >> push vm.tx.gasprice++        OpExtcodesize ->+          case stk of+            x':xs -> forceAddr x' "EXTCODESIZE" $ \x -> do+              let impl = accessAndBurn x $+                           fetchAccount x $ \c -> do+                             next+                             assign (#state % #stack) xs+                             case view bytecode c of+                               Just b -> pushSym (bufLength b)+                               Nothing -> pushSym $ CodeSize x+              case x of+                a@(LitAddr _) -> if a == cheatCode+                  then do+                    next+                    assign (#state % #stack) xs+                    pushSym (Lit 1)+                  else impl+                _ -> impl+            [] ->+              underrun++        OpExtcodecopy ->+          case stk of+            extAccount':memOffset':codeOffset:codeSize':xs ->+              forceConcrete2 (memOffset', codeSize') "EXTCODECOPY" $ \(memOffset, codeSize) -> do+                forceAddr extAccount' "EXTCODECOPY" $ \extAccount -> do+                  acc <- accessAccountForGas extAccount+                  let cost = if acc then g_warm_storage_read else g_cold_account_access+                  burn (cost + g_copy * ceilDiv (unsafeInto codeSize) 32) $+                    accessMemoryRange memOffset codeSize $+                      fetchAccount extAccount $ \c -> do+                        next+                        assign (#state % #stack) xs+                        case view bytecode c of+                          Just b -> copyBytesToMemory b codeSize' codeOffset memOffset'+                          Nothing -> do+                            pc <- use (#state % #pc)+                            partial $ UnexpectedSymbolicArg pc "Cannot copy from unknown code at" (wrap [extAccount])+            _ -> underrun++        OpReturndatasize ->+          limitStack 1 . burn g_base $+            next >> pushSym (bufLength vm.state.returndata)++        OpReturndatacopy ->+          case stk of+            xTo':xFrom:xSize':xs -> forceConcrete2 (xTo', xSize') "RETURNDATACOPY" $+              \(xTo, xSize) ->+                burn (g_verylow + g_copy * ceilDiv (unsafeInto xSize) 32) $+                  accessMemoryRange xTo xSize $ do+                    next+                    assign (#state % #stack) xs++                    let jump True = vmError ReturnDataOutOfBounds+                        jump False = copyBytesToMemory vm.state.returndata xSize' xFrom xTo'++                    case (xFrom, bufLength vm.state.returndata) of+                      (Lit f, Lit l) ->+                        jump $ l < f + xSize || f + xSize < f+                      _ -> do+                        let oob = Expr.lt (bufLength vm.state.returndata) (Expr.add xFrom xSize')+                            overflow = Expr.lt (Expr.add xFrom xSize') (xFrom)+                        branch (Expr.or oob overflow) jump+            _ -> underrun++        OpExtcodehash ->+          case stk of+            x':xs -> forceAddr x' "EXTCODEHASH" $ \x ->+              accessAndBurn x $ do+                next+                assign (#state % #stack) xs+                fetchAccount x $ \c ->+                   if accountEmpty c+                     then push (W256 0)+                     else case view bytecode c of+                            Just b -> pushSym $ keccak b+                            Nothing -> pushSym $ CodeHash x+            [] ->+              underrun++        OpBlockhash -> do+          -- We adopt the fake block hash scheme of the VMTests,+          -- so that blockhash(i) is the hash of i as decimal ASCII.+          stackOp1 g_blockhash $ \case+            Lit i -> if i + 256 < vm.block.number || i >= vm.block.number+                     then Lit 0+                     else (into i :: Integer) & show & Char8.pack & keccak' & Lit+            i -> BlockHash i++        OpCoinbase ->+          limitStack 1 . burn g_base $+            next >> pushAddr vm.block.coinbase++        OpTimestamp ->+          limitStack 1 . burn g_base $+            next >> pushSym vm.block.timestamp++        OpNumber ->+          limitStack 1 . burn g_base $+            next >> push vm.block.number++        OpPrevRandao -> do+          limitStack 1 . burn g_base $+            next >> push vm.block.prevRandao++        OpGaslimit ->+          limitStack 1 . burn g_base $+            next >> push (into vm.block.gaslimit)++        OpChainid ->+          limitStack 1 . burn g_base $+            next >> push vm.env.chainId++        OpSelfbalance ->+          limitStack 1 . burn g_low $+            next >> pushSym this.balance++        OpBaseFee ->+          limitStack 1 . burn g_base $+            next >> push vm.block.baseFee++        OpPop ->+          case stk of+            _:xs -> burn g_base (next >> assign (#state % #stack) xs)+            _    -> underrun++        OpMload ->+          case stk of+            x':xs -> forceConcrete x' "MLOAD" $ \x ->+              burn g_verylow $+                accessMemoryWord x $ do+                  next+                  buf <- readMemory (Lit x) (Lit 32)+                  let w = Expr.readWordFromBytes (Lit 0) buf+                  assign (#state % #stack) (w : xs)+            _ -> underrun++        OpMstore ->+          case stk of+            x':y:xs -> forceConcrete x' "MSTORE index" $ \x ->+              burn g_verylow $+                accessMemoryWord x $ do+                  next+                  gets (.state.memory) >>= \case+                    ConcreteMemory mem -> do+                      case y of+                        Lit w ->+                          writeMemory mem (unsafeInto x) (word256Bytes w)+                        _ -> do+                          -- copy out and move to symbolic memory+                          buf <- freezeMemory mem+                          assign (#state % #memory) (SymbolicMemory $ writeWord (Lit x) y buf)+                    SymbolicMemory mem ->+                      assign (#state % #memory) (SymbolicMemory $ writeWord (Lit x) y mem)+                  assign (#state % #stack) xs+            _ -> underrun++        OpMstore8 ->+          case stk of+            x':y:xs -> forceConcrete x' "MSTORE8" $ \x ->+              burn g_verylow $+                accessMemoryRange x 1 $ do+                  let yByte = indexWord (Lit 31) y+                  next+                  gets (.state.memory) >>= \case+                    ConcreteMemory mem -> do+                      case yByte of+                        LitByte byte ->+                          writeMemory mem (unsafeInto x) (BS.pack [byte])+                        _ -> do+                          -- copy out and move to symbolic memory+                          buf <- freezeMemory mem+                          assign (#state % #memory) (SymbolicMemory $ writeByte (Lit x) yByte buf)+                    SymbolicMemory mem ->+                      assign (#state % #memory) (SymbolicMemory $ writeByte (Lit x) yByte mem)++                  assign (#state % #stack) xs+            _ -> underrun++        OpSload ->+          case stk of+            x:xs -> do+              acc <- accessStorageForGas self x+              let cost = if acc then g_warm_storage_read else g_cold_sload+              burn cost $+                accessStorage self x $ \y -> do+                  next+                  assign (#state % #stack) (y:xs)+            _ -> underrun++        OpSstore ->+          notStatic $+          case stk of+            x:new:xs ->+              accessStorage self x $ \current -> do+                availableGas <- use (#state % #gas)++                if availableGas <= g_callstipend then+                  finishFrame (FrameErrored (OutOfGas availableGas g_callstipend))+                else do+                  let+                    original =+                      case Expr.simplify $ SLoad x this.origStorage of+                        Lit v -> v+                        _ -> 0+                    storage_cost =+                      case (maybeLitWord current, maybeLitWord new) of+                        (Just current', Just new') ->+                           if (current' == new') then g_sload+                           else if (current' == original) && (original == 0) then g_sset+                           else if (current' == original) then g_sreset+                           else g_sload++                        -- if any of the arguments are symbolic,+                        -- assume worst case scenario+                        _-> g_sset++                  acc <- accessStorageForGas self x+                  let cold_storage_cost = if acc then 0 else g_cold_sload+                  burn (storage_cost + cold_storage_cost) $ do+                    next+                    assign (#state % #stack) xs+                    modifying (#env % #contracts % ix self % #storage) (writeStorage x new)++                    case (maybeLitWord current, maybeLitWord new) of+                       (Just current', Just new') ->+                          unless (current' == new') $+                            if current' == original then+                              when (original /= 0 && new' == 0) $+                                refund (g_sreset + g_access_list_storage_key)+                            else do+                              when (original /= 0) $+                                if current' == 0+                                then unRefund (g_sreset + g_access_list_storage_key)+                                else when (new' == 0) $ refund (g_sreset + g_access_list_storage_key)+                              when (original == new') $+                                if original == 0+                                then refund (g_sset - g_sload)+                                else refund (g_sreset - g_sload)+                       -- if any of the arguments are symbolic,+                       -- don't change the refund counter+                       _ -> noop+            _ -> underrun++        OpJump ->+          case stk of+            x:xs ->+              burn g_mid $ forceConcrete x "JUMP: symbolic jumpdest" $ \x' ->+                case toInt x' of+                  Nothing -> vmError BadJumpDestination+                  Just i -> checkJump i xs+            _ -> underrun++        OpJumpi -> do+          case stk of+            (x:y:xs) -> forceConcrete x "JUMPI: symbolic jumpdest" $ \x' ->+                burn g_high $+                  let jump :: Bool -> EVM s ()+                      jump False = assign (#state % #stack) xs >> next+                      jump _    = case toInt x' of+                        Nothing -> vmError BadJumpDestination+                        Just i -> checkJump i xs+                  in branch y jump+            _ -> underrun++        OpPc ->+          limitStack 1 . burn g_base $+            next >> push (unsafeInto vm.state.pc)++        OpMsize ->+          limitStack 1 . burn g_base $+            next >> push (into vm.state.memorySize)++        OpGas ->+          limitStack 1 . burn g_base $+            next >> push (into (vm.state.gas - g_base))++        OpJumpdest -> burn g_jumpdest next++        OpExp ->+          -- NOTE: this can be done symbolically using unrolling like this:+          --       https://hackage.haskell.org/package/sbv-9.0/docs/src/Data.SBV.Core.Model.html#.%5E+          --       However, it requires symbolic gas, since the gas depends on the exponent+          case stk of+            base:exponent':xs -> forceConcrete exponent' "EXP: symbolic exponent" $ \exponent ->+              let cost = if exponent == 0+                         then g_exp+                         else g_exp + g_expbyte * unsafeInto (ceilDiv (1 + log2 exponent) 8)+              in burn cost $ do+                next+                (#state % #stack) .= Expr.exp base exponent' : xs+            _ -> underrun++        OpSignextend -> stackOp2 g_low Expr.sex++        OpCreate ->+          notStatic $+          case stk of+            xValue:xOffset':xSize':xs -> forceConcrete2 (xOffset', xSize') "CREATE" $+              \(xOffset, xSize) -> do+                accessMemoryRange xOffset xSize $ do+                  availableGas <- use (#state % #gas)+                  let+                    (cost, gas') = costOfCreate fees availableGas xSize False+                  newAddr <- createAddress self this.nonce+                  _ <- accessAccountForGas newAddr+                  burn cost $ do+                    initCode <- readMemory xOffset' xSize'+                    create self this xSize gas' xValue xs newAddr initCode+            _ -> underrun++        OpCall ->+          case stk of+            xGas':xTo':xValue:xInOffset':xInSize':xOutOffset':xOutSize':xs ->+              forceConcrete5 (xGas', xInOffset', xInSize', xOutOffset', xOutSize') "CALL" $+              \(xGas, xInOffset, xInSize, xOutOffset, xOutSize) ->+                branch (Expr.gt xValue (Lit 0)) $ \gt0 -> do+                  (if gt0 then notStatic else id) $+                    forceAddr xTo' "unable to determine a call target" $ \xTo ->+                      case tryFrom xGas of+                        Left _ -> vmError IllegalOverflow+                        Right gas ->+                          delegateCall this gas xTo xTo xValue xInOffset xInSize xOutOffset xOutSize xs $+                            \callee -> do+                              let from' = fromMaybe self vm.config.overrideCaller+                              zoom #state $ do+                                assign #callvalue xValue+                                assign #caller from'+                                assign #contract callee+                              assign (#config % #overrideCaller) Nothing+                              touchAccount from'+                              touchAccount callee+                              transfer from' callee xValue+            _ ->+              underrun++        OpCallcode ->+          case stk of+            xGas':xTo':xValue:xInOffset':xInSize':xOutOffset':xOutSize':xs ->+              forceConcrete5 (xGas', xInOffset', xInSize', xOutOffset', xOutSize') "CALLCODE" $+              \(xGas, xInOffset, xInSize, xOutOffset, xOutSize) ->+                forceAddr xTo' "unable to determine a call target" $ \xTo ->+                  case tryFrom xGas of+                    Left _ -> vmError IllegalOverflow+                    Right gas ->+                      delegateCall this gas xTo self xValue xInOffset xInSize xOutOffset xOutSize xs $ \_ -> do+                        zoom #state $ do+                          assign #callvalue xValue+                          assign #caller $ fromMaybe self vm.config.overrideCaller+                        assign (#config % #overrideCaller) Nothing+                        touchAccount self+            _ ->+              underrun++        OpReturn ->+          case stk of+            xOffset':xSize':_ -> forceConcrete2 (xOffset', xSize') "RETURN" $ \(xOffset, xSize) ->+              accessMemoryRange xOffset xSize $ do+                output <- readMemory xOffset' xSize'+                let+                  codesize = fromMaybe (internalError "processing opcode RETURN. Cannot return dynamically sized abstract data")+                               . maybeLitWord . bufLength $ output+                  maxsize = vm.block.maxCodeSize+                  creation = case vm.frames of+                    [] -> vm.tx.isCreate+                    frame:_ -> case frame.context of+                       CreationContext {} -> True+                       CallContext {} -> False+                if creation+                then+                  if codesize > maxsize+                  then+                    finishFrame (FrameErrored (MaxCodeSizeExceeded maxsize codesize))+                  else do+                    let frameReturned = burn (g_codedeposit * unsafeInto codesize) $+                                          finishFrame (FrameReturned output)+                        frameErrored = finishFrame $ FrameErrored InvalidFormat+                    case readByte (Lit 0) output of+                      LitByte 0xef -> frameErrored+                      LitByte _ -> frameReturned+                      y -> branch (Expr.eqByte y (LitByte 0xef)) $ \case+                          True -> frameErrored+                          False -> frameReturned+                else+                   finishFrame (FrameReturned output)+            _ -> underrun++        OpDelegatecall ->+          case stk of+            xGas':xTo:xInOffset':xInSize':xOutOffset':xOutSize':xs ->+              forceConcrete5 (xGas', xInOffset', xInSize', xOutOffset', xOutSize') "DELEGATECALL" $+              \(xGas, xInOffset, xInSize, xOutOffset, xOutSize) ->+                case wordToAddr xTo of+                  Nothing -> do+                    loc <- codeloc+                    let msg = "Unable to determine a call target"+                    partial $ UnexpectedSymbolicArg (snd loc) msg [SomeExpr xTo]+                  Just xTo' ->+                    case tryFrom xGas of+                      Left _ -> vmError IllegalOverflow+                      Right gas ->+                        delegateCall this gas xTo' self (Lit 0) xInOffset xInSize xOutOffset xOutSize xs $+                          \_ -> touchAccount self+            _ -> underrun++        OpCreate2 -> notStatic $+          case stk of+            xValue:xOffset':xSize':xSalt':xs ->+              forceConcrete3 (xOffset', xSize', xSalt') "CREATE2" $+              \(xOffset, xSize, xSalt) ->+                accessMemoryRange xOffset xSize $ do+                  availableGas <- use (#state % #gas)+                  buf <- readMemory xOffset' xSize'+                  forceConcreteBuf buf "CREATE2" $+                    \initCode -> do+                      let+                        (cost, gas') = costOfCreate fees availableGas xSize True+                      newAddr <- create2Address self xSalt initCode+                      _ <- accessAccountForGas newAddr+                      burn cost $+                        create self this xSize gas' xValue xs newAddr (ConcreteBuf initCode)+            _ -> underrun++        OpStaticcall ->+          case stk of+            xGas':xTo:xInOffset':xInSize':xOutOffset':xOutSize':xs ->+              forceConcrete5 (xGas', xInOffset', xInSize', xOutOffset', xOutSize') "STATICCALL" $+              \(xGas, xInOffset, xInSize, xOutOffset, xOutSize) -> do+                case wordToAddr xTo of+                  Nothing -> do+                    loc <- codeloc+                    let msg = "Unable to determine a call target"+                    partial $ UnexpectedSymbolicArg (snd loc) msg [SomeExpr xTo]+                  Just xTo' ->+                    case tryFrom xGas of+                      Left _ -> vmError IllegalOverflow+                      Right gas ->+                        delegateCall this gas xTo' xTo' (Lit 0) xInOffset xInSize xOutOffset xOutSize xs $+                          \callee -> do+                            zoom #state $ do+                              assign #callvalue (Lit 0)+                              assign #caller $ fromMaybe self (vm.config.overrideCaller)+                              assign #contract callee+                              assign #static True+                            assign (#config % #overrideCaller) Nothing+                            touchAccount self+                            touchAccount callee+            _ ->+              underrun++        OpSelfdestruct ->+          notStatic $+          case stk of+            [] -> underrun+            (xTo':_) -> forceAddr xTo' "SELFDESTRUCT" $ \case+              xTo@(LitAddr _) -> do+                acc <- accessAccountForGas xTo+                let cost = if acc then 0 else g_cold_account_access+                    funds = this.balance+                    recipientExists = accountExists xTo vm+                branch (Expr.iszero $ Expr.eq funds (Lit 0)) $ \hasFunds -> do+                  let c_new = if (not recipientExists) && hasFunds+                              then g_selfdestruct_newaccount+                              else 0+                  burn (g_selfdestruct + c_new + cost) $ do+                    selfdestruct self+                    touchAccount xTo++                    if hasFunds+                    then fetchAccount xTo $ \_ -> do+                           #env % #contracts % ix xTo % #balance %= (Expr.add funds)+                           assign (#env % #contracts % ix self % #balance) (Lit 0)+                           doStop+                    else do+                      doStop+              a -> do+                pc <- use (#state % #pc)+                partial $ UnexpectedSymbolicArg pc "trying to self destruct to a symbolic address" (wrap [a])++        OpRevert ->+          case stk of+            xOffset':xSize':_ -> forceConcrete2 (xOffset', xSize') "REVERT" $ \(xOffset, xSize) ->+              accessMemoryRange xOffset xSize $ do+                output <- readMemory xOffset' xSize'+                finishFrame (FrameReverted output)+            _ -> underrun++        OpUnknown xxx ->+          vmError $ UnrecognizedOpcode xxx++transfer :: Expr EAddr -> Expr EAddr -> Expr EWord -> EVM s ()+transfer _ _ (Lit 0) = pure ()+transfer src dst val = do+  sb <- preuse $ #env % #contracts % ix src % #balance+  db <- preuse $ #env % #contracts % ix dst % #balance+  baseState <- use (#config % #baseState)+  let mkc = case baseState of+              AbstractBase -> unknownContract+              EmptyBase -> const emptyContract+  case (sb, db) of+    -- both sender and recipient in state+    (Just srcBal, Just _) ->+      branch (Expr.gt val srcBal) $ \case+        True -> vmError $ BalanceTooLow val srcBal+        False -> do+          (#env % #contracts % ix src % #balance) %= (`Expr.sub` val)+          (#env % #contracts % ix dst % #balance) %= (`Expr.add` val)+    -- sender not in state+    (Nothing, Just _) -> do+      case src of+        LitAddr _ -> do+          (#env % #contracts) %= (Map.insert src (mkc src))+          transfer src dst val+        SymAddr _ -> do+          pc <- use (#state % #pc)+          partial $ UnexpectedSymbolicArg pc "Attempting to transfer eth from a symbolic address that is not present in the state" (wrap [src])+        GVar _ -> internalError "Unexpected GVar"+    -- recipient not in state+    (_ , Nothing) -> do+      case dst of+        LitAddr _ -> do+          (#env % #contracts) %= (Map.insert dst (mkc dst))+          transfer src dst val+        SymAddr _ -> do+          pc <- use (#state % #pc)+          partial $ UnexpectedSymbolicArg pc "Attempting to transfer eth to a symbolic address that is not present in the state" (wrap [dst])+        GVar _ -> internalError "Unexpected GVar"++-- | Checks a *CALL for failure; OOG, too many callframes, memory access etc.+callChecks+  :: (?op :: Word8)+  => Contract -> Word64 -> Expr EAddr -> Expr EAddr -> Expr EWord -> W256 -> W256 -> W256 -> W256 -> [Expr EWord]+   -- continuation with gas available for call+  -> (Word64 -> EVM s ())+  -> EVM s ()+callChecks this xGas xContext xTo xValue xInOffset xInSize xOutOffset xOutSize xs continue = do+  vm <- get+  let fees = vm.block.schedule+  accessMemoryRange xInOffset xInSize $+    accessMemoryRange xOutOffset xOutSize $ do+      availableGas <- use (#state % #gas)+      let recipientExists = accountExists xContext vm+      (cost, gas') <- costOfCall fees recipientExists xValue availableGas xGas xTo+      burn (cost - gas') $+        branch (Expr.gt xValue this.balance) $ \case+          True -> do+            assign (#state % #stack) (Lit 0 : xs)+            assign (#state % #returndata) mempty+            pushTrace $ ErrorTrace (BalanceTooLow xValue this.balance)+            next+          False ->+            if length vm.frames >= 1024+            then do+              assign (#state % #stack) (Lit 0 : xs)+              assign (#state % #returndata) mempty+              pushTrace $ ErrorTrace CallDepthLimitReached+              next+            else continue gas'++precompiledContract+  :: (?op :: Word8)+  => Contract+  -> Word64+  -> Addr+  -> Addr+  -> Expr EWord+  -> W256 -> W256 -> W256 -> W256+  -> [Expr EWord]+  -> EVM s ()+precompiledContract this xGas precompileAddr recipient xValue inOffset inSize outOffset outSize xs+  = callChecks this xGas (LitAddr recipient) (LitAddr precompileAddr) xValue inOffset inSize outOffset outSize xs $ \gas' ->+    do+      executePrecompile precompileAddr gas' inOffset inSize outOffset outSize xs+      self <- use (#state % #contract)+      stk <- use (#state % #stack)+      pc' <- use (#state % #pc)+      result' <- use #result+      case result' of+        Nothing -> case stk of+          x:_ -> case maybeLitWord x of+            Just 0 ->+              pure ()+            Just 1 ->+              fetchAccount (LitAddr recipient) $ \_ -> do+                touchAccount self+                touchAccount (LitAddr recipient)+                transfer self (LitAddr recipient) xValue+            _ -> partial $+                   UnexpectedSymbolicArg pc' "unexpected return value from precompile" (wrap [x])+          _ -> underrun+        _ -> pure ()++executePrecompile+  :: (?op :: Word8)+  => Addr+  -> Word64 -> W256 -> W256 -> W256 -> W256 -> [Expr EWord]+  -> EVM s ()+executePrecompile preCompileAddr gasCap inOffset inSize outOffset outSize xs  = do+  vm <- get+  input <- readMemory (Lit inOffset) (Lit inSize)+  let fees = vm.block.schedule+      cost = costOfPrecompile fees preCompileAddr input+      notImplemented = internalError $ "precompile at address " <> show preCompileAddr <> " not yet implemented"+      precompileFail = burn (gasCap - cost) $ do+                         assign (#state % #stack) (Lit 0 : xs)+                         pushTrace $ ErrorTrace PrecompileFailure+                         next+  if cost > gasCap then+    burn gasCap $ do+      assign (#state % #stack) (Lit 0 : xs)+      next+  else burn cost $+    case preCompileAddr of+      -- ECRECOVER+      0x1 ->+        -- TODO: support symbolic variant+        forceConcreteBuf input "ECRECOVER" $ \input' -> do+          case EVM.Precompiled.execute 0x1 (truncpadlit 128 input') 32 of+            Nothing -> do+              -- return no output for invalid signature+              assign (#state % #stack) (Lit 1 : xs)+              assign (#state % #returndata) mempty+              next+            Just output -> do+              assign (#state % #stack) (Lit 1 : xs)+              assign (#state % #returndata) (ConcreteBuf output)+              copyBytesToMemory (ConcreteBuf output) (Lit outSize) (Lit 0) (Lit outOffset)+              next++      -- SHA2-256+      0x2 ->+        forceConcreteBuf input "SHA2-256" $ \input' -> do+          let+            hash = sha256Buf input'+            sha256Buf x = ConcreteBuf $ BA.convert (Crypto.hash x :: Digest SHA256)+          assign (#state % #stack) (Lit 1 : xs)+          assign (#state % #returndata) hash+          copyBytesToMemory hash (Lit outSize) (Lit 0) (Lit outOffset)+          next++      -- RIPEMD-160+      0x3 ->+        -- TODO: support symbolic variant+        forceConcreteBuf input "RIPEMD160" $ \input' -> do+          let+            padding = BS.pack $ replicate 12 0+            hash' = BA.convert (Crypto.hash input' :: Digest RIPEMD160)+            hash  = ConcreteBuf $ padding <> hash'+          assign (#state % #stack) (Lit 1 : xs)+          assign (#state % #returndata) hash+          copyBytesToMemory hash (Lit outSize) (Lit 0) (Lit outOffset)+          next++      -- IDENTITY+      0x4 -> do+          assign (#state % #stack) (Lit 1 : xs)+          assign (#state % #returndata) input+          copyCallBytesToMemory input (Lit outSize) (Lit outOffset)+          next++      -- MODEXP+      0x5 ->+        -- TODO: support symbolic variant+        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 (Lit outSize) (Lit 0) (Lit outOffset)+          next++      -- ECADD+      0x6 ->+        -- TODO: support symbolic variant+        forceConcreteBuf input "ECADD" $ \input' ->+          case EVM.Precompiled.execute 0x6 (truncpadlit 128 input') 64 of+            Nothing -> precompileFail+            Just output -> do+              let truncpaddedOutput = ConcreteBuf $ truncpadlit 64 output+              assign (#state % #stack) (Lit 1 : xs)+              assign (#state % #returndata) truncpaddedOutput+              copyBytesToMemory truncpaddedOutput (Lit outSize) (Lit 0) (Lit outOffset)+              next++      -- ECMUL+      0x7 ->+        -- TODO: support symbolic variant+        forceConcreteBuf input "ECMUL" $ \input' ->+          case EVM.Precompiled.execute 0x7 (truncpadlit 96 input') 64 of+          Nothing -> precompileFail+          Just output -> do+            let truncpaddedOutput = ConcreteBuf $ truncpadlit 64 output+            assign (#state % #stack) (Lit 1 : xs)+            assign (#state % #returndata) truncpaddedOutput+            copyBytesToMemory truncpaddedOutput (Lit outSize) (Lit 0) (Lit outOffset)+            next++      -- ECPAIRING+      0x8 ->+        -- TODO: support symbolic variant+        forceConcreteBuf input "ECPAIR" $ \input' ->+          case EVM.Precompiled.execute 0x8 input' 32 of+          Nothing -> precompileFail+          Just output -> do+            let truncpaddedOutput = ConcreteBuf $ truncpadlit 32 output+            assign (#state % #stack) (Lit 1 : xs)+            assign (#state % #returndata) truncpaddedOutput+            copyBytesToMemory truncpaddedOutput (Lit outSize) (Lit 0) (Lit outOffset)+            next++      -- BLAKE2+      0x9 ->+        -- TODO: support symbolic variant+        forceConcreteBuf input "BLAKE2" $ \input' -> do+          case (BS.length input', 1 >= BS.last input') of+            (213, True) -> case EVM.Precompiled.execute 0x9 input' 64 of+              Just output -> do+                let truncpaddedOutput = ConcreteBuf $ truncpadlit 64 output+                assign (#state % #stack) (Lit 1 : xs)+                assign (#state % #returndata) truncpaddedOutput+                copyBytesToMemory truncpaddedOutput (Lit outSize) (Lit 0) (Lit outOffset)+                next+              Nothing -> precompileFail+            _ -> precompileFail++      _ -> notImplemented++truncpadlit :: Int -> ByteString -> ByteString+truncpadlit n xs = if m > n then BS.take n xs+                   else BS.append xs (BS.replicate (n - m) 0)+  where m = BS.length xs++lazySlice :: W256 -> W256 -> ByteString -> LS.ByteString+lazySlice offset size bs =+  let bs' = LS.take (unsafeInto size) (LS.drop (unsafeInto offset) (fromStrict bs))+  in bs' <> LS.replicate (unsafeInto size - LS.length bs') 0++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+  in (lenb, lene, lenm)++--- checks if a range of ByteString bs starting at offset and length size is all zeros.+isZero :: W256 -> W256 -> ByteString -> Bool+isZero offset size bs =+  LS.all (== 0) $+    LS.take (unsafeInto size) $+      LS.drop (unsafeInto offset) $+        fromStrict bs++asInteger :: LS.ByteString -> Integer+asInteger xs = if xs == mempty then 0+  else 256 * asInteger (LS.init xs)+      + into (LS.last xs)++-- * Opcode helper actions++noop :: Monad m => m ()+noop = pure ()++pushTo :: MonadState s m => Lens s s [a] [a] -> a -> m ()+pushTo f x = f %= (x :)++pushToSequence :: MonadState s m => Setter s s (Seq a) (Seq a) -> a -> m ()+pushToSequence f x = f %= (Seq.|> x)++getCodeLocation :: VM s -> CodeLocation+getCodeLocation vm = (vm.state.contract, vm.state.pc)++query :: Query s -> EVM s ()+query = assign #result . Just . HandleEffect . Query++choose :: Choose s -> EVM s ()+choose = assign #result . Just . HandleEffect . Choose++branch :: forall s. Expr EWord -> (Bool -> EVM s ()) -> EVM s ()+branch cond continue = do+  loc <- codeloc+  pathconds <- use #constraints+  query $ PleaseAskSMT cond pathconds (choosePath loc)+  where+    condSimp = Expr.simplify cond+    choosePath :: CodeLocation -> BranchCondition -> EVM s ()+    choosePath loc (Case v) = do+      assign #result Nothing+      pushTo #constraints $ if v then Expr.simplifyProp (condSimp ./= Lit 0) else Expr.simplifyProp (condSimp .== Lit 0)+      (iteration, _) <- use (#iterations % at loc % non (0,[]))+      stack <- use (#state % #stack)+      assign (#cache % #path % at (loc, iteration)) (Just v)+      assign (#iterations % at loc) (Just (iteration + 1, stack))+      continue v+    -- Both paths are possible; we ask for more input+    choosePath loc Unknown =+      choose . PleaseChoosePath condSimp $ choosePath loc . Case++-- | Construct RPC Query and halt execution until resolved+fetchAccount :: Expr EAddr -> (Contract -> EVM s ()) -> EVM s ()+fetchAccount addr continue =+  use (#env % #contracts % at addr) >>= \case+    Just c -> continue c+    Nothing -> case addr of+      SymAddr _ -> do+        pc <- use (#state % #pc)+        partial $ UnexpectedSymbolicArg pc "trying to access a symbolic address that isn't already present in storage" (wrap [addr])+      LitAddr a -> do+        use (#cache % #fetched % at a) >>= \case+          Just c -> do+            assign (#env % #contracts % at addr) (Just c)+            continue c+          Nothing -> do+            base <- use (#config % #baseState)+            assign (#result) . Just . HandleEffect . Query $+              PleaseFetchContract a base+                (\c -> do assign (#cache % #fetched % at a) (Just c)+                          assign (#env % #contracts % at addr) (Just c)+                          assign #result Nothing+                          continue c)+      GVar _ -> internalError "Unexpected GVar"++accessStorage+  :: Expr EAddr+  -> Expr EWord+  -> (Expr EWord -> EVM s ())+  -> EVM s ()+accessStorage addr slot continue = do+  use (#env % #contracts % at addr) >>= \case+    Just c ->+      case readStorage slot c.storage of+        Just x ->+          continue x+        Nothing ->+          if c.external then+            forceConcreteAddr addr "cannot read storage from symbolic addresses via rpc" $ \addr' ->+              forceConcrete slot "cannot read symbolic slots via RPC" $ \slot' -> do+                -- check if the slot is cached+                contract <- preuse (#cache % #fetched % ix addr')+                case contract of+                  Nothing -> internalError "contract marked external not found in cache"+                  Just fetched -> case readStorage (Lit slot') fetched.storage of+                              Nothing -> mkQuery addr' slot'+                              Just val -> continue val+          else do+            modifying (#env % #contracts % ix addr % #storage) (writeStorage slot (Lit 0))+            continue $ Lit 0+    Nothing ->+      fetchAccount addr $ \_ ->+        accessStorage addr slot continue+  where+      mkQuery a s = query $+        PleaseFetchSlot a s+          (\x -> do+              modifying (#cache % #fetched % ix a % #storage) (writeStorage (Lit s) (Lit x))+              modifying (#env % #contracts % ix (LitAddr a) % #storage) (writeStorage (Lit s) (Lit x))+              assign #result Nothing+              continue (Lit x))++accountExists :: Expr EAddr -> VM s -> Bool+accountExists addr vm =+  case Map.lookup addr vm.env.contracts of+    Just c -> not (accountEmpty c)+    Nothing -> False++-- EIP 161+accountEmpty :: Contract -> Bool+accountEmpty c =+  case c.code of+    RuntimeCode (ConcreteRuntimeCode "") -> True+    RuntimeCode (SymbolicRuntimeCode b) -> null b+    _ -> False+  && c.nonce == (Just 0)+  -- TODO: handle symbolic balance...+  && c.balance == Lit 0++-- * How to finalize a transaction+finalize :: EVM s ()+finalize = do+  let+    revertContracts  = use (#tx % #txReversion) >>= assign (#env % #contracts)+    revertSubstate   = assign (#tx % #substate) (SubState mempty mempty mempty mempty mempty)++  use #result >>= \case+    Just (VMFailure (Revert _)) -> do+      revertContracts+      revertSubstate+    Just (VMFailure _) -> do+      -- burn remaining gas+      assign (#state % #gas) 0+      revertContracts+      revertSubstate+    Just (VMSuccess output) -> do+      -- deposit the code from a creation tx+      pc' <- use (#state % #pc)+      creation <- use (#tx % #isCreate)+      createe  <- use (#state % #contract)+      createeExists <- (Map.member createe) <$> use (#env % #contracts)+      let onContractCode contractCode =+            when (creation && createeExists) $ replaceCode createe contractCode+      case output of+        ConcreteBuf bs ->+          onContractCode $ RuntimeCode (ConcreteRuntimeCode bs)+        _ ->+          case Expr.toList output of+            Nothing ->+              partial $+                UnexpectedSymbolicArg pc' "runtime code cannot have an abstract lentgh" (wrap [output])+            Just ops ->+              onContractCode $ RuntimeCode (SymbolicRuntimeCode ops)+    _ ->+      internalError "Finalising an unfinished tx."++  -- compute and pay the refund to the caller and the+  -- corresponding payment to the miner+  block        <- use #block+  tx           <- use #tx+  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+    minerPay     = tx.priorityFee * (into gasUsed)++  modifying (#env % #contracts)+     (Map.adjust (over #balance (Expr.add (Lit originPay))) tx.origin)+  modifying (#env % #contracts)+     (Map.adjust (over #balance (Expr.add (Lit minerPay))) block.coinbase)+  touchAccount block.coinbase++  -- perform state trie clearing (EIP 161), of selfdestructs+  -- and touched accounts. addresses are cleared if they have+  --    a) selfdestructed, or+  --    b) been touched and+  --    c) are empty.+  -- (see Yellow Paper "Accrued Substate")+  --+  -- remove any destructed addresses+  destroyedAddresses <- use (#tx % #substate % #selfdestructs)+  modifying (#env % #contracts)+    (Map.filterWithKey (\k _ -> (k `notElem` destroyedAddresses)))+  -- then, clear any remaining empty and touched addresses+  touchedAddresses <- use (#tx % #substate % #touchedAccounts)+  modifying (#env % #contracts)+    (Map.filterWithKey+      (\k a -> not ((k `elem` touchedAddresses) && accountEmpty a)))++-- | Loads the selected contract as the current contract to execute+loadContract :: Expr EAddr -> State (VM s) ()+loadContract target =+  preuse (#env % #contracts % ix target % #code) >>=+    \case+      Nothing ->+        internalError "Call target doesn't exist"+      Just targetCode -> do+        assign (#state % #contract) target+        assign (#state % #code)     targetCode+        assign (#state % #codeContract) target++limitStack :: Int -> EVM s () -> EVM s ()+limitStack n continue = do+  stk <- use (#state % #stack)+  if length stk + n > 1024+    then vmError StackLimitExceeded+    else continue++notStatic :: EVM s () -> EVM s ()+notStatic continue = do+  bad <- use (#state % #static)+  if bad+    then vmError StateChangeWhileStatic+    else continue++-- | Burn gas, failing if insufficient gas is available+burn :: Word64 -> EVM s () -> EVM s ()+burn n continue = do+  available <- use (#state % #gas)+  if n <= available+    then do+      #state % #gas %= (subtract n)+      #burned %= (+ n)+      continue+    else+      vmError (OutOfGas available n)++forceAddr :: Expr EWord -> String -> (Expr EAddr -> EVM s ()) -> EVM s ()+forceAddr n msg continue = case wordToAddr n of+  Nothing -> do+    vm <- get+    partial $ UnexpectedSymbolicArg vm.state.pc msg (wrap [n])+  Just c -> continue c++forceConcrete :: Expr EWord -> String -> (W256 -> EVM s ()) -> EVM s ()+forceConcrete n msg continue = case maybeLitWord n of+  Nothing -> do+    vm <- get+    partial $ UnexpectedSymbolicArg vm.state.pc msg (wrap [n])+  Just c -> continue c++forceConcreteAddr :: Expr EAddr -> String -> (Addr -> EVM s ()) -> EVM s ()+forceConcreteAddr n msg continue = case maybeLitAddr n of+  Nothing -> do+    vm <- get+    partial $ UnexpectedSymbolicArg vm.state.pc msg (wrap [n])+  Just c -> continue c++forceConcreteAddr2 :: (Expr EAddr, Expr EAddr) -> String -> ((Addr, Addr) -> EVM s ()) -> EVM s ()+forceConcreteAddr2 (n,m) msg continue = case (maybeLitAddr n, maybeLitAddr m) of+  (Just c, Just d) -> continue (c,d)+  _ -> do+    vm <- get+    partial $ UnexpectedSymbolicArg vm.state.pc msg (wrap [n, m])++forceConcrete2 :: (Expr EWord, Expr EWord) -> String -> ((W256, W256) -> EVM s ()) -> EVM s ()+forceConcrete2 (n,m) msg continue = case (maybeLitWord n, maybeLitWord m) of+  (Just c, Just d) -> continue (c, d)+  _ -> do+    vm <- get+    partial $ UnexpectedSymbolicArg vm.state.pc msg (wrap [n, m])++forceConcrete3 :: (Expr EWord, Expr EWord, Expr EWord) -> String -> ((W256, W256, W256) -> EVM s ()) -> EVM s ()+forceConcrete3 (k,n,m) msg continue = case (maybeLitWord k, maybeLitWord n, maybeLitWord m) of+  (Just c, Just d, Just f) -> continue (c, d, f)+  _ -> do+    vm <- get+    partial $ UnexpectedSymbolicArg vm.state.pc msg (wrap [k, n, m])++forceConcrete4 :: (Expr EWord, Expr EWord, Expr EWord, Expr EWord) -> String -> ((W256, W256, W256, W256) -> EVM s ()) -> EVM s ()+forceConcrete4 (k,l,n,m) msg continue = case (maybeLitWord k, maybeLitWord l, maybeLitWord n, maybeLitWord m) of+  (Just b, Just c, Just d, Just f) -> continue (b, c, d, f)+  _ -> do+    vm <- get+    partial $ UnexpectedSymbolicArg vm.state.pc msg (wrap [k, l, n, m])++forceConcrete5 :: (Expr EWord, Expr EWord, Expr EWord, Expr EWord, Expr EWord) -> String -> ((W256, W256, W256, W256, W256) -> EVM s ()) -> EVM s ()+forceConcrete5 (k,l,m,n,o) msg continue = case (maybeLitWord k, maybeLitWord l, maybeLitWord m, maybeLitWord n, maybeLitWord o) of+  (Just a, Just b, Just c, Just d, Just e) -> continue (a, b, c, d, e)+  _ -> do+    vm <- get+    partial $ UnexpectedSymbolicArg vm.state.pc msg (wrap [k, l, m, n, o])++forceConcrete6 :: (Expr EWord, Expr EWord, Expr EWord, Expr EWord, Expr EWord, Expr EWord) -> String -> ((W256, W256, W256, W256, W256, W256) -> EVM s ()) -> EVM s ()+forceConcrete6 (k,l,m,n,o,p) msg continue = case (maybeLitWord k, maybeLitWord l, maybeLitWord m, maybeLitWord n, maybeLitWord o, maybeLitWord p) of+  (Just a, Just b, Just c, Just d, Just e, Just f) -> continue (a, b, c, d, e, f)+  _ -> do+    vm <- get+    partial $ UnexpectedSymbolicArg vm.state.pc msg (wrap [k, l, m, n, o, p])++forceConcreteBuf :: Expr Buf -> String -> (ByteString -> EVM s ()) -> EVM s ()+forceConcreteBuf (ConcreteBuf b) _ continue = continue b+forceConcreteBuf b msg _ = do+    vm <- get+    partial $ UnexpectedSymbolicArg vm.state.pc msg (wrap [b])++-- * Substate manipulation+refund :: Word64 -> EVM s ()+refund n = do+  self <- use (#state % #contract)+  pushTo (#tx % #substate % #refunds) (self, n)++unRefund :: Word64 -> EVM s ()+unRefund n = do+  self <- use (#state % #contract)+  refs <- use (#tx % #substate % #refunds)+  assign (#tx % #substate % #refunds)+    (filter (\(a,b) -> not (a == self && b == n)) refs)++touchAccount :: Expr EAddr -> EVM s ()+touchAccount = pushTo ((#tx % #substate) % #touchedAccounts)++selfdestruct :: Expr EAddr -> EVM s ()+selfdestruct = pushTo ((#tx % #substate) % #selfdestructs)++accessAndBurn :: Expr EAddr -> EVM s () -> EVM s ()+accessAndBurn x cont = do+  FeeSchedule {..} <- use (#block % #schedule)+  acc <- accessAccountForGas x+  let cost = if acc then g_warm_storage_read else g_cold_account_access+  burn cost cont++-- | returns a wrapped boolean- if true, this address has been touched before in the txn (warm gas cost as in EIP 2929)+-- otherwise cold+accessAccountForGas :: Expr EAddr -> EVM s Bool+accessAccountForGas addr = do+  accessedAddrs <- use (#tx % #substate % #accessedAddresses)+  let accessed = member addr accessedAddrs+  assign (#tx % #substate % #accessedAddresses) (insert addr accessedAddrs)+  pure accessed++-- | returns a wrapped boolean- if true, this slot has been touched before in the txn (warm gas cost as in EIP 2929)+-- otherwise cold+accessStorageForGas :: Expr EAddr -> Expr EWord -> EVM s Bool+accessStorageForGas addr key = do+  accessedStrkeys <- use (#tx % #substate % #accessedStorageKeys)+  case maybeLitWord key of+    Just litword -> do+      let accessed = member (addr, litword) accessedStrkeys+      assign (#tx % #substate % #accessedStorageKeys) (insert (addr, litword) accessedStrkeys)+      pure accessed+    _ -> return False++-- * Cheat codes++-- The cheat code is 7109709ecfa91a80626ff3989d68f67f5b1dd12d.+-- Call this address using one of the cheatActions below to do+-- special things, e.g. changing the block timestamp. Beware that+-- these are necessarily hevm specific.+cheatCode :: Expr EAddr+cheatCode = LitAddr $ unsafeInto (keccak' "hevm cheat code")++cheat+  :: (?op :: Word8)+  => (W256, W256) -> (W256, W256)+  -> EVM s ()+cheat (inOffset, inSize) (outOffset, outSize) = do+  vm <- get+  input <- readMemory (Lit $ inOffset + 4) (Lit $ inSize - 4)+  abi <- readBytes 4 (Lit 0) <$> readMemory (Lit inOffset) (Lit 4)+  pushTrace $ FrameTrace (CallContext cheatCode cheatCode inOffset inSize (Lit 0) (maybeLitWord abi) input vm.env.contracts vm.tx.substate)+  case maybeLitWord abi of+    Nothing -> partial $ UnexpectedSymbolicArg vm.state.pc "symbolic cheatcode selector" (wrap [abi])+    Just (unsafeInto -> abi') ->+      case Map.lookup abi' cheatActions of+        Nothing ->+          vmError (BadCheatCode abi')+        Just action -> do+            action (Lit outOffset) (Lit outSize) input+            popTrace+            next+            push 1++type CheatAction s = Expr EWord -> Expr EWord -> Expr Buf -> EVM s ()++cheatActions :: Map FunctionSelector (CheatAction s)+cheatActions =+  Map.fromList+    [ action "ffi(string[])" $+        \sig outOffset outSize input -> do+          vm <- get+          if vm.config.allowFFI then+            case decodeBuf [AbiArrayDynamicType AbiStringType] input of+              CAbi valsArr -> case valsArr of+                [AbiArrayDynamic AbiStringType strsV] ->+                  let+                    cmd = fmap+                            (\case+                              (AbiString a) -> unpack $ decodeUtf8 a+                              _ -> "")+                            (V.toList strsV)+                    cont bs = do+                      let encoded = ConcreteBuf bs+                      assign (#state % #returndata) encoded+                      copyBytesToMemory encoded outSize (Lit 0) outOffset+                      assign #result Nothing+                  in query (PleaseDoFFI cmd cont)+                _ -> vmError (BadCheatCode sig)+              _ -> vmError (BadCheatCode sig)+          else+            let msg = "ffi disabled: run again with --ffi if you want to allow tests to call external scripts"+            in partial $ UnexpectedSymbolicArg vm.state.pc msg [],++      action "warp(uint256)" $+        \sig _ _ input -> case decodeStaticArgs 0 1 input of+          [x]  -> assign (#block % #timestamp) x+          _ -> vmError (BadCheatCode sig),++      action "deal(address,uint256)" $+        \sig _ _ input -> case decodeStaticArgs 0 2 input of+          [a, amt] ->+            forceAddr a "vm.deal: cannot decode target into an address" $ \usr ->+              fetchAccount usr $ \_ -> do+                assign (#env % #contracts % ix usr % #balance) amt+          _ -> vmError (BadCheatCode sig),++      action "assume(bool)" $+        \sig _ _ input -> case decodeStaticArgs 0 1 input of+          [c] -> modifying #constraints ((:) (PEq c (Lit 1)))+          _ -> vmError (BadCheatCode sig),++      action "roll(uint256)" $+        \sig _ _ input -> case decodeStaticArgs 0 1 input of+          [x] -> forceConcrete x "cannot roll to a symbolic block number" (assign (#block % #number))+          _ -> vmError (BadCheatCode sig),++      action "store(address,bytes32,bytes32)" $+        \sig _ _ input -> case decodeStaticArgs 0 3 input of+          [a, slot, new] -> case wordToAddr a of+            Just a'@(LitAddr _) -> fetchAccount a' $ \_ ->+              modifying (#env % #contracts % ix a' % #storage) (writeStorage slot new)+            _ -> vmError (BadCheatCode sig)+          _ -> vmError (BadCheatCode sig),++      action "load(address,bytes32)" $+        \sig outOffset _ input -> case decodeStaticArgs 0 2 input of+          [a, slot] -> case wordToAddr a of+            Just a'@(LitAddr _) -> fetchAccount a' $ \_ ->+              accessStorage a' slot $ \res -> do+                assign (#state % #returndata % word256At (Lit 0)) res+                let buf = writeWord (Lit 0) res (ConcreteBuf "")+                copyBytesToMemory buf (Lit 32) (Lit 0) outOffset+            _ -> vmError (BadCheatCode sig)+          _ -> vmError (BadCheatCode sig),++      action "sign(uint256,bytes32)" $+        \sig outOffset _ input -> case decodeStaticArgs 0 2 input of+          [sk, hash] ->+            forceConcrete2 (sk, hash) "cannot sign symbolic data" $ \(sk', hash') -> do+              let (v,r,s) = EVM.Sign.sign hash' (toInteger sk')+                  encoded = encodeAbiValue $+                    AbiTuple (V.fromList+                      [ AbiUInt 8 $ into v+                      , AbiBytes 32 (word256Bytes r)+                      , AbiBytes 32 (word256Bytes s)+                      ])+              assign (#state % #returndata) (ConcreteBuf encoded)+              copyBytesToMemory (ConcreteBuf encoded) (Lit . unsafeInto . BS.length $ encoded) (Lit 0) outOffset+          _ -> vmError (BadCheatCode sig),++      action "addr(uint256)" $+        \sig outOffset _ input -> case decodeStaticArgs 0 1 input of+          [sk] -> forceConcrete sk "cannot derive address for a symbolic key" $ \sk' -> do+            let a = EVM.Sign.deriveAddr $ into sk'+            case a of+              Nothing -> vmError (BadCheatCode sig)+              Just address -> do+                let expAddr = litAddr address+                assign (#state % #returndata % word256At (Lit 0)) expAddr+                let buf = ConcreteBuf $ word256Bytes (into address)+                copyBytesToMemory buf (Lit 32) (Lit 0) outOffset+          _ -> vmError (BadCheatCode sig),++      action "prank(address)" $+        \sig _ _ input -> case decodeStaticArgs 0 1 input of+          [addr]  -> case wordToAddr addr of+            Just a -> assign (#config % #overrideCaller) (Just a)+            Nothing -> vmError (BadCheatCode sig)+          _ -> vmError (BadCheatCode sig)++    ]+  where+    action s f = (abiKeccak s, f (abiKeccak s))++-- * General call implementation ("delegateCall")+-- note that the continuation is ignored in the precompile case+delegateCall+  :: (?op :: Word8)+  => Contract -> Word64 -> Expr EAddr -> Expr EAddr -> Expr EWord -> W256 -> W256 -> W256 -> W256+  -> [Expr EWord]+  -> (Expr EAddr -> EVM s ())+  -> EVM s ()+delegateCall this gasGiven xTo xContext xValue xInOffset xInSize xOutOffset xOutSize xs continue+  | isPrecompileAddr xTo+      = forceConcreteAddr2 (xTo, xContext) "Cannot call precompile with symbolic addresses" $+          \(xTo', xContext') ->+            precompiledContract this gasGiven xTo' xContext' xValue xInOffset xInSize xOutOffset xOutSize xs+  | xTo == cheatCode = do+      assign (#state % #stack) xs+      cheat (xInOffset, xInSize) (xOutOffset, xOutSize)+  | otherwise =+      callChecks this gasGiven xContext xTo xValue xInOffset xInSize xOutOffset xOutSize xs $+        \xGas -> do+          vm0 <- get+          fetchAccount xTo $ \target -> case target.code of+              UnknownCode _ -> do+                pc <- use (#state % #pc)+                partial $ UnexpectedSymbolicArg pc "call target has unknown code" (wrap [xTo])+              _ -> do+                burn xGas $ do+                  calldata <- readMemory (Lit xInOffset) (Lit xInSize)+                  abi <- maybeLitWord . readBytes 4 (Lit 0) <$> readMemory (Lit xInOffset) (Lit 4)+                  let newContext = CallContext+                                    { target    = xTo+                                    , context   = xContext+                                    , offset    = xOutOffset+                                    , size      = xOutSize+                                    , codehash  = target.codehash+                                    , callreversion = vm0.env.contracts+                                    , subState  = vm0.tx.substate+                                    , abi+                                    , calldata+                                    }+                  pushTrace (FrameTrace newContext)+                  next+                  vm1 <- get++                  pushTo #frames $ Frame+                    { state = vm1.state { stack = xs }+                    , context = newContext+                    }++                  let clearInitCode = \case+                        (InitCode _ _) -> InitCode mempty mempty+                        a -> a++                  newMemory <- ConcreteMemory <$> VUnboxed.Mutable.new 0+                  zoom #state $ do+                    assign #gas xGas+                    assign #pc 0+                    assign #code (clearInitCode target.code)+                    assign #codeContract xTo+                    assign #stack mempty+                    assign #memory newMemory+                    assign #memorySize 0+                    assign #returndata mempty+                    assign #calldata calldata+                  continue xTo++-- -- * Contract creation++-- EIP 684+collision :: Maybe Contract -> Bool+collision c' = case c' of+  Just c -> c.nonce /= Just 0 || case c.code of+    RuntimeCode (ConcreteRuntimeCode "") -> False+    RuntimeCode (SymbolicRuntimeCode b) -> not $ null b+    _ -> True+  Nothing -> False++create :: (?op :: Word8)+  => Expr EAddr -> Contract+  -> W256 -> Word64 -> Expr EWord -> [Expr EWord] -> Expr EAddr -> Expr Buf -> EVM s ()+create self this xSize xGas xValue xs newAddr initCode = do+  vm0 <- get+  if xSize > vm0.block.maxCodeSize * 2+  then do+    assign (#state % #stack) (Lit 0 : xs)+    assign (#state % #returndata) mempty+    vmError $ MaxInitCodeSizeExceeded (vm0.block.maxCodeSize * 2) xSize+  else if this.nonce == Just maxBound+  then do+    assign (#state % #stack) (Lit 0 : xs)+    assign (#state % #returndata) mempty+    pushTrace $ ErrorTrace NonceOverflow+    next+  else if length vm0.frames >= 1024+  then do+    assign (#state % #stack) (Lit 0 : xs)+    assign (#state % #returndata) mempty+    pushTrace $ ErrorTrace CallDepthLimitReached+    next+  else if collision $ Map.lookup newAddr vm0.env.contracts+  then burn xGas $ do+    assign (#state % #stack) (Lit 0 : xs)+    assign (#state % #returndata) mempty+    modifying (#env % #contracts % ix self % #nonce) (fmap ((+) 1))+    next+  -- do we have enough balance+  else branch (Expr.gt xValue this.balance) $ \case+      True -> do+        assign (#state % #stack) (Lit 0 : xs)+        assign (#state % #returndata) mempty+        pushTrace $ ErrorTrace $ BalanceTooLow xValue this.balance+        next+        touchAccount self+        touchAccount newAddr+      -- are we overflowing the nonce+      False -> burn xGas $ do+        case parseInitCode initCode of+          Nothing ->+            partial $ UnexpectedSymbolicArg vm0.state.pc "initcode must have a concrete prefix" []+          Just c -> do+            let+              newContract = initialContract c+              newContext  =+                CreationContext { address   = newAddr+                                , codehash  = newContract.codehash+                                , createreversion = vm0.env.contracts+                                , substate  = vm0.tx.substate+                                }++            zoom (#env % #contracts) $ do+              oldAcc <- use (at newAddr)+              let oldBal = maybe (Lit 0) (.balance) oldAcc++              assign (at newAddr) (Just (newContract & #balance .~ oldBal))+              modifying (ix self % #nonce) (fmap ((+) 1))++            let+              resetStorage :: Expr Storage -> Expr Storage+              resetStorage = \case+                  ConcreteStore _ -> ConcreteStore mempty+                  AbstractStore a -> AbstractStore a+                  SStore _ _ p -> resetStorage p+                  GVar _  -> error "unexpected global variable"++            modifying (#env % #contracts % ix newAddr % #storage) resetStorage+            modifying (#env % #contracts % ix newAddr % #origStorage) resetStorage++            transfer self newAddr xValue++            pushTrace (FrameTrace newContext)+            next+            vm1 <- get+            pushTo #frames $ Frame+              { context = newContext+              , state   = vm1.state { stack = xs }+              }++            state <- lift blankState+            assign #state $ state+              { contract     = newAddr+              , codeContract = newAddr+              , code         = c+              , callvalue    = xValue+              , caller       = self+              , gas          = xGas+              }++-- | Parses a raw Buf into an InitCode+--+-- solidity implements constructor args by appending them to the end of+-- the initcode. we support this internally by treating initCode as a+-- concrete region (initCode) followed by a potentially symbolic region+-- (arguments).+--+-- when constructing a contract that has symbolic construcor args, we+-- need to apply some heuristics to convert the (unstructured) initcode+-- in memory into this structured representation. The (unsound, bad,+-- hacky) way that we do this, is by: looking for the first potentially+-- symbolic byte in the input buffer and then splitting it there into code / data.+parseInitCode :: Expr Buf -> Maybe ContractCode+parseInitCode (ConcreteBuf b) = Just (InitCode b mempty)+parseInitCode buf = if V.null conc+                    then Nothing+                    else Just $ InitCode (BS.pack $ V.toList conc) sym+  where+    conc = Expr.concretePrefix buf+    -- unsafeInto: findIndex will always be positive+    sym = Expr.drop (unsafeInto (V.length conc)) buf++-- | Replace a contract's code, like when CREATE returns+-- from the constructor code.+replaceCode :: Expr EAddr -> ContractCode -> EVM s ()+replaceCode target newCode =+  zoom (#env % #contracts % at target) $+    get >>= \case+      Just now -> case now.code of+        InitCode _ _ ->+          put . Just $+            ((initialContract newCode) :: Contract)+              { balance = now.balance+              , nonce = now.nonce+              , storage = now.storage+              }+        RuntimeCode _ ->+          internalError $ "Can't replace code of deployed contract " <> show target+        UnknownCode _ ->+          internalError "Can't replace unknown code"+      Nothing ->+        internalError "Can't replace code of nonexistent contract"++replaceCodeOfSelf :: ContractCode -> EVM s ()+replaceCodeOfSelf newCode = do+  vm <- get+  replaceCode vm.state.contract newCode++resetState :: EVM s ()+resetState = do+  state <- lift blankState+  modify' $ \vm -> vm { result = Nothing, frames = [], state }++-- * VM error implementation++vmError :: EvmError -> EVM s ()+vmError e = finishFrame (FrameErrored e)++partial :: PartialExec -> EVM s ()+partial e = assign #result (Just (Unfinished e))++wrap :: Typeable a => [Expr a] -> [SomeExpr]+wrap = fmap SomeExpr++underrun :: EVM s ()+underrun = vmError StackUnderrun++-- | A stack frame can be popped in three ways.+data FrameResult+  = FrameReturned (Expr Buf) -- ^ STOP, RETURN, or no more code+  | FrameReverted (Expr Buf) -- ^ REVERT+  | FrameErrored EvmError -- ^ Any other error+  deriving Show++-- | This function defines how to pop the current stack frame in either of+-- the ways specified by 'FrameResult'.+--+-- It also handles the case when the current stack frame is the only one;+-- in this case, we set the final '_result' of the VM execution.+finishFrame :: FrameResult -> EVM s ()+finishFrame how = do+  oldVm <- get++  case oldVm.frames of+    -- Is the current frame the only one?+    [] -> do+      case how of+          FrameReturned output -> assign #result . Just $ VMSuccess output+          FrameReverted buffer -> assign #result . Just $ VMFailure (Revert buffer)+          FrameErrored e       -> assign #result . Just $ VMFailure e+      finalize++    -- Are there some remaining frames?+    nextFrame : remainingFrames -> do++      -- Insert a debug trace.+      insertTrace $+        case how of+          FrameErrored e ->+            ErrorTrace e+          FrameReverted e ->+            ErrorTrace (Revert e)+          FrameReturned output ->+            ReturnTrace output nextFrame.context+      -- Pop to the previous level of the debug trace stack.+      popTrace++      -- Pop the top frame.+      assign #frames remainingFrames+      -- Install the state of the frame to which we shall return.+      assign #state nextFrame.state++      -- When entering a call, the gas allowance is counted as burned+      -- in advance; this unburns the remainder and adds it to the+      -- parent frame.+      let remainingGas = oldVm.state.gas+          reclaimRemainingGasAllowance = do+            modifying #burned (subtract remainingGas)+            modifying (#state % #gas) (+ remainingGas)++      -- Now dispatch on whether we were creating or calling,+      -- and whether we shall return, revert, or internalError(six cases).+      case nextFrame.context of++        -- Were we calling?+        CallContext _ _ (Lit -> outOffset) (Lit -> outSize) _ _ _ reversion substate' -> do++          -- Excerpt K.1. from the yellow paper:+          -- K.1. Deletion of an Account Despite Out-of-gas.+          -- At block 2675119, in the transaction 0xcf416c536ec1a19ed1fb89e4ec7ffb3cf73aa413b3aa9b77d60e4fd81a4296ba,+          -- an account at address 0x03 was called and an out-of-gas occurred during the call.+          -- Against the equation (197), this added 0x03 in the set of touched addresses, and this transaction turned σ[0x03] into ∅.++          -- In other words, we special case address 0x03 and keep it in the set of touched accounts during revert+          touched <- use (#tx % #substate % #touchedAccounts)++          let+            substate'' = over #touchedAccounts (maybe id cons (find (LitAddr 3 ==) touched)) substate'+            revertContracts = assign (#env % #contracts) reversion+            revertSubstate  = assign (#tx % #substate) substate''++          case how of+            -- Case 1: Returning from a call?+            FrameReturned output -> do+              assign (#state % #returndata) output+              copyCallBytesToMemory output outSize outOffset+              reclaimRemainingGasAllowance+              push 1++            -- Case 2: Reverting during a call?+            FrameReverted output -> do+              revertContracts+              revertSubstate+              assign (#state % #returndata) output+              copyCallBytesToMemory output outSize outOffset+              reclaimRemainingGasAllowance+              push 0++            -- Case 3: Error during a call?+            FrameErrored _ -> do+              revertContracts+              revertSubstate+              assign (#state % #returndata) mempty+              push 0+        -- Or were we creating?+        CreationContext _ _ reversion substate' -> do+          creator <- use (#state % #contract)+          let+            createe = oldVm.state.contract+            revertContracts = assign (#env % #contracts) reversion'+            revertSubstate  = assign (#tx % #substate) substate'++            -- persist the nonce through the reversion+            reversion' = (Map.adjust (over #nonce (fmap ((+) 1))) creator) reversion++          case how of+            -- Case 4: Returning during a creation?+            FrameReturned output -> do+              let onContractCode contractCode = do+                    replaceCode createe contractCode+                    assign (#state % #returndata) mempty+                    reclaimRemainingGasAllowance+                    pushAddr createe+              case output of+                ConcreteBuf bs ->+                  onContractCode $ RuntimeCode (ConcreteRuntimeCode bs)+                _ ->+                  case Expr.toList output of+                    Nothing -> partial $+                      UnexpectedSymbolicArg+                        oldVm.state.pc+                        "runtime code cannot have an abstract length"+                        (wrap [output])+                    Just newCode -> do+                      onContractCode $ RuntimeCode (SymbolicRuntimeCode newCode)++            -- Case 5: Reverting during a creation?+            FrameReverted output -> do+              revertContracts+              revertSubstate+              assign (#state % #returndata) output+              reclaimRemainingGasAllowance+              push 0++            -- Case 6: Error during a creation?+            FrameErrored _ -> do+              revertContracts+              revertSubstate+              assign (#state % #returndata) mempty+              push 0+++-- * Memory helpers++accessUnboundedMemoryRange+  :: Word64+  -> Word64+  -> EVM s ()+  -> EVM s ()+accessUnboundedMemoryRange _ 0 continue = continue+accessUnboundedMemoryRange f l continue = do+  m0 <- use (#state % #memorySize)+  fees <- gets (.block.schedule)+  let m1 = 32 * ceilDiv (max m0 (f + l)) 32+  burn (memoryCost fees m1 - memoryCost fees m0) $ do+    assign (#state % #memorySize) m1+    continue++accessMemoryRange+  :: W256+  -> W256+  -> EVM s ()+  -> EVM s ()+accessMemoryRange _ 0 continue = continue+accessMemoryRange offs sz continue =+  case (,) <$> toWord64 offs <*> toWord64 sz of+    Nothing -> vmError IllegalOverflow+    Just (offs64, sz64) ->+      if offs64 + sz64 < sz64+        then vmError IllegalOverflow+        else accessUnboundedMemoryRange offs64 sz64 continue++accessMemoryWord+  :: W256 -> EVM s () -> EVM s ()+accessMemoryWord x = accessMemoryRange x 32++copyBytesToMemory+  :: Expr Buf -> Expr EWord -> Expr EWord -> Expr EWord -> EVM s ()+copyBytesToMemory bs size srcOffset memOffset =+  if size == Lit 0 then noop+  else do+    gets (.state.memory) >>= \case+      ConcreteMemory mem ->+        case (bs, size, srcOffset, memOffset) of+          (ConcreteBuf b, Lit size', Lit srcOffset', Lit memOffset') -> do+            let src =+                  if srcOffset' >= unsafeInto (BS.length b) then+                    BS.replicate (unsafeInto size') 0+                  else+                    BS.take (unsafeInto size') $+                    padRight (unsafeInto size') $+                    BS.drop (unsafeInto srcOffset') b++            writeMemory mem (unsafeInto memOffset') src+          _ -> do+            -- copy out and move to symbolic memory+            buf <- freezeMemory mem+            assign (#state % #memory) $+              SymbolicMemory $ copySlice srcOffset memOffset size bs buf+      SymbolicMemory mem ->+        assign (#state % #memory) $+          SymbolicMemory $ copySlice srcOffset memOffset size bs mem++copyCallBytesToMemory+  :: Expr Buf -> Expr EWord -> Expr EWord -> EVM s ()+copyCallBytesToMemory bs size yOffset =+  copyBytesToMemory bs (Expr.min size (bufLength bs)) (Lit 0) yOffset++readMemory :: Expr EWord -> Expr EWord -> EVM s (Expr Buf)+readMemory offset' size' = do+  vm <- get+  case vm.state.memory of+    ConcreteMemory mem -> do+      case (offset', size') of+        (Lit offset, Lit size) -> do+          let memSize :: Word64 = unsafeInto (VUnboxed.Mutable.length mem)+          if size > Expr.maxBytes ||+             offset + size > Expr.maxBytes ||+             offset >= into memSize then+            -- reads past memory are all zeros+            pure $ ConcreteBuf $ BS.replicate (unsafeInto size) 0+          else do+            let pastEnd = (unsafeInto offset + unsafeInto size) - unsafeInto memSize+            let fromMemSize = if pastEnd > 0 then unsafeInto size - pastEnd else unsafeInto size++            buf <- VUnboxed.freeze $ VUnboxed.Mutable.slice (unsafeInto offset) fromMemSize mem+            let dataFromMem = BS.pack $ VUnboxed.toList buf+            pure $ ConcreteBuf $ dataFromMem <> BS.replicate pastEnd 0+        _ -> do+          buf <- freezeMemory mem+          pure $ copySlice offset' (Lit 0) size' buf mempty+    SymbolicMemory mem ->+      pure $ copySlice offset' (Lit 0) size' mem mempty++-- * Tracing++withTraceLocation :: TraceData -> EVM s Trace+withTraceLocation x = do+  vm <- get+  let this = fromJust $ currentContract vm+  pure Trace+    { tracedata = x+    , contract = this+    , opIx = fromMaybe 0 $ this.opIxMap SV.!? vm.state.pc+    }++pushTrace :: TraceData -> EVM s ()+pushTrace x = do+  trace <- withTraceLocation x+  modifying #traces $+    \t -> Zipper.children $ Zipper.insert (Node trace []) t++insertTrace :: TraceData -> EVM s ()+insertTrace x = do+  trace <- withTraceLocation x+  modifying #traces $+    \t -> Zipper.nextSpace $ Zipper.insert (Node trace []) t++popTrace :: EVM s ()+popTrace =+  modifying #traces $+    \t -> case Zipper.parent t of+            Nothing -> internalError "internal internalError(trace root)"+            Just t' -> Zipper.nextSpace t'++zipperRootForest :: Zipper.TreePos Zipper.Empty a -> Forest a+zipperRootForest z =+  case Zipper.parent z of+    Nothing -> Zipper.toForest z+    Just z' -> zipperRootForest (Zipper.nextSpace z')++traceForest :: VM s -> Forest Trace+traceForest vm = zipperRootForest vm.traces++traceForest' :: Expr End -> Forest Trace+traceForest' (Success _ (Traces f _) _ _) = f+traceForest' (Partial _ (Traces f _) _) = f+traceForest' (Failure _ (Traces f _) _) = f+traceForest' (ITE {}) = internalError"Internal Error: ITE does not contain a trace"+traceForest' (GVar {}) = internalError"Internal Error: Unexpected GVar"++traceContext :: Expr End -> Map (Expr EAddr) Contract+traceContext (Success _ (Traces _ c) _ _) = c+traceContext (Partial _ (Traces _ c) _) = c+traceContext (Failure _ (Traces _ c) _) = c+traceContext (ITE {}) = internalError"Internal Error: ITE does not contain a trace"+traceContext (GVar {}) = internalError"Internal Error: Unexpected GVar"++traceTopLog :: [Expr Log] -> EVM s ()+traceTopLog [] = noop+traceTopLog ((LogEntry addr bytes topics) : _) = do+  trace <- withTraceLocation (EventTrace addr bytes topics)+  modifying #traces $+    \t -> Zipper.nextSpace (Zipper.insert (Node trace []) t)+traceTopLog ((GVar _) : _) = internalError "unexpected global variable"++-- * Stack manipulation++push :: W256 -> EVM s ()+push = pushSym . Lit++pushSym :: Expr EWord -> EVM s ()+pushSym x = #state % #stack %= (x :)++pushAddr :: Expr EAddr -> EVM s ()+pushAddr (LitAddr x) = #state % #stack %= (Lit (into x) :)+pushAddr x@(SymAddr _) = #state % #stack %= (WAddr x :)+pushAddr (GVar _) = internalError "Unexpected GVar"++stackOp1+  :: (?op :: Word8)+  => Word64+  -> (Expr EWord -> Expr EWord)+  -> EVM s ()+stackOp1 cost f =+  use (#state % #stack) >>= \case+    x:xs ->+      burn cost $ do+        next+        let !y = f x+        #state % #stack .= y : xs+    _ ->+      underrun++stackOp2+  :: (?op :: Word8)+  => Word64+  -> (Expr EWord -> Expr EWord -> Expr EWord)+  -> EVM s ()+stackOp2 cost f =+  use (#state % #stack) >>= \case+    x:y:xs ->+      burn cost $ do+        next+        #state % #stack .= f x y : xs+    _ ->+      underrun++stackOp3+  :: (?op :: Word8)+  => Word64+  -> (Expr EWord -> Expr EWord -> Expr EWord -> Expr EWord)+  -> EVM s ()+stackOp3 cost f =+  use (#state % #stack) >>= \case+    x:y:z:xs ->+      burn cost $ do+      next+      (#state % #stack) .= f x y z : xs+    _ ->+      underrun++-- * Bytecode data functions++use' :: (VM s -> a) -> EVM s a+use' f = do+  vm <- get+  pure (f vm)++checkJump :: Int -> [Expr EWord] -> EVM s ()+checkJump x xs = noJumpIntoInitData x $ do+  vm <- get+  case isValidJumpDest vm x of+    True -> do+      #state % #stack .= xs+      #state % #pc .= x+    False -> vmError BadJumpDestination++-- fails with partial if we're trying to jump into the symbolic region of an `InitCode`+noJumpIntoInitData :: Int -> EVM s () -> EVM s ()+noJumpIntoInitData idx cont = do+  vm <- get+  case vm.state.code of+    -- init code is totally concrete, so we don't return partial if we're+    -- jumping beyond the range of `ops`+    InitCode _ (ConcreteBuf "") -> cont+    -- init code has a symbolic region, so check if we're trying to jump into+    -- the symbolic region and return partial if we are+    InitCode ops _ -> if idx > BS.length ops+                      then partial $ JumpIntoSymbolicCode vm.state.pc idx+                      else cont+    -- we're not executing init code, so nothing to check here+    _ -> cont++isValidJumpDest :: VM s -> Int -> Bool+isValidJumpDest vm x = let+    code = vm.state.code+    self = vm.state.codeContract+    contract = fromMaybe+      (internalError "self not found in current contracts")+      (Map.lookup self vm.env.contracts)+    op = case code of+      UnknownCode _ -> internalError "Cannot analyze jumpdests for unknown code"+      InitCode ops _ -> BS.indexMaybe ops x+      RuntimeCode (ConcreteRuntimeCode ops) -> BS.indexMaybe ops x+      RuntimeCode (SymbolicRuntimeCode ops) -> ops V.!? x >>= maybeLitByte+  in case op of+       Nothing -> False+       Just b -> 0x5b == b && OpJumpdest == snd (contract.codeOps V.! (contract.opIxMap SV.! x))++opSize :: Word8 -> Int+opSize x | x >= 0x60 && x <= 0x7f = into x - 0x60 + 2+opSize _                          = 1++--  i of the resulting vector contains the operation index for+-- the program counter value i.  This is needed because source map+-- entries are per operation, not per byte.+mkOpIxMap :: ContractCode -> SV.Vector Int+mkOpIxMap (UnknownCode _) = internalError "Cannot build opIxMap for unknown code"+mkOpIxMap (InitCode conc _)+  = SV.create $ SV.new (BS.length conc) >>= \v ->+      -- Loop over the byte string accumulating a vector-mutating action.+      -- This is somewhat obfuscated, but should be fast.+      let (_, _, _, m) = BS.foldl' (go v) (0 :: Word8, 0, 0, pure ()) conc+      in m >> pure v+      where+        -- concrete case+        go v (0, !i, !j, !m) x | x >= 0x60 && x <= 0x7f =+          {- Start of PUSH op. -} (x - 0x60 + 1, i + 1, j,     m >> SV.write v i j)+        go v (1, !i, !j, !m) _ =+          {- End of PUSH op. -}   (0,            i + 1, j + 1, m >> SV.write v i j)+        go v (0, !i, !j, !m) _ =+          {- Other op. -}         (0,            i + 1, j + 1, m >> SV.write v i j)+        go v (n, !i, !j, !m) _ =+          {- PUSH data. -}        (n - 1,        i + 1, j,     m >> SV.write v i j)++mkOpIxMap (RuntimeCode (ConcreteRuntimeCode ops)) =+  mkOpIxMap (InitCode ops mempty) -- a bit hacky++mkOpIxMap (RuntimeCode (SymbolicRuntimeCode ops))+  = SV.create $ SV.new (length ops) >>= \v ->+      let (_, _, _, m) = foldl (go v) (0, 0, 0, pure ()) (stripBytecodeMetadataSym $ V.toList ops)+      in m >> pure v+      where+        go v (0, !i, !j, !m) x = case maybeLitByte x of+          Just x' -> if x' >= 0x60 && x' <= 0x7f+            -- start of PUSH op --+                     then (x' - 0x60 + 1, i + 1, j,     m >> SV.write v i j)+            -- other data --+                     else (0,             i + 1, j + 1, m >> SV.write v i j)+          _ -> internalError $ "cannot analyze symbolic code:\nx: " <> show x <> " i: " <> show i <> " j: " <> show j++        go v (1, !i, !j, !m) _ =+          {- End of PUSH op. -}   (0,            i + 1, j + 1, m >> SV.write v i j)+        go v (n, !i, !j, !m) _ =+          {- PUSH data. -}        (n - 1,        i + 1, j,     m >> SV.write v i j)+++vmOp :: VM s -> Maybe Op+vmOp vm =+  let i  = vm ^. #state % #pc+      code' = vm ^. #state % #code+      (op, pushdata) = case code' of+        UnknownCode _ -> internalError "cannot get op from unknown code"+        InitCode xs' _ ->+          (BS.index xs' i, fmap LitByte $ BS.unpack $ BS.drop i xs')+        RuntimeCode (ConcreteRuntimeCode xs') ->+          (BS.index xs' i, fmap LitByte $ BS.unpack $ BS.drop i xs')+        RuntimeCode (SymbolicRuntimeCode xs') ->+          ( fromMaybe (internalError "unexpected symbolic code") . maybeLitByte $ xs' V.! i , V.toList $ V.drop i xs')+  in if (opslen code' < i)+     then Nothing+     else Just (readOp op pushdata)++vmOpIx :: VM s -> Maybe Int+vmOpIx vm =+  do self <- currentContract vm+     self.opIxMap SV.!? vm.state.pc++-- Maps operation indicies into a pair of (bytecode index, operation)+mkCodeOps :: ContractCode -> V.Vector (Int, Op)+mkCodeOps contractCode =+  let l = case contractCode of+            UnknownCode _ -> internalError "Cannot make codeOps for unknown code"+            InitCode bytes _ ->+              LitByte <$> (BS.unpack bytes)+            RuntimeCode (ConcreteRuntimeCode ops) ->+              LitByte <$> (BS.unpack $ stripBytecodeMetadata ops)+            RuntimeCode (SymbolicRuntimeCode ops) ->+              stripBytecodeMetadataSym $ V.toList ops+  in V.fromList . toList $ go 0 l+  where+    go !i !xs =+      case uncons xs of+        Nothing ->+          mempty+        Just (x, xs') ->+          let x' = fromMaybe (internalError "unexpected symbolic code argument") $ maybeLitByte x+              j = opSize x'+          in (i, readOp x' xs') Seq.<| go (i + j) (drop j xs)++-- * Gas cost calculation helpers++-- Gas cost function for CALL, transliterated from the Yellow Paper.+costOfCall+  :: FeeSchedule Word64+  -> Bool -> Expr EWord -> Word64 -> Word64 -> Expr EAddr+  -> EVM s (Word64, Word64)+costOfCall (FeeSchedule {..}) recipientExists (Lit xValue) availableGas xGas target = do+  acc <- accessAccountForGas target+  let call_base_gas = if acc then g_warm_storage_read else g_cold_account_access+      c_new = if not recipientExists && xValue /= 0+            then g_newaccount+            else 0+      c_xfer = if xValue /= 0  then g_callvalue else 0+      c_extra = call_base_gas + c_xfer + c_new+      c_gascap =  if availableGas >= c_extra+                  then min xGas (allButOne64th (availableGas - c_extra))+                  else xGas+      c_callgas = if xValue /= 0 then c_gascap + g_callstipend else c_gascap+  pure (c_gascap + c_extra, c_callgas)+-- calls are free if value is symbolic :)+costOfCall _ _ _ _ _ _ = pure (0,0)++-- Gas cost of create, including hash cost if needed+costOfCreate+  :: FeeSchedule Word64+  -> Word64 -> W256 -> Bool -> (Word64, Word64)+costOfCreate (FeeSchedule {..}) availableGas size hashNeeded = (createCost, initGas)+  where+    byteCost   = if hashNeeded then g_sha3word + g_initcodeword else g_initcodeword+    createCost = g_create + codeCost+    codeCost   = byteCost * (ceilDiv (unsafeInto size) 32)+    initGas    = allButOne64th (availableGas - createCost)++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++-- Gas cost of precompiles+costOfPrecompile :: FeeSchedule Word64 -> Addr -> Expr Buf -> Word64+costOfPrecompile (FeeSchedule {..}) precompileAddr input =+  let errorDynamicSize = internalError "precompile input cannot have a dynamic size"+      inputLen = case input of+                   ConcreteBuf bs -> unsafeInto $ BS.length bs+                   AbstractBuf _ -> errorDynamicSize+                   buf -> case bufLength buf of+                            Lit l -> unsafeInto l -- TODO: overflow+                            _ -> errorDynamicSize+  in case precompileAddr of+    -- ECRECOVER+    0x1 -> 3000+    -- SHA2-256+    0x2 -> (((inputLen + 31) `div` 32) * 12) + 60+    -- RIPEMD-160+    0x3 -> (((inputLen + 31) `div` 32) * 120) + 600+    -- IDENTITY+    0x4 -> (((inputLen + 31) `div` 32) * 3) + 15+    -- MODEXP+    0x5 -> case input of+             ConcreteBuf i -> concreteModexpGasFee i+             _ -> internalError "Unsupported symbolic modexp gas calc "+    -- ECADD+    0x6 -> g_ecadd+    -- ECMUL+    0x7 -> g_ecmul+    -- ECPAIRING+    0x8 -> (inputLen `div` 192) * g_pairing_point + g_pairing_base+    -- BLAKE2+    0x9 -> case input of+             ConcreteBuf i -> g_fround * (unsafeInto $ asInteger $ lazySlice 0 4 i)+             _ -> internalError "Unsupported symbolic blake2 gas calc"+    _ -> internalError $ "unimplemented precompiled contract " ++ show precompileAddr++-- Gas cost of memory expansion+memoryCost :: FeeSchedule Word64 -> Word64 -> Word64+memoryCost FeeSchedule{..} byteCount =+  let+    wordCount = ceilDiv byteCount 32+    linearCost = g_memory * wordCount+    quadraticCost = div (wordCount * wordCount) 512+  in+    linearCost + quadraticCost++hashcode :: ContractCode -> Expr EWord+hashcode (UnknownCode a) = CodeHash a+hashcode (InitCode ops args) = keccak $ (ConcreteBuf ops) <> args+hashcode (RuntimeCode (ConcreteRuntimeCode ops)) = keccak (ConcreteBuf ops)+hashcode (RuntimeCode (SymbolicRuntimeCode ops)) = keccak . Expr.fromList $ ops++-- | The length of the code ignoring any constructor args.+-- This represents the region that can contain executable opcodes+opslen :: ContractCode -> Int+opslen (UnknownCode _) = internalError "Cannot produce concrete opslen for unknown code"+opslen (InitCode ops _) = BS.length ops+opslen (RuntimeCode (ConcreteRuntimeCode ops)) = BS.length ops+opslen (RuntimeCode (SymbolicRuntimeCode ops)) = length ops++-- | The length of the code including any constructor args.+-- This can return an abstract value+codelen :: ContractCode -> Expr EWord+codelen (UnknownCode a) = CodeSize a+codelen c@(InitCode {}) = case toBuf c of+  Just b -> bufLength b+  Nothing -> internalError "impossible"+-- these are never going to be negative so unsafeInto is fine here+codelen (RuntimeCode (ConcreteRuntimeCode ops)) = Lit . unsafeInto $ BS.length ops+codelen (RuntimeCode (SymbolicRuntimeCode ops)) = Lit . unsafeInto $ length ops++toBuf :: ContractCode -> Maybe (Expr Buf)+toBuf (UnknownCode _) = Nothing+toBuf (InitCode ops args) = Just $ ConcreteBuf ops <> args+toBuf (RuntimeCode (ConcreteRuntimeCode ops)) = Just $ ConcreteBuf ops+toBuf (RuntimeCode (SymbolicRuntimeCode ops)) = Just $ Expr.fromList ops++codeloc :: EVM s CodeLocation+codeloc = do+  vm <- get+  pure (vm.state.contract, vm.state.pc)++createAddress :: Expr EAddr -> Maybe W64 -> EVM s (Expr EAddr)+createAddress (LitAddr a) (Just n) = pure $ Concrete.createAddress a n+createAddress (GVar _) _ = internalError "Unexpected GVar"+createAddress _ _ = freshSymAddr++create2Address :: Expr EAddr -> W256 -> ByteString -> EVM s (Expr EAddr)+create2Address (LitAddr a) s b = pure $ Concrete.create2Address a s b+create2Address (SymAddr _) _ _ = freshSymAddr+create2Address (GVar _) _ _ = internalError "Unexpected GVar"++freshSymAddr :: EVM s (Expr EAddr)+freshSymAddr = do+  modifying (#env % #freshAddresses) (+ 1)+  n <- use (#env % #freshAddresses)+  pure $ SymAddr ("freshSymAddr" <> (pack $ show n))++isPrecompileAddr :: Expr EAddr -> Bool+isPrecompileAddr = \case+  LitAddr a -> 0x0 < a && a <= 0x09+  SymAddr _ -> False+  GVar _ -> internalError "Unexpected GVar"++-- * Arithmetic++ceilDiv :: (Num a, Integral a) => a -> a -> a+ceilDiv m n = div (m + n - 1) n++allButOne64th :: (Num a, Integral a) => a -> a+allButOne64th n = n - div n 64++log2 :: FiniteBits b => b -> Int+log2 x = finiteBitSize x - 1 - countLeadingZeros x++writeMemory :: MutableMemory s -> Int -> ByteString -> EVM s ()+writeMemory memory offset buf = do+  memory' <- expandMemory (offset + BS.length buf)+  mapM_ (uncurry (VUnboxed.Mutable.write memory'))+        (zip [offset..] (BS.unpack buf))+  where+  expandMemory targetSize = do+    let toAlloc = targetSize - VUnboxed.Mutable.length memory+    if toAlloc > 0 then do+      memory' <- VUnboxed.Mutable.grow memory toAlloc+      assign (#state % #memory) (ConcreteMemory memory')+      pure memory'+    else+      pure memory++freezeMemory :: MutableMemory s -> EVM s (Expr Buf)+freezeMemory memory =+  ConcreteBuf . BS.pack . VUnboxed.toList <$> VUnboxed.freeze memory
src/EVM/ABI.hs view
@@ -37,6 +37,7 @@   , SolError (..)   , Anonymity (..)   , Indexed (..)+  , Sig(..)   , putAbi   , getAbi   , getAbiSeq@@ -82,13 +83,17 @@ import Data.Vector qualified as Vector import Data.Word (Word32) import GHC.Generics (Generic)- import Test.QuickCheck hiding ((.&.), label)+ import Text.Megaparsec qualified as P import Text.Megaparsec.Char qualified as P import Text.ParserCombinators.ReadP import Witch (unsafeInto, into) +-- | A method name, and the (ordered) types of it's arguments+data Sig = Sig Text [AbiType]+  deriving (Show, Eq)+ data AbiValue   = AbiUInt         Int Word256   | AbiInt          Int Int256@@ -410,80 +415,6 @@     <* skip ((roundTo32Bytes n) - n)   where n = fromIntegral i --- QuickCheck instances--genAbiValue :: AbiType -> Gen AbiValue-genAbiValue = \case-   AbiUIntType n -> AbiUInt n <$> genUInt n-   AbiIntType n -> do-     x <- genUInt n-     pure $ AbiInt n (signedWord (x - 2^(n-1)))-   AbiAddressType ->-     AbiAddress . fromIntegral <$> genUInt 20-   AbiBoolType ->-     elements [AbiBool False, AbiBool True]-   AbiBytesType n ->-     do xs <- replicateM n arbitrary-        pure (AbiBytes n (BS.pack xs))-   AbiBytesDynamicType ->-     AbiBytesDynamic . BS.pack <$> listOf arbitrary-   AbiStringType ->-     AbiString . BS.pack <$> listOf arbitrary-   AbiArrayDynamicType t ->-     do xs <- listOf1 (scale (`div` 2) (genAbiValue t))-        pure (AbiArrayDynamic t (Vector.fromList xs))-   AbiArrayType n t ->-     AbiArray n t . Vector.fromList <$>-       replicateM n (scale (`div` 2) (genAbiValue t))-   AbiTupleType ts ->-     AbiTuple <$> mapM genAbiValue ts-   AbiFunctionType ->-     do xs <- replicateM 24 arbitrary-        pure (AbiFunction (BS.pack xs))-  where-    genUInt :: Int -> Gen Word256-    genUInt n = arbitraryIntegralWithMax (2^n-1) :: Gen Word256--instance Arbitrary AbiType where-  arbitrary = oneof-    [ (AbiUIntType . (* 8)) <$> choose (1, 32)-    , (AbiIntType . (* 8)) <$> choose (1, 32)-    , pure AbiAddressType-    , pure AbiBoolType-    , AbiBytesType <$> choose (1,32)-    , pure AbiBytesDynamicType-    , pure AbiStringType-    , AbiArrayDynamicType <$> scale (`div` 2) arbitrary-    , AbiArrayType-        <$> (getPositive <$> arbitrary)-        <*> scale (`div` 2) arbitrary-    ]--instance Arbitrary AbiValue where-  arbitrary = arbitrary >>= genAbiValue-  shrink = \case-    AbiArrayDynamic t v ->-      Vector.toList v ++-        map (AbiArrayDynamic t . Vector.fromList)-            (shrinkList shrink (Vector.toList v))-    AbiBytesDynamic b -> AbiBytesDynamic . BS.pack <$> shrinkList shrinkIntegral (BS.unpack b)-    AbiString b -> AbiString . BS.pack <$> shrinkList shrinkIntegral (BS.unpack b)-    AbiBytes n a | n <= 32 -> shrink $ AbiUInt (n * 8) (word256 a)-    --bytesN for N > 32 don't really exist right now anyway..-    AbiBytes _ _ | otherwise -> []-    AbiArray _ t v ->-      Vector.toList v ++-        map (\x -> AbiArray (length x) t (Vector.fromList x))-            (shrinkList shrink (Vector.toList v))-    AbiTuple v -> Vector.toList $ AbiTuple . Vector.fromList . shrink <$> v-    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-- -- Bool synonym with custom read instance -- to be able to parse lower case 'false' and 'true' newtype Boolz = Boolz Bool@@ -557,7 +488,7 @@     then NoVals     else let       vs = decodeStaticArgs 0 (length tps) buf-      allLit = Prelude.and . (fmap isLitWord) $ vs+      allLit = Prelude.and (fmap isLitWord vs)       asBS = mconcat $ fmap word256Bytes (mapMaybe maybeLitWord vs)     in if not allLit        then SAbi vs@@ -573,6 +504,78 @@ decodeStaticArgs offset numArgs b =   [readWord (Lit . unsafeInto $ i) b | i <- [offset,(offset+32) .. (offset + (numArgs-1)*32)]] +-- QuickCheck instances++genAbiValue :: AbiType -> Gen AbiValue+genAbiValue = \case+   AbiUIntType n -> AbiUInt n <$> genUInt n+   AbiIntType n -> do+     x <- genUInt n+     pure $ AbiInt n (signedWord (x - 2^(n-1)))+   AbiAddressType ->+     AbiAddress . fromIntegral <$> genUInt 20+   AbiBoolType ->+     elements [AbiBool False, AbiBool True]+   AbiBytesType n ->+     do xs <- replicateM n arbitrary+        pure (AbiBytes n (BS.pack xs))+   AbiBytesDynamicType ->+     AbiBytesDynamic . BS.pack <$> listOf arbitrary+   AbiStringType ->+     AbiString . BS.pack <$> listOf arbitrary+   AbiArrayDynamicType t ->+     do xs <- listOf1 (scale (`div` 2) (genAbiValue t))+        pure (AbiArrayDynamic t (Vector.fromList xs))+   AbiArrayType n t ->+     AbiArray n t . Vector.fromList <$>+       replicateM n (scale (`div` 2) (genAbiValue t))+   AbiTupleType ts ->+     AbiTuple <$> mapM genAbiValue ts+   AbiFunctionType ->+     do xs <- replicateM 24 arbitrary+        pure (AbiFunction (BS.pack xs))+  where+    genUInt :: Int -> Gen Word256+    genUInt n = arbitraryIntegralWithMax (2^n-1) :: Gen Word256++instance Arbitrary AbiType where+  arbitrary = oneof+    [ (AbiUIntType . (* 8)) <$> choose (1, 32)+    , (AbiIntType . (* 8)) <$> choose (1, 32)+    , pure AbiAddressType+    , pure AbiBoolType+    , AbiBytesType <$> choose (1,32)+    , pure AbiBytesDynamicType+    , pure AbiStringType+    , AbiArrayDynamicType <$> scale (`div` 2) arbitrary+    , AbiArrayType+        <$> (getPositive <$> arbitrary)+        <*> scale (`div` 2) arbitrary+    ]++instance Arbitrary AbiValue where+  arbitrary = arbitrary >>= genAbiValue+  shrink = \case+    AbiArrayDynamic t v ->+      Vector.toList v +++        map (AbiArrayDynamic t . Vector.fromList)+            (shrinkList shrink (Vector.toList v))+    AbiBytesDynamic b -> AbiBytesDynamic . BS.pack <$> shrinkList shrinkIntegral (BS.unpack b)+    AbiString b -> AbiString . BS.pack <$> shrinkList shrinkIntegral (BS.unpack b)+    AbiBytes n a | n <= 32 -> shrink $ AbiUInt (n * 8) (word256 a)+    --bytesN for N > 32 don't really exist right now anyway..+    AbiBytes _ _ | otherwise -> []+    AbiArray _ t v ->+      Vector.toList v +++        map (\x -> AbiArray (length x) t (Vector.fromList x))+            (shrinkList shrink (Vector.toList v))+    AbiTuple v -> Vector.toList $ AbiTuple . Vector.fromList . shrink <$> v+    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  -- A modification of 'arbitrarySizedBoundedIntegral' quickcheck library -- which takes the maxbound explicitly rather than relying on a Bounded instance.
src/EVM/Concrete.hs view
@@ -1,34 +1,23 @@+{-# Language DataKinds #-}+ module EVM.Concrete where +import Prelude hiding (Word) import EVM.RLP import EVM.Types -import Data.Bits (Bits(..), shiftR)-import Data.ByteString (ByteString, (!?))+import Data.ByteString (ByteString) import Data.ByteString qualified as BS-import Data.Maybe (fromMaybe)-import Data.Word (Word8)-import Witch (unsafeInto)--wordAt :: Int -> ByteString -> W256-wordAt i bs =-  word (padRight 32 (BS.drop i bs))--readByteOrZero :: Int -> ByteString -> Word8-readByteOrZero i bs = fromMaybe 0 (bs !? i)+import Witch (unsafeInto, into)  byteStringSliceWithDefaultZeroes :: Int -> Int -> ByteString -> ByteString byteStringSliceWithDefaultZeroes offset size bs =   if size == 0   then ""-  -- else if offset > BS.length bs-  -- then BS.replicate size 0-  -- todo: this ^^ should work, investigate why it causes more GST fails   else     let bs' = BS.take size (BS.drop offset bs)     in bs' <> BS.replicate (size - BS.length bs') 0 - sliceMemory :: W256 -> W256 -> ByteString -> ByteString sliceMemory o s =   byteStringSliceWithDefaultZeroes (unsafeInto o) (unsafeInto s)@@ -49,24 +38,9 @@   in     a <> a' <> c <> b' --- Copied from the standard library just to get specialization.--- We also use bit operations instead of modulo and multiply.--- (This operation was significantly slow.)-(^) :: W256 -> W256 -> W256-x0 ^ y0 | y0 < 0    = errorWithoutStackTrace "Negative exponent"-        | y0 == 0   = 1-        | otherwise = f x0 y0-    where-          f x y | not (testBit y 0) = f (x * x) (y `shiftR` 1)-                | y == 1      = x-                | otherwise   = g (x * x) ((y - 1) `shiftR` 1) x-          g x y z | not (testBit y 0) = g (x * x) (y `shiftR` 1) z-                  | y == 1      = x * z-                  | otherwise   = g (x * x) ((y - 1) `shiftR` 1) (x * z)--createAddress :: Addr -> W256 -> Addr-createAddress a n = unsafeInto $ keccak' $ rlpList [rlpAddrFull a, rlpWord256 n]+createAddress :: Addr -> W64 -> Expr EAddr+createAddress a n = LitAddr . unsafeInto . keccak' . rlpList $ [rlpAddrFull a, rlpWord256 (into n)] -create2Address :: Addr -> W256 -> ByteString -> Addr-create2Address a s b = unsafeInto $ keccak' $ mconcat+create2Address :: Addr -> W256 -> ByteString -> Expr EAddr+create2Address a s b = LitAddr $ unsafeInto $ keccak' $ mconcat   [BS.singleton 0xff, word160Bytes a, word256Bytes s, word256Bytes $ keccak' b]
src/EVM/Dapp.hs view
@@ -1,24 +1,25 @@+{-# Language DataKinds #-}+ module EVM.Dapp where  import EVM.ABI import EVM.Concrete-import EVM.Debug (srcMapCodePos) import EVM.Solidity import EVM.Types -import Control.Arrow ((>>>))+import Control.Arrow ((>>>), second) import Data.Aeson (Value)-import Data.Bifunctor (first) import Data.ByteString (ByteString) import Data.ByteString qualified as BS import Data.List (find, sort) import Data.Map (Map) import Data.Map qualified as Map-import Data.Maybe (isJust, fromJust, mapMaybe)+import Data.Maybe (mapMaybe) import Data.Sequence qualified as Seq-import Data.Text (Text, isPrefixOf, pack, unpack)+import Data.Text (Text, isPrefixOf, pack) import Data.Text.Encoding (encodeUtf8) import Data.Vector qualified as V+import Optics.Core import Witch (unsafeInto)  data DappInfo = DappInfo@@ -27,7 +28,7 @@   , solcByHash :: Map W256 (CodeType, SolcContract)   , solcByCode :: [(Code, SolcContract)] -- for contracts with `immutable` vars.   , sources    :: SourceCache-  , unitTests  :: [(Text, [(Test, [AbiType])])]+  , unitTests  :: [(Text, [Sig])]   , abiMap     :: Map FunctionSelector Method   , eventMap   :: Map W256 Event   , errorMap   :: Map W256 SolError@@ -44,14 +45,9 @@  data DappContext = DappContext   { info :: DappInfo-  , env  :: Map Addr Contract+  , env  :: Map (Expr EAddr) Contract   } -data Test = ConcreteTest Text | SymbolicTest Text | InvariantTest Text--instance Show Test where-  show t = unpack $ extractSig t- dappInfo :: FilePath -> BuildOutput -> DappInfo dappInfo root (BuildOutput (Contracts cs) sources) =   let@@ -87,29 +83,28 @@ emptyDapp = dappInfo "" mempty  -- Dapp unit tests are detected by searching within abi methods--- that begin with "test" or "prove", that are in a contract with+-- 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()".------ Tests beginning with "test" are interpreted as concrete tests, whereas--- tests beginning with "prove" are interpreted as symbolic tests.  unitTestMarkerAbi :: FunctionSelector unitTestMarkerAbi = abiKeccak (encodeUtf8 "IS_TEST()") -findAllUnitTests :: [SolcContract] -> [(Text, [(Test, [AbiType])])]-findAllUnitTests = findUnitTests ".*:.*\\.(test|prove|invariant).*"+findAllUnitTests :: [SolcContract] -> [(Text, [Sig])]+findAllUnitTests = findUnitTests ".*:.*\\.(check|prove).*" -mkTest :: Text -> Maybe Test-mkTest sig-  | "test" `isPrefixOf` sig = Just (ConcreteTest sig)-  | "prove" `isPrefixOf` sig = Just (SymbolicTest sig)-  | "invariant" `isPrefixOf` sig = Just (InvariantTest sig)+mkSig :: Method -> Maybe Sig+mkSig method+  | "prove" `isPrefixOf` testname = Just (Sig testname argtypes)+  | "check" `isPrefixOf` testname = Just (Sig testname argtypes)   | otherwise = Nothing+  where+    testname = method.methodSignature+    argtypes = snd <$> method.inputs -findUnitTests :: Text -> ([SolcContract] -> [(Text, [(Test, [AbiType])])])+findUnitTests :: Text -> ([SolcContract] -> [(Text, [Sig])]) findUnitTests match =   concatMap $ \c ->     case Map.lookup unitTestMarkerAbi c.abiMap of@@ -118,25 +113,16 @@         let testNames = unitTestMethodsFiltered (regexMatches match) c         in [(c.contractName, testNames) | not (BS.null c.runtimeCode) && not (null testNames)] -unitTestMethodsFiltered :: (Text -> Bool) -> (SolcContract -> [(Test, [AbiType])])+unitTestMethodsFiltered :: (Text -> Bool) -> (SolcContract -> [Sig]) unitTestMethodsFiltered matcher c =-  let-    testName method = c.contractName <> "." <> (extractSig (fst method))-  in-    filter (matcher . testName) (unitTestMethods c)+  let testName (Sig n _) = c.contractName <> "." <> n+  in filter (matcher . testName) (unitTestMethods c) -unitTestMethods :: SolcContract -> [(Test, [AbiType])]+unitTestMethods :: SolcContract -> [Sig] unitTestMethods =   (.abiMap)   >>> Map.elems-  >>> map (\f -> (mkTest f.methodSignature, snd <$> f.inputs))-  >>> filter (isJust . fst)-  >>> fmap (first fromJust)--extractSig :: Test -> Text-extractSig (ConcreteTest sig) = sig-extractSig (SymbolicTest sig) = sig-extractSig (InvariantTest sig) = sig+  >>> mapMaybe mkSig  traceSrcMap :: DappInfo -> Trace -> Maybe SrcMap traceSrcMap dapp trace = srcMap dapp trace.contract trace.opIx@@ -144,10 +130,11 @@ srcMap :: DappInfo -> Contract -> Int -> Maybe SrcMap srcMap dapp contr opIndex = do   sol <- findSrc contr dapp-  case contr.contractcode of-    (InitCode _ _) ->-      Seq.lookup opIndex sol.creationSrcmap-    (RuntimeCode _) ->+  case contr.code of+    UnknownCode _ -> Nothing+    InitCode _ _ ->+     Seq.lookup opIndex sol.creationSrcmap+    RuntimeCode _ ->       Seq.lookup opIndex sol.runtimeSrcmap  findSrc :: Contract -> DappInfo -> Maybe SolcContract@@ -155,10 +142,11 @@   hash <- maybeLitWord c.codehash   case Map.lookup hash dapp.solcByHash of     Just (_, v) -> Just v-    Nothing -> lookupCode c.contractcode dapp+    Nothing -> lookupCode c.code dapp   lookupCode :: ContractCode -> DappInfo -> Maybe SolcContract+lookupCode (UnknownCode _) _ = Nothing lookupCode (InitCode c _) a =   snd <$> Map.lookup (keccak' (stripBytecodeMetadata c)) a.solcByHash lookupCode (RuntimeCode (ConcreteRuntimeCode c)) a =@@ -174,7 +162,7 @@ compareCode :: ByteString -> Code -> Bool compareCode raw (Code template locs) =   let holes' = sort [(start, len) | (Reference start len) <- locs]-      insert at' len' bs = writeMemory (BS.replicate len' 0) (unsafeInto len') 0 (unsafeInto at') bs+      insert loc len' bs = writeMemory (BS.replicate len' 0) (unsafeInto len') 0 (unsafeInto loc) bs       refined = foldr (\(start, len) acc -> insert start len acc) raw holes'   in BS.length raw == BS.length template && template == refined @@ -187,3 +175,15 @@         Nothing -> Left "<source not found>"         Just (fileName, lineIx) ->           Right (pack fileName <> ":" <> pack (show lineIx))++srcMapCodePos :: SourceCache -> SrcMap -> Maybe (FilePath, Int)+srcMapCodePos cache sm =+  fmap (second f) $ cache.files ^? ix sm.file+  where+    f v = BS.count 0xa (BS.take sm.offset v) + 1++srcMapCode :: SourceCache -> SrcMap -> Maybe ByteString+srcMapCode cache sm =+  fmap f $ cache.files ^? ix sm.file+  where+    f (_, v) = BS.take (min 80 sm.length) (BS.drop sm.offset v)
− src/EVM/Debug.hs
@@ -1,52 +0,0 @@-module EVM.Debug where--import EVM (bytecode)-import EVM.Expr (bufLength)-import EVM.Solidity (SrcMap(..), SourceCache(..))-import EVM.Types (Contract(..), Addr)--import Control.Arrow (second)-import Data.ByteString (ByteString)-import Data.ByteString qualified as ByteString-import Data.Map (Map)-import Data.Map qualified as Map-import Optics.Core-import Text.PrettyPrint.ANSI.Leijen-import Witch (unsafeInto)--data Mode = Debug | Run | JsonTrace deriving (Eq, Show)--object :: [(Doc, Doc)] -> Doc-object xs =-  group $ lbrace-    <> line-    <> indent 2 (sep (punctuate (char ';') [k <+> equals <+> v | (k, v) <- xs]))-    <> line-    <> rbrace--prettyContract :: Contract -> Doc-prettyContract c =-  object-    [ (text "codesize", text . show $ (bufLength (c ^. bytecode)))-    , (text "codehash", text (show c.codehash))-    , (text "balance", int (unsafeInto c.balance))-    , (text "nonce", int (unsafeInto c.nonce))-    ]--prettyContracts :: Map Addr Contract -> Doc-prettyContracts x =-  object-    (map (\(a, b) -> (text (show a), prettyContract b))-     (Map.toList x))--srcMapCodePos :: SourceCache -> SrcMap -> Maybe (FilePath, Int)-srcMapCodePos cache sm =-  fmap (second f) $ cache.files ^? ix sm.file-  where-    f v = ByteString.count 0xa (ByteString.take sm.offset v) + 1--srcMapCode :: SourceCache -> SrcMap -> Maybe ByteString-srcMapCode cache sm =-  fmap f $ cache.files ^? ix sm.file-  where-    f (_, v) = ByteString.take (min 80 sm.length) (ByteString.drop sm.offset v)
− src/EVM/Dev.hs
@@ -1,498 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE QuasiQuotes #-}--{-|-Module: EVM.Dev-Description: Helpers for repl driven hevm hacking--}-module EVM.Dev where--import Control.Monad.State.Strict hiding (state)-import Data.ByteString hiding (writeFile, zip)-import Data.Maybe (fromJust)-import Data.String.Here-import Data.Text.IO qualified as T-import Data.Text.Lazy.IO qualified as TL-import Data.Typeable (Typeable)-import System.Directory (withCurrentDirectory)-import System.Exit (exitFailure)--import EVM-import EVM.Dapp (dappInfo)-import EVM.Expr (numBranches, simplify)-import EVM.Fetch qualified as Fetch-import EVM.FeeSchedule qualified as FeeSchedule-import EVM.Format (formatExpr)-import EVM.SMT-import EVM.Solvers-import EVM.Solidity-import EVM.SymExec-import EVM.Types-import EVM.UnitTest-import GHC.Conc-import Witch (unsafeInto)--checkEquiv :: (Typeable a) => Expr a -> Expr a -> IO ()-checkEquiv a b = withSolvers Z3 1 Nothing $ \s -> do-  let smt = assertProps [a ./= b]-  res <- checkSat s smt-  print res--runDappTest :: FilePath -> IO ()-runDappTest root =-  withCurrentDirectory root $ do-    cores <- unsafeInto <$> getNumProcessors-    let testFile = root <> "/out/dapp.sol.json"-    Right (BuildOutput contracts _) <- readSolc DappTools root testFile-    withSolvers Z3 cores Nothing $ \solvers -> do-      opts <- testOpts solvers root testFile-      res <- unitTest opts contracts Nothing-      unless res exitFailure--testOpts :: SolverGroup -> FilePath -> FilePath -> IO UnitTestOptions-testOpts solvers root testFile = do-  srcInfo <- readSolc DappTools root testFile >>= \case-    Left e -> internalError e-    Right out ->-      pure $ dappInfo root out--  params <- getParametersFromEnvironmentVariables Nothing-  pure EVM.UnitTest.UnitTestOptions-    { EVM.UnitTest.solvers = solvers-    , EVM.UnitTest.rpcInfo = Nothing-    , EVM.UnitTest.maxIter = Nothing-    , EVM.UnitTest.askSmtIters = 1-    , EVM.UnitTest.smtTimeout = Nothing-    , EVM.UnitTest.smtDebug = False-    , EVM.UnitTest.solver = Nothing-    , EVM.UnitTest.covMatch = Nothing-    , EVM.UnitTest.verbose = Nothing-    , EVM.UnitTest.match = ".*"-    , EVM.UnitTest.maxDepth = Nothing-    , EVM.UnitTest.fuzzRuns = 100-    , EVM.UnitTest.replay = Nothing-    , EVM.UnitTest.vmModifier = id-    , EVM.UnitTest.testParams = params-    , EVM.UnitTest.dapp = srcInfo-    , EVM.UnitTest.ffiAllowed = True-    }--doTest :: IO ()-doTest = do-  c <- testContract-  reachable' False c-  --e <- simplify <$> buildExpr c-  --Prelude.putStrLn (formatExpr e)--analyzeDai :: IO ()-analyzeDai = do-  d <- dai-  reachable' False d--daiExpr :: IO (Expr End)-daiExpr = do-  d <- dai-  withSolvers Z3 1 Nothing $ \s -> buildExpr s d--analyzeVat :: IO ()-analyzeVat = do-  putStrLn "starting"-  v <- vat-  withSolvers Z3 1 Nothing $ \s -> do-    e <- buildExpr s v-    putStrLn $ "done (" <> show (numBranches e) <> " branches)"-    reachable' False v--analyzeDeposit :: IO ()-analyzeDeposit = 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);-      }-     }-    |]-  withSolvers Z3 1 Nothing $ \s -> do-    putStrLn "Exploring Contract"-    e <- simplify <$> buildExpr s c-    putStrLn "Writing AST"-    T.writeFile "full.ast" (formatExpr e)---reachable' :: Bool -> ByteString -> IO ()-reachable' smtdebug c = do-  putStrLn "Exploring contract"-  withSolvers Z3 4 Nothing $ \s -> do-    full <- simplify <$> buildExpr s c-    putStrLn $ "Explored contract (" <> (show $ numBranches full) <> " branches)"-    --putStrLn $ formatExpr full-    T.writeFile "full.ast" $ formatExpr full-    putStrLn "Dumped to full.ast"-    putStrLn "Checking reachability"-    (qs, less) <- reachable s full-    putStrLn $ "Checked reachability (" <> (show $ numBranches less) <> " reachable branches)"-    T.writeFile "reachable.ast" $ formatExpr less-    putStrLn "Dumped to reachable.ast"-    --putStrLn $ formatExpr less-    when smtdebug $ do-      putStrLn "\n\nQueries\n\n"-      forM_ qs $ \q -> do-        putStrLn "\n\n-- Query --"-        TL.putStrLn $ formatSMT2 q---showExpr :: ByteString -> IO ()-showExpr c = do-  withSolvers Z3 1 Nothing $ \s -> do-    e <- buildExpr s c-    T.putStrLn $ formatExpr (simplify e)--summaryStore :: IO ByteString-summaryStore = do-  let src =-        [i|-          contract A {-            uint x;-            function f(uint256 y) public {-               unchecked {-                 x += y;-                 x += y;-               }-            }-          }-        |]-  fmap fromJust (solcRuntime "A" src)--safeAdd :: IO ByteString-safeAdd = do-  let src =-        [i|-          contract SafeAdd {-            function add(uint x, uint y) public pure returns (uint z) {-                 require((z = x + y) >= x);-            }-          }-        |]-  fmap fromJust (solcRuntime "SafeAdd" src)----testContract :: IO ByteString-testContract = do-  let src =-        [i|-          contract C {-            uint x;-            function set(uint v) public {-              x = v + v;-            }-          }-          |]-  fmap fromJust (solcRuntime "C" src)--vat :: IO ByteString-vat = do-  let src =-        [i|-          /// vat.sol -- Dai CDP database--          // Copyright (C) 2018 Rain <rainbreak@riseup.net>-          //-          // This program is free software: you can redistribute it and/or modify-          // it under the terms of the GNU Affero General Public License as published by-          // the Free Software Foundation, either version 3 of the License, or-          // (at your option) any later version.-          //-          // This program is distributed in the hope that it will be useful,-          // but WITHOUT ANY WARRANTY; without even the implied warranty of-          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-          // GNU Affero General Public License for more details.-          //-          // You should have received a copy of the GNU Affero General Public License-          // along with this program.  If not, see <https://www.gnu.org/licenses/>.--          // FIXME: This contract was altered compared to the production version.-          // It doesn't use LibNote anymore.-          // New deployments of this contract will need to include custom events (TO DO).--          contract Vat {-              // --- Auth ----              mapping (address => uint) public wards;-              function rely(address usr) external auth { require(live == 1, "Vat/not-live"); wards[usr] = 1; }-              function deny(address usr) external auth { require(live == 1, "Vat/not-live"); wards[usr] = 0; }-              modifier auth {-                  require(wards[msg.sender] == 1, "Vat/not-authorized");-                  _;-              }--              mapping(address => mapping (address => uint)) public can;-              function hope(address usr) external { can[msg.sender][usr] = 1; }-              function nope(address usr) external { can[msg.sender][usr] = 0; }-              function wish(address bit, address usr) internal view returns (bool) {-                  return either(bit == usr, can[bit][usr] == 1);-              }--              // --- Data ----              struct Ilk {-                  uint256 Art;   // Total Normalised Debt     [wad]-                  uint256 rate;  // Accumulated Rates         [ray]-                  uint256 spot;  // Price with Safety Margin  [ray]-                  uint256 line;  // Debt Ceiling              [rad]-                  uint256 dust;  // Urn Debt Floor            [rad]-              }-              struct Urn {-                  uint256 ink;   // Locked Collateral  [wad]-                  uint256 art;   // Normalised Debt    [wad]-              }--              mapping (bytes32 => Ilk)                       public ilks;-              mapping (bytes32 => mapping (address => Urn )) public urns;-              mapping (bytes32 => mapping (address => uint)) public gem;  // [wad]-              mapping (address => uint256)                   public dai;  // [rad]-              mapping (address => uint256)                   public sin;  // [rad]--              uint256 public debt;  // Total Dai Issued    [rad]-              uint256 public vice;  // Total Unbacked Dai  [rad]-              uint256 public Line;  // Total Debt Ceiling  [rad]-              uint256 public live;  // Active Flag--              // --- Init ----              constructor() public {-                  wards[msg.sender] = 1;-                  live = 1;-              }--              // --- Math ----              function _add(uint x, int y) internal pure returns (uint z) {-                  z = x + uint(y);-                  require(y >= 0 || z <= x);-                  require(y <= 0 || z >= x);-              }-              function _sub(uint x, int y) internal pure returns (uint z) {-                  z = x - uint(y);-                  require(y <= 0 || z <= x);-                  require(y >= 0 || z >= x);-              }-              function _mul(uint x, int y) internal pure returns (int z) {-                  z = int(x) * y;-                  require(int(x) >= 0);-                  require(y == 0 || z / y == int(x));-              }-              function _add(uint x, uint y) internal pure returns (uint z) {-                  require((z = x + y) >= x);-              }-              function _sub(uint x, uint y) internal pure returns (uint z) {-                  require((z = x - y) <= x);-              }-              function _mul(uint x, uint y) internal pure returns (uint z) {-                  require(y == 0 || (z = x * y) / y == x);-              }--              // --- Administration ----              function init(bytes32 ilk) external auth {-                  require(ilks[ilk].rate == 0, "Vat/ilk-already-init");-                  ilks[ilk].rate = 10 ** 27;-              }-              function file(bytes32 what, uint data) external auth {-                  require(live == 1, "Vat/not-live");-                  if (what == "Line") Line = data;-                  else revert("Vat/file-unrecognized-param");-              }-              function file(bytes32 ilk, bytes32 what, uint data) external auth {-                  require(live == 1, "Vat/not-live");-                  if (what == "spot") ilks[ilk].spot = data;-                  else if (what == "line") ilks[ilk].line = data;-                  else if (what == "dust") ilks[ilk].dust = data;-                  else revert("Vat/file-unrecognized-param");-              }-              function cage() external auth {-                  live = 0;-              }--              // --- Fungibility ----              function slip(bytes32 ilk, address usr, int256 wad) external auth {-                  gem[ilk][usr] = _add(gem[ilk][usr], wad);-              }-              function flux(bytes32 ilk, address src, address dst, uint256 wad) external {-                  require(wish(src, msg.sender), "Vat/not-allowed");-                  gem[ilk][src] = _sub(gem[ilk][src], wad);-                  gem[ilk][dst] = _add(gem[ilk][dst], wad);-              }-              function move(address src, address dst, uint256 rad) external {-                  require(wish(src, msg.sender), "Vat/not-allowed");-                  dai[src] = _sub(dai[src], rad);-                  dai[dst] = _add(dai[dst], rad);-              }--              function either(bool x, bool y) internal pure returns (bool z) {-                  assembly{ z := or(x, y)}-              }-              function both(bool x, bool y) internal pure returns (bool z) {-                  assembly{ z := and(x, y)}-              }--              // --- CDP Confiscation ----              function grab(bytes32 i, address u, address v, address w, int dink, int dart) external auth {-                  Urn storage urn = urns[i][u];-                  Ilk storage ilk = ilks[i];--                  urn.ink = _add(urn.ink, dink);-                  urn.art = _add(urn.art, dart);-                  ilk.Art = _add(ilk.Art, dart);--                  int dtab = _mul(ilk.rate, dart);--                  gem[i][v] = _sub(gem[i][v], dink);-                  sin[w]    = _sub(sin[w],    dtab);-                  vice      = _sub(vice,      dtab);-              }-          }-          |]-  fmap fromJust (solcRuntime "Vat" src)--initVm :: ByteString -> VM-initVm bs = vm-  where-    contractCode = RuntimeCode (ConcreteRuntimeCode bs)-    c = Contract-      { contractcode = contractCode-      , balance      = 0-      , nonce        = 0-      , codehash     = keccak (ConcreteBuf bs)-      , opIxMap      = mkOpIxMap contractCode-      , codeOps      = mkCodeOps contractCode-      , external     = False-      }-    vm = makeVm $ VMOpts-      { contract       = c-      , calldata       = (AbstractBuf "txdata", [])-      , value          = CallValue 0-      , address        = Addr 0xffffffffffffffff-      , caller         = Lit 0-      , origin         = Addr 0xffffffffffffffff-      , gas            = 0xffffffffffffffff-      , gaslimit       = 0xffffffffffffffff-      , initialStorage = AbstractStore-      , baseFee        = 0-      , priorityFee    = 0-      , coinbase       = 0-      , number         = 0-      , timestamp      = Var "timestamp"-      , blockGaslimit  = 0-      , gasprice       = 0-      , maxCodeSize    = 0xffffffff-      , prevRandao     = 420-      , schedule       = FeeSchedule.berlin-      , chainId        = 1-      , create         = False-      , txAccessList   = mempty-      , allowFFI       = False-      }----- | Builds the Expr for the given evm bytecode object-buildExpr :: SolverGroup -> ByteString -> IO (Expr End)-buildExpr solvers bs = interpret (Fetch.oracle solvers Nothing) Nothing 1 Naive (initVm bs) runExpr--dai :: IO ByteString-dai = do-  let src =-        [i|-        contract Dai {-            // --- Auth ----            mapping (address => uint) public wards;-            function rely(address guy) external auth { wards[guy] = 1; }-            function deny(address guy) external auth { wards[guy] = 0; }-            modifier auth {-                require(wards[msg.sender] == 1, "Dai/not-authorized");-                _;-            }--            // --- ERC20 Data ----            string  public constant name     = "Dai Stablecoin";-            string  public constant symbol   = "DAI";-            string  public constant version  = "1";-            uint8   public constant decimals = 18;-            uint256 public totalSupply;--            mapping (address => uint)                      public balanceOf;-            mapping (address => mapping (address => uint)) public allowance;--            event Approval(address indexed src, address indexed guy, uint wad);-            event Transfer(address indexed src, address indexed dst, uint wad);--            // --- Math ----            function add(uint x, uint y) internal pure returns (uint z) {-                require((z = x + y) >= x);-            }-            function sub(uint x, uint y) internal pure returns (uint z) {-                require((z = x - y) <= x);-            }--            // --- EIP712 niceties ----            constructor() public {-                wards[msg.sender] = 1;-            }--            // --- Token ----            function transfer(address dst, uint wad) external returns (bool) {-                return transferFrom(msg.sender, dst, wad);-            }-            function transferFrom(address src, address dst, uint wad)-                public returns (bool)-            {-                require(balanceOf[src] >= wad, "Dai/insufficient-balance");-                if (src != msg.sender && allowance[src][msg.sender] != type(uint).max) {-                    require(allowance[src][msg.sender] >= wad, "Dai/insufficient-allowance");-                    allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad);-                }-                balanceOf[src] = sub(balanceOf[src], wad);-                balanceOf[dst] = add(balanceOf[dst], wad);-                emit Transfer(src, dst, wad);-                return true;-            }-            function mint(address usr, uint wad) external auth {-                balanceOf[usr] = add(balanceOf[usr], wad);-                totalSupply    = add(totalSupply, wad);-                emit Transfer(address(0), usr, wad);-            }-            function burn(address usr, uint wad) external {-                require(balanceOf[usr] >= wad, "Dai/insufficient-balance");-                if (usr != msg.sender && allowance[usr][msg.sender] != type(uint).max) {-                    require(allowance[usr][msg.sender] >= wad, "Dai/insufficient-allowance");-                    allowance[usr][msg.sender] = sub(allowance[usr][msg.sender], wad);-                }-                balanceOf[usr] = sub(balanceOf[usr], wad);-                totalSupply    = sub(totalSupply, wad);-                emit Transfer(usr, address(0), wad);-            }-            function approve(address usr, uint wad) external returns (bool) {-                allowance[msg.sender][usr] = wad;-                emit Approval(msg.sender, usr, wad);-                return true;-            }--            // --- Alias ----            function push(address usr, uint wad) external {-                transferFrom(msg.sender, usr, wad);-            }-            function pull(address usr, uint wad) external {-                transferFrom(usr, msg.sender, wad);-            }-            function move(address src, address dst, uint wad) external {-                transferFrom(src, dst, wad);-            }-        }-        |]-  fmap fromJust (solcRuntime "Dai" src)
src/EVM/Exec.hs view
@@ -1,32 +1,33 @@ module EVM.Exec where -import EVM+import EVM hiding (createAddress) import EVM.Concrete (createAddress)-import EVM.FeeSchedule qualified as FeeSchedule-import EVM.Expr (litAddr)+import EVM.FeeSchedule (feeSchedule) import EVM.Types  import Control.Monad.Trans.State.Strict (get, State) import Data.ByteString (ByteString) import Data.Maybe (isNothing) import Optics.Core+import Control.Monad.ST (ST)  ethrunAddress :: Addr ethrunAddress = Addr 0x00a329c0648769a73afac7f9381e08fb43dbea72 -vmForEthrunCreation :: ByteString -> VM+vmForEthrunCreation :: ByteString -> ST s (VM s) vmForEthrunCreation creationCode =   (makeVm $ VMOpts     { contract = initialContract (InitCode creationCode mempty)+    , otherContracts = []     , calldata = mempty-    , value = (Lit 0)-    , initialStorage = EmptyStore+    , value = Lit 0+    , baseState = EmptyBase     , address = createAddress ethrunAddress 1-    , caller = litAddr ethrunAddress-    , origin = ethrunAddress-    , coinbase = 0+    , caller = LitAddr ethrunAddress+    , origin = LitAddr ethrunAddress+    , coinbase = LitAddr 0     , number = 0-    , timestamp = (Lit 0)+    , timestamp = Lit 0     , blockGaslimit = 0     , gasprice = 0     , prevRandao = 42069@@ -35,29 +36,29 @@     , baseFee = 0     , priorityFee = 0     , maxCodeSize = 0xffffffff-    , schedule = FeeSchedule.berlin+    , schedule = feeSchedule     , chainId = 1     , create = False     , txAccessList = mempty     , allowFFI = False-    }) & set (#env % #contracts % at ethrunAddress)+    }) <&> set (#env % #contracts % at (LitAddr ethrunAddress))              (Just (initialContract (RuntimeCode (ConcreteRuntimeCode "")))) -exec :: State VM VMResult+exec :: EVM s (VMResult s) exec = do   vm <- get   case vm.result of     Nothing -> exec1 >> exec     Just r -> pure r -run :: State VM VM+run :: EVM s (VM s) run = do   vm <- get   case vm.result of     Nothing -> exec1 >> run     Just _ -> pure vm -execWhile :: (VM -> Bool) -> State VM Int+execWhile :: (VM s -> Bool) -> State (VM s) Int execWhile p = go 0   where     go i = do
src/EVM/Expr.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE PatternSynonyms #-}  {-|    Helper functions for working with Expr instances.@@ -7,26 +8,36 @@ module EVM.Expr where  import Prelude hiding (LT, GT)+import Control.Monad.ST import Data.Bits hiding (And, Xor) import Data.ByteString (ByteString) import Data.ByteString qualified as BS import Data.DoubleWord (Int256, Word256(Word256), Word128(Word128)) import Data.List import Data.Map.Strict qualified as Map-import Data.Maybe (mapMaybe)+import Data.Maybe (mapMaybe, isJust, fromMaybe) import Data.Semigroup (Any, Any(..), getAny) import Data.Vector qualified as V+import Data.Vector (Vector)+import Data.Vector.Mutable qualified as MV+import Data.Vector.Mutable (MVector) import Data.Vector.Storable qualified as VS import Data.Vector.Storable.ByteString import Data.Word (Word8, Word32) import Witch (unsafeInto, into, tryFrom)+import Data.Containers.ListUtils (nubOrd)+import Control.Monad.State  import Optics.Core  import EVM.Traversals import EVM.Types +-- ** Constants ** +maxLit :: W256+maxLit = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff+ -- ** Stack Ops ** ---------------------------------------------------------------------------------  @@ -335,7 +346,7 @@ -- concrete indicies & 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 = 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@@ -362,11 +373,14 @@   writeWord :: Expr EWord -> Expr EWord -> Expr Buf -> Expr Buf+writeWord o@(Lit offset) (WAddr (LitAddr val)) b@(ConcreteBuf _)+  | offset < maxBytes && offset + 32 < maxBytes+  = writeWord o (Lit $ into val) b writeWord (Lit offset) (Lit val) (ConcreteBuf "")-  | offset + 32 < maxBytes+  | offset < maxBytes && offset + 32 < maxBytes   = ConcreteBuf $ BS.replicate (unsafeInto offset) 0 <> word256Bytes val writeWord o@(Lit offset) v@(Lit val) buf@(ConcreteBuf src)-  | offset + 32 < maxBytes+  | offset < maxBytes && offset + 32 < maxBytes     = ConcreteBuf $ (padRight (unsafeInto offset) $ BS.take (unsafeInto offset) src)                  <> word256Bytes val                  <> BS.drop ((unsafeInto offset) + 32) src@@ -418,31 +432,6 @@         Nothing -> internalError "cannot compute length of open expression"     go l (GVar (BufVar a)) = EVM.Expr.max l (BufLength (GVar (BufVar a))) --- | If a buffer has a concrete prefix, we return it's length here-concPrefix :: Expr Buf -> Maybe Integer-concPrefix (CopySlice (Lit srcOff) (Lit _) (Lit _) src (ConcreteBuf "")) = do-  sz <- go 0 src-  pure . into $ (unsafeInto sz) - srcOff-  where-    go :: W256 -> Expr Buf -> Maybe Integer-    -- base cases-    go _ (AbstractBuf _) = Nothing-    go l (ConcreteBuf b) = Just . into $ Prelude.max (unsafeInto . BS.length $ b) l--    -- writes to a concrete index-    go l (WriteWord (Lit idx) (Lit _) b) = go (Prelude.max l (idx + 32)) b-    go l (WriteByte (Lit idx) (LitByte _) b) = go (Prelude.max l (idx + 1)) b-    go l (CopySlice _ (Lit dstOffset) (Lit size) _ dst) = go (Prelude.max (dstOffset + size) l) dst--    -- writes to an abstract index are ignored-    go l (WriteWord _ _ b) = go l b-    go l (WriteByte _ _ b) = go l b-    go _ (CopySlice _ _ _ _ _) = internalError "cannot compute a concrete prefix length for nested copySlice expressions"-    go _ (GVar _) = internalError "cannot calculate a concrete prefix of an open expression"-concPrefix (ConcreteBuf b) = Just (into . BS.length $ b)-concPrefix e = internalError $ "cannot compute a concrete prefix length for: " <> show e-- -- | Return the minimum possible length of a buffer. In the case of an -- abstract buffer, it is the largest write that is made on a concrete -- location. Parameterized by an environment for buffer variables.@@ -465,6 +454,51 @@       b <- Map.lookup a bufEnv       go l b +-- returns the largest prefix that is guaranteed to be concrete (if one exists)+-- partial: will hard error if we encounter an input buf with a concrete size > 500mb+-- partial: will hard error if the prefix is > 500mb+concretePrefix :: Expr Buf -> Vector Word8+concretePrefix b = V.create $ do+    v <- MV.new (fromMaybe 1024 inputLen)+    (filled, v') <- go 0 v+    pure $ MV.take filled v'+  where++    -- if our prefix is > 500mb then we have other issues and should just bail...+    maxIdx :: Num i => i+    maxIdx = 500 * (10 ^ (6 :: Int))++    -- attempts to compute a concrete length for the input buffer+    inputLen :: Maybe Int+    inputLen = case bufLength b of+      Lit s -> if s > maxIdx+        then internalError "concretePrefix: input buffer size exceeds 500mb"+        -- unafeInto: s is <= 500,000,000+        else Just (unsafeInto s)+      _ -> Nothing++    -- recursively reads succesive bytes from `b` until we reach a symbolic+    -- byte returns the larged index read from and a reference to the mutable+    -- vec (might not be the same as the input because of the call to grow)+    go :: forall s . Int -> MVector s Word8 -> ST s (Int, MVector s Word8)+    go i v+      -- if the prefix is very large then bail+      | i >= maxIdx = internalError "concretePrefix: prefix size exceeds 500mb"+      -- if the input buffer has a concrete size, then don't read past the end+      | Just mr <- inputLen, i >= mr = pure (i, v)+      -- double the size of the vector if we've reached the end+      | i >= MV.length v = do+        v' <- MV.grow v (MV.length v)+        go i v'+      -- read the byte at `i` in `b` into `v` if it is concrete, or halt if we've reached a symbolic byte+      -- unsafeInto: i will always be positive+      | otherwise = case readByte (Lit . unsafeInto $ i) b of+          LitByte byte -> do+            MV.write v i byte+            go (i+1) v+          _ -> pure (i, v)++ word256At :: Expr EWord -> Lens (Expr Buf) (Expr Buf) (Expr EWord) (Expr EWord) word256At i = lens getter setter where   getter = readWord i@@ -555,12 +589,17 @@   WriteByte i v prev -> WriteByte i v (stripWrites off size prev)   WriteWord i v prev -> WriteWord i v (stripWrites off size prev)   CopySlice srcOff dstOff size' src dst -> CopySlice srcOff dstOff size' src dst-  GVar _ ->  internalError "unexpected GVar in stripWrites"+  GVar _ ->  internalError "Unexpected GVar in stripWrites"   -- ** Storage ** -----------------------------------------------------------------------------------  +readStorage' :: Expr EWord -> Expr Storage -> Expr EWord+readStorage' loc store = case readStorage loc store of+                           Just v -> v+                           Nothing -> Lit 0+ -- | Reads the word at the given slot from the given storage expression. -- -- Note that we return a Nothing instead of a 0x0 if we are reading from a@@ -568,74 +607,159 @@ -- no explicit writes to the requested slot. This makes implementing rpc -- storage lookups much easier. If the store is backed by an AbstractStore we -- always return a symbolic value.-readStorage :: Expr EWord -> Expr EWord -> Expr Storage -> Maybe (Expr EWord)-readStorage _ _ EmptyStore = Nothing-readStorage addr slot store@(ConcreteStore s) = case (addr, slot) of-  (Lit a, Lit l) -> do-    ctrct <- Map.lookup a s-    val <- Map.lookup l ctrct-    pure $ Lit val-  _ -> Just $ SLoad addr slot store-readStorage addr' slot' s@AbstractStore = Just $ SLoad addr' slot' s-readStorage addr' slot' s@(SStore addr slot val prev) =-  if addr == addr'-  then if slot == slot'-       -- if address and slot match then we return the val in this write-       then Just val-       else case (slot, slot') of-              -- if the slots don't match and are lits, we can skip this write-              (Lit _, Lit _) -> readStorage addr' slot' prev-              -- if the slots don't match syntactically and are abstract then we can't skip this write-              _ -> Just $ SLoad addr' slot' s-  else case (addr, addr') of-    -- if the the addresses don't match and are lits, we can skip this write-    (Lit _, Lit _) -> readStorage addr' slot' prev-    -- if the the addresses don't match syntactically and are abstract then we can't skip this write-    _ -> Just $ SLoad addr' slot' s-readStorage _ _ (GVar _) = internalError "Can't read from a GVar"+readStorage :: Expr EWord -> Expr Storage -> Maybe (Expr EWord)+readStorage w st = go (simplify w) st+  where+    go :: Expr EWord -> Expr Storage -> Maybe (Expr EWord)+    go _ (GVar _) = internalError "Can't read from a GVar"+    go slot s@(AbstractStore _) = Just $ SLoad slot s+    go (Lit l) (ConcreteStore s) = Lit <$> Map.lookup l s+    go slot store@(ConcreteStore _) = Just $ SLoad slot store+    go slot s@(SStore prevSlot val prev) = case (prevSlot, slot) of+      -- if address and slot match then we return the val in this write+      _ | prevSlot == slot -> Just val -readStorage' :: Expr EWord -> Expr EWord -> Expr Storage -> Expr EWord-readStorage' addr loc store = case readStorage addr loc store of-                                Just v -> v-                                Nothing -> Lit 0+      -- if the slots don't match (see previous guard) and are lits, we can skip this write+      (Lit _, Lit _) -> go slot prev +      -- slot is for a map + map -> skip write+      (MappingSlot idA _, MappingSlot idB _)     | idsDontMatch idA idB  -> go slot prev+      (MappingSlot _ keyA, MappingSlot _ keyB)   | surelyNotEq keyA keyB -> go slot prev +      -- special case of array + map -> skip write+      (ArraySlotWithOffset idA _, Keccak64Bytes) | BS.length idA /= 64 -> go slot prev+      (ArraySlotZero idA, Keccak64Bytes)         | BS.length idA /= 64 -> go slot prev++      -- special case of array + map -> skip write+      (Keccak64Bytes, ArraySlotWithOffset idA _) | BS.length idA /= 64 -> go slot prev+      (Keccak64Bytes, ArraySlotZero idA)         | BS.length idA /= 64 -> go slot prev++      -- Fixed SMALL value will never match Keccak (well, it might, but that's VERY low chance)+      (Lit a, Keccak _) | a < 256 -> go slot prev+      (Keccak _, Lit a) | a < 256 -> go slot prev++      -- the chance of adding a value <= 2^32 to any given keccack output+      -- leading to an overflow is effectively zero. the chance of an overflow+      -- occuring here is 2^32/2^256 = 2^-224, which is close enough to zero+      -- for our purposes. This lets us completely simplify reads from write+      -- chains involving writes to arrays at literal offsets.+      (Lit a, Add (Lit b) (Keccak _) ) | a < 256, b < maxW32 -> go slot prev+      (Add (Lit a) (Keccak _) , Lit b) | a < 256, b < maxW32 -> go slot prev++      -- Finding two Keccaks that are < 256 away from each other should be VERY hard+      -- This simplification allows us to deal with maps of structs+      (Add (Lit a2) (Keccak _), Add (Lit b2) (Keccak _)) | a2 /= b2, abs(a2-b2) < 256 -> go slot prev+      (Add (Lit a2) (Keccak _), (Keccak _)) | a2 > 0, a2 < 256 -> go slot prev+      ((Keccak _), Add (Lit b2) (Keccak _)) | b2 > 0, b2 < 256 -> go slot prev++      -- case of array + array, but different id's or different concrete offsets+      -- zero offs vs zero offs+      (ArraySlotZero idA, ArraySlotZero idB)                   | idA /= idB -> go slot prev+      -- zero offs vs non-zero offs+      (ArraySlotZero idA, ArraySlotWithOffset idB _)           | idA /= idB -> go slot prev+      (ArraySlotZero _, ArraySlotWithOffset _ (Lit offB))      | offB /= 0  -> go slot prev+      -- non-zero offs vs zero offs+      (ArraySlotWithOffset idA _, ArraySlotZero idB)           | idA /= idB -> go slot prev+      (ArraySlotWithOffset _ (Lit offA), ArraySlotZero _)      | offA /= 0  -> go slot prev+      -- non-zero offs vs non-zero offs+      (ArraySlotWithOffset idA _, ArraySlotWithOffset idB _)   | idA /= idB -> go slot prev++      (ArraySlotWithOffset _ offA, ArraySlotWithOffset _ offB) | surelyNotEq offA offB -> go slot prev++      -- we are unable to determine statically whether or not we can safely move deeper in the write chain, so return an abstract term+      _ -> Just $ SLoad slot s++    surelyNotEq :: Expr a -> Expr a -> Bool+    surelyNotEq (Lit a) (Lit b) = a /= b+    -- never equal: x+y (y is concrete) vs x+z (z is concrete), y!=z+    surelyNotEq (Add (Lit l1) v1) (Add (Lit l2) v2) = l1 /= l2 && v1 == v2+    -- never equal: x+y (y is concrete, non-zero) vs x+    surelyNotEq v1 (Add (Lit l2) v2) = l2 /= 0 && v1 == v2+    surelyNotEq (Add (Lit l1) v1) v2 = l1 /= 0 && v1 == v2+    surelyNotEq _ _ = False++    maxW32 :: W256+    maxW32 = into (maxBound :: Word32)++-- storage slots for maps are determined by (keccak (bytes32(key) ++ bytes32(id)))+pattern MappingSlot :: ByteString -> Expr EWord -> Expr EWord+pattern MappingSlot id key = Keccak (CopySlice (Lit 0) (Lit 0) (Lit 64) (WriteWord (Lit 0) key (ConcreteBuf id)) (ConcreteBuf ""))++-- keccak of any 64 bytes value+pattern Keccak64Bytes :: Expr EWord+pattern Keccak64Bytes <- Keccak (CopySlice (Lit 0) (Lit 0) (Lit 64) _ (ConcreteBuf ""))++-- storage slots for arrays are determined by (keccak(bytes32(id)) + offset)+pattern ArraySlotWithOffset :: ByteString -> Expr EWord -> Expr EWord+pattern ArraySlotWithOffset id offset = Add (Keccak (ConcreteBuf id)) offset++-- special pattern to match the 0th element because the `Add` term gets simplified out+pattern ArraySlotZero :: ByteString -> Expr EWord+pattern ArraySlotZero id = Keccak (ConcreteBuf id)++-- checks if two mapping ids match or not+idsDontMatch :: ByteString -> ByteString -> Bool+idsDontMatch a b = BS.length a >= 64 && BS.length b >= 64 && diff32to64Byte a b+  where+    diff32to64Byte :: ByteString -> ByteString -> Bool+    diff32to64Byte x y = x32 /= y32+      where+       x32 = BS.take 32 $ BS.drop 32 x+       y32 = BS.take 32 $ BS.drop 32 y++slotPos :: Word8 -> ByteString+slotPos pos = BS.pack ((replicate 31 (0::Word8))++[pos])++-- | Turns Literals into keccak(bytes32(id)) + offset (i.e. writes to arrays)+structureArraySlots :: Expr a -> Expr a+structureArraySlots e = mapExpr go e+  where+    go :: Expr a -> Expr a+    go orig@(Lit key) = case litToArrayPreimage key of+      Just (array, offset) -> ArraySlotWithOffset (slotPos array) (Lit offset)+      _ -> orig+    go a = a++-- Takes in value, checks if it's within 256 of a pre-computed array hash value+-- if it is, it returns (array_number, offset)+litToArrayPreimage :: W256 -> Maybe (Word8, W256)+litToArrayPreimage val = go preImages+  where+    go :: [(W256, Word8)] -> Maybe (Word8, W256)+    go ((image, preimage):ax) = if val >= image && val-image <= 255 then Just (preimage, val-image)+                                                                    else go ax+    go [] = Nothing+ -- | Writes a value to a key in a storage expression. -- -- Concrete writes on top of a concrete or empty store will produce a new -- ConcreteStore, otherwise we add a new write to the storage expression.-writeStorage :: Expr EWord -> Expr EWord -> Expr EWord -> Expr Storage -> Expr Storage-writeStorage a@(Lit addr) k@(Lit key) v@(Lit val) store = case store of-  EmptyStore -> ConcreteStore (Map.singleton addr (Map.singleton key val))-  ConcreteStore s -> let-      ctrct = Map.findWithDefault Map.empty addr s-    in ConcreteStore (Map.insert addr (Map.insert key val ctrct) s)-  _ -> SStore a k v store-writeStorage addr key val store@(SStore addr' key' val' prev)-  | addr == addr'+writeStorage :: Expr EWord -> Expr EWord -> Expr Storage -> Expr Storage+writeStorage k@(Lit key) v@(Lit val) store = case store of+  ConcreteStore s -> ConcreteStore (Map.insert key val s)+  _ -> SStore k v store+writeStorage key val store@(SStore key' val' prev)      = if key == key'        -- if we're overwriting an existing location, then drop the write-       then SStore addr key val prev-       else case (addr, addr', key, key') of+       then SStore key val prev+       else case (key, key') of               -- if we can know statically that the new write doesn't overlap with the existing write, then we continue down the write chain               -- we impose an ordering relation on the writes that we push down to ensure termination when this routine is called from the simplifier-              (Lit a, Lit a', Lit k, Lit k') -> if a > a' || (a == a' && k > k')-                                                then SStore addr' key' val' (writeStorage addr key val prev)-                                                else SStore addr key val store+              (Lit k, Lit k') -> if k > k'+                                 then SStore key' val' (writeStorage key val prev)+                                 else SStore key val store               -- otherwise stack a new write on top of the the existing write chain-              _ -> SStore addr key val store-  | otherwise-     = case (addr, addr') of-        -- if we can know statically that the new write doesn't overlap with the existing write, then we continue down the write chain-        -- once again we impose an ordering relation on the pushed down writes to ensure termination-        (Lit a, Lit a') -> if a > a'-                           then SStore addr' key' val' (writeStorage addr key val prev)-                           else SStore addr key val store-        -- otherwise stack a new write on top of the the existing write chain-        _ -> SStore addr key val store-writeStorage addr key val store = SStore addr key val store+              _ -> SStore key val store+writeStorage key val store = SStore key val store  +getAddr :: Expr Storage -> Maybe (Expr EAddr)+getAddr (SStore _ _ p) = getAddr p+getAddr (AbstractStore a) = Just a+getAddr (ConcreteStore _) = Nothing+getAddr (GVar _) = internalError "cannot determine addr of a GVar"++ -- ** Whole Expression Simplification ** -----------------------------------------------------------  @@ -644,21 +768,27 @@ simplify :: Expr a -> Expr a simplify e = if (mapExpr go e == e)                then e-               else simplify (mapExpr go e)+               else simplify (mapExpr go (structureArraySlots e))   where     go :: Expr a -> Expr a++    go (Failure a b c) = Failure (simplifyProps a) b c+    go (Partial a b c) = Partial (simplifyProps a) b c+    go (Success a b c d) = Success (simplifyProps a) b c d+     -- redundant CopySlice     go (CopySlice (Lit 0x0) (Lit 0x0) (Lit 0x0) _ dst) = dst      -- simplify storage-    go (SLoad addr slot store) = readStorage' addr slot store-    go (SStore addr slot val store) = writeStorage addr slot val store+    go (SLoad slot store) = readStorage' slot store+    go (SStore slot val store) = writeStorage slot val store      -- simplify buffers     go o@(ReadWord (Lit _) _) = simplifyReads o     go (ReadWord idx buf) = readWord idx buf     go o@(ReadByte (Lit _) _) = simplifyReads o     go (ReadByte idx buf) = readByte idx buf+    go (BufLength buf) = bufLength buf      -- We can zero out any bytes in a base ConcreteBuf that we know will be overwritten by a later write     -- TODO: make this fully general for entire write chains, not just a single write.@@ -673,6 +803,20 @@     go (WriteWord a b c) = writeWord a b c      go (WriteByte a b c) = writeByte a b c++    -- truncate some concrete source buffers to the portion relevant for the CopySlice if we're copying a fully concrete region+    go orig@(CopySlice srcOff@(Lit n) dstOff size@(Lit sz)+        -- It doesn't matter what wOffs we write to, because only the first+        -- n+sz of ConcreteBuf will be used by CopySlice+        (WriteWord wOff value (ConcreteBuf buf)) dst)+          -- Let's not deal with overflow+          | n+sz >= n+          , n+sz >= sz+          , n+sz <= maxBytes+            = (CopySlice srcOff dstOff size+                (WriteWord wOff value (ConcreteBuf simplifiedBuf)) dst)+          | otherwise = orig+            where simplifiedBuf = BS.take (unsafeInto (n+sz)) buf     go (CopySlice a b c d f) = copySlice a b c d f      go (IndexWord a b) = indexWord a b@@ -684,26 +828,31 @@     go (EVM.Types.LT _ (Lit 0)) = Lit 0      -- normalize all comparisons in terms of LT-    go (EVM.Types.GT a b) = EVM.Types.LT b a-    go (EVM.Types.GEq a b) = EVM.Types.LEq b a-    go (EVM.Types.LEq a b) = EVM.Types.IsZero (EVM.Types.GT a b)-+    go (EVM.Types.GT a b) = lt b a+    go (EVM.Types.GEq a b) = leq b a+    go (EVM.Types.LEq a b) = EVM.Types.IsZero (gt a b)     go (IsZero a) = iszero a      -- 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 o@(Eq a b)+    go (Eq (Lit 0) (Sub a b)) = eq a b+    go (Eq a b)       | a == b = Lit 1-      | otherwise = o+      | otherwise = eq a b      -- redundant ITE     go (ITE (Lit x) a b)       | x == 0 = b       | otherwise = a +    -- address masking+    go (And (Lit 0xffffffffffffffffffffffffffffffffffffffff) a@(WAddr _)) = a++    -- literal addresses+    go (WAddr (LitAddr a)) = Lit $ into a+     -- simple div/mod/add/sub     go (Div o1@(Lit _)  o2@(Lit _)) = EVM.Expr.div  o1 o2     go (SDiv o1@(Lit _) o2@(Lit _)) = EVM.Expr.sdiv o1 o2@@ -727,28 +876,28 @@     go (Add (Sub orig (Lit x)) (Lit y)) = Add orig (Lit (y-x))      -- redundant add / sub-    go o@(Sub (Add a b) c)+    go (Sub (Add a b) c)       | a == c = b       | b == c = a-      | otherwise = o+      | otherwise = sub (add a b) c      -- add / sub identities-    go o@(Add a b)+    go (Add a b)       | b == (Lit 0) = a       | a == (Lit 0) = b-      | otherwise = o-    go o@(Sub a b)+      | otherwise = add a b+    go (Sub a b)       | a == b = Lit 0       | b == (Lit 0) = a-      | otherwise = o+      | otherwise = sub a b      -- SHL / SHR by 0-    go o@(SHL a v)+    go (SHL a v)       | a == (Lit 0) = v-      | otherwise = o-    go o@(SHR a v)+      | otherwise = shl a v+    go (SHR a v)       | a == (Lit 0) = v-      | otherwise = o+      | otherwise = shr a v      -- doubled And     go o@(And a (And b c))@@ -760,8 +909,9 @@     go o@(And (Lit x) _)       | x == 0 = Lit 0       | otherwise = o-    go o@(And _ (Lit x))+    go o@(And v (Lit x))       | x == 0 = Lit 0+      | x == maxLit = v       | otherwise = o     go o@(Or (Lit x) b)       | x == 0 = b@@ -790,9 +940,23 @@     go (EVM.Types.Not (EVM.Types.Not a)) = a      -- Some trivial min / max eliminations-    go (Max (Lit 0) a) = a-    go (Min (Lit 0) _) = Lit 0+    go (Max a b) = case (a, b) of+                    (Lit 0, _) -> b+                    _ -> EVM.Expr.max a b+    go (Min a b) = case (a, b) of+                     (Lit 0, _) -> Lit 0+                     _ -> EVM.Expr.min a b +    -- Some trivial mul eliminations+    go (Mul a b) = case (a, b) of+                     (Lit 0, _) -> Lit 0+                     (Lit 1, _) -> b+                     _ -> mul a b+    -- Some trivial div eliminations+    go (Div (Lit 0) _) = Lit 0 -- divide 0 by anything (including 0) is zero in EVM+    go (Div _ (Lit 0)) = Lit 0 -- divide anything by 0 is zero in EVM+    go (Div a (Lit 1)) = a+     -- If a >= b then the value of the `Max` expression can never be < b     go o@(LT (Max (Lit a) _) (Lit b))       | a >= b = Lit 0@@ -809,6 +973,109 @@     go a = a  +-- ** Prop Simplification ** -----------------------------------------------------------------------+++simplifyProps :: [Prop] -> [Prop]+simplifyProps ps = if canBeSat then simplified else [PBool False]+  where+    simplified = remRedundantProps . map simplifyProp . flattenProps $ ps+    canBeSat = constFoldProp simplified++-- | Evaluate the provided proposition down to its most concrete result+simplifyProp :: Prop -> Prop+simplifyProp prop =+  let new = mapProp' go (simpInnerExpr prop)+  in if (new == prop) then prop else simplifyProp new+  where+    go :: Prop -> Prop++    -- LT/LEq comparisions+    go (PLT  (Var _) (Lit 0)) = PBool False+    go (PLEq (Lit 0) (Var _)) = PBool True+    go (PLT  (Lit val) (Var _)) | val == maxLit = PBool False+    go (PLEq (Var _) (Lit val)) | val == maxLit = PBool True+    go (PLT (Lit l) (Lit r)) = PBool (l < r)+    go (PLEq (Lit l) (Lit r)) = PBool (l <= r)+    go (PLEq a (Max b _)) | a == b = PBool True+    go (PLEq a (Max _ b)) | a == b = PBool True++    -- negations+    go (PNeg (PBool b)) = PBool (Prelude.not b)+    go (PNeg (PNeg a)) = a++    -- solc specific stuff++    -- iszero(a) -> (a == 0)+    -- iszero(iszero(a))) -> ~(a == 0) -> a > 0+    -- iszero(iszero(a)) == 0 -> ~~(a == 0) -> a == 0+    -- ~(iszero(iszero(a)) == 0) -> ~~~(a == 0) -> ~(a == 0) -> a > 0+    go (PNeg (PEq (IsZero (IsZero a)) (Lit 0))) = PGT a (Lit 0)++    -- iszero(a) -> (a == 0)+    -- iszero(a) == 0 -> ~(a == 0)+    -- ~(iszero(a) == 0) -> ~~(a == 0) -> a == 0+    go (PNeg (PEq (IsZero a) (Lit 0))) = PEq a (Lit 0)++    -- a < b == 0 -> ~(a < b)+    -- ~(a < b == 0) -> ~~(a < b) -> a < b+    go (PNeg (PEq (LT a b) (Lit 0x0))) = PLT a b++    -- And/Or+    go (PAnd (PBool l) (PBool r)) = PBool (l && r)+    go (PAnd (PBool False) _) = PBool False+    go (PAnd _ (PBool False)) = PBool False+    go (POr (PBool True) _) = PBool True+    go (POr _ (PBool True)) = PBool True+    go (POr (PBool l) (PBool r)) = PBool (l || r)++    -- Imply+    go (PImpl _ (PBool True)) = PBool True+    go (PImpl (PBool True) b) = b+    go (PImpl (PBool False) _) = PBool True++    -- Eq+    go (PEq (Eq a b) (Lit 0)) = PNeg (PEq a b)+    go (PEq (Eq a b) (Lit 1)) = PEq a b+    go (PEq (Sub a b) (Lit 0)) = PEq a b+    go (PEq (Lit l) (Lit r)) = PBool (l == r)+    go o@(PEq l r)+      | l == r = PBool True+      | otherwise = o+    go p = p+++    -- Applies `simplify` to the inner part of a Prop, e.g.+    -- (PEq (Add (Lit 1) (Lit 2)) (Var "a")) becomes+    -- (PEq (Lit 3) (Var "a")+    simpInnerExpr :: Prop -> Prop+    -- rewrite everything as LEq or LT+    simpInnerExpr (PGEq a b) = simpInnerExpr (PLEq b a)+    simpInnerExpr (PGT a b) = simpInnerExpr (PLT b a)+    -- simplifies the inner expression+    simpInnerExpr (PEq a b) = PEq (simplify a) (simplify b)+    simpInnerExpr (PLT a b) = PLT (simplify a) (simplify b)+    simpInnerExpr (PLEq a b) = PLEq (simplify a) (simplify b)+    simpInnerExpr (PNeg a) = PNeg (simpInnerExpr a)+    simpInnerExpr (PAnd a b) = PAnd (simpInnerExpr a) (simpInnerExpr b)+    simpInnerExpr (POr a b) = POr (simpInnerExpr a) (simpInnerExpr b)+    simpInnerExpr (PImpl a b) = PImpl (simpInnerExpr a) (simpInnerExpr b)+    simpInnerExpr orig@(PBool _) = orig++-- Makes [PAnd a b] into [a,b]+flattenProps :: [Prop] -> [Prop]+flattenProps [] = []+flattenProps (a:ax) = case a of+  PAnd x1 x2 -> x1:x2:flattenProps ax+  x -> x:flattenProps ax++-- removes redundant (constant True/False) props+remRedundantProps :: [Prop] -> [Prop]+remRedundantProps p = collapseFalse . filter (\x -> x /= PBool True) . nubOrd $ p+  where+    collapseFalse ps = if isJust $ find (== PBool False) ps then [PBool False] else ps++ -- ** Conversions ** -------------------------------------------------------------------------------  @@ -819,6 +1086,16 @@ exprToAddr (Lit x) = Just (unsafeInto x) exprToAddr _ = Nothing +-- TODO: make this smarter, probably we will need to use the solver here?+wordToAddr :: Expr EWord -> Maybe (Expr EAddr)+wordToAddr = go . simplify+  where+    go :: Expr EWord -> Maybe (Expr EAddr)+    go = \case+      WAddr a -> Just a+      Lit a -> Just $ LitAddr (truncateToAddr a)+      _ -> Nothing+ litCode :: BS.ByteString -> [Expr Byte] litCode bs = fmap LitByte (BS.unpack bs) @@ -837,8 +1114,24 @@ -- Is the given expr a literal word? isLitWord :: Expr EWord -> Bool isLitWord (Lit _) = True+isLitWord (WAddr (LitAddr _)) = True isLitWord _ = False +isSuccess :: Expr End -> Bool+isSuccess = \case+  Success {} -> True+  _ -> False++isFailure :: Expr End -> Bool+isFailure = \case+  Failure {} -> True+  _ -> False++isPartial :: Expr End -> Bool+isPartial = \case+  Partial {} -> True+  _ -> False+ -- | Returns the byte at idx from the given word. indexWord :: Expr EWord -> Expr EWord -> Expr Byte -- Simplify masked reads:@@ -997,3 +1290,46 @@  inRange :: Int -> Expr EWord -> Prop inRange sz e = PAnd (PGEq e (Lit 0)) (PLEq e (Lit $ 2 ^ sz - 1))+++-- | images of keccak(bytes32(x)) where 0 <= x < 256+preImages :: [(W256, Word8)]+preImages = [(keccak' (word256Bytes . into $ i), i) | i <- [0..255]]++data ConstState = ConstState+  { values :: Map.Map (Expr EWord) W256+  , canBeSat :: Bool+  }+  deriving (Show)++-- | Folds constants+constFoldProp :: [Prop] -> Bool+constFoldProp ps = oneRun ps (ConstState mempty True)+  where+    oneRun ps2 startState = (execState (mapM (go . simplifyProp) ps2) startState).canBeSat+    go :: Prop -> State ConstState ()+    go x = case x of+        PEq (Lit l) a -> do+          s <- get+          case Map.lookup a s.values of+            Just l2 -> case l==l2 of+                True -> pure ()+                False -> put ConstState {canBeSat=False, values=mempty}+            Nothing -> do+              let vs' = Map.insert a l s.values+              put $ s{values=vs'}+        PEq a b@(Lit _) -> go (PEq b a)+        PAnd a b -> do+          go a+          go b+        POr a b -> do+          s <- get+          let+            v1 = oneRun [a] s+            v2 = oneRun [b] s+          when (Prelude.not v1) $ go b+          when (Prelude.not v2) $ go a+          s2 <- get+          put $ s{canBeSat=(s2.canBeSat && (v1 || v2))}+        PBool False -> put $ ConstState {canBeSat=False, values=mempty}+        _ -> pure ()
− src/EVM/Facts.hs
@@ -1,231 +0,0 @@-{-# LANGUAGE PatternSynonyms #-}---- Converts between Ethereum contract states and simple trees of--- texts.  Dumps and loads such trees as Git repositories (the state--- gets serialized as commits with folders and files).------ Example state file hierarchy:------   /0123...abc/balance      says "0x500"---   /0123...abc/code         says "60023429..."---   /0123...abc/nonce        says "0x3"---   /0123...abc/storage/0x1  says "0x1"---   /0123...abc/storage/0x2  says "0x0"------ This format could easily be serialized into any nested record--- syntax, e.g. JSON.--module EVM.Facts-  ( File (..)-  , Fact (..)-  , Data (..)-  , Path (..)-  , apply-  , applyCache-  , cacheFacts-  , contractFacts-  , vmFacts-  , factToFile-  , fileToFact-  ) where--import EVM (bytecode)-import EVM qualified-import EVM.Expr (writeStorage, litAddr)-import EVM.Types--import Optics.Core-import Optics.State--import Control.Monad.State.Strict (execState, when)-import Data.ByteString (ByteString)-import Data.ByteString.Base16 qualified as BS16-import Data.ByteString qualified as BS-import Data.ByteString.Char8 qualified as Char8-import Data.Map (Map)-import Data.Map qualified as Map-import Data.Set (Set)-import Data.Set qualified as Set-import Data.Ord (comparing)-import Text.Read (readMaybe)-import Witch (into)---- We treat everything as ASCII byte strings because--- we only use hex digits (and the letter 'x').-type ASCII = ByteString---- When using string literals, default to infer the ASCII type.-default (ASCII)---- We use the word "fact" to mean one piece of serializable--- information about the state.------ Note that Haskell allows this kind of union of records.--- It's convenient here, but typically avoided.-data Fact-  = BalanceFact { addr :: Addr, what :: W256 }-  | NonceFact   { addr :: Addr, what :: W256 }-  | StorageFact { addr :: Addr, what :: W256, which :: W256 }-  | CodeFact    { addr :: Addr, blob :: ByteString }-  deriving (Eq, Show)---- A fact path means something like "/0123...abc/storage/0x1",--- or alternatively "contracts['0123...abc'].storage['0x1']".-data Path = Path [ASCII] ASCII-  deriving (Eq, Ord, Show)---- A fact data is the content of a file.  We encapsulate it--- with a newtype to make it easier to change the representation--- (to use bytestrings, some sum type, or whatever).-newtype Data = Data { dataASCII :: ASCII }-  deriving (Eq, Ord, Show)---- We use the word "file" to denote a serialized value at a path.-data File = File { filePath :: Path, fileData :: Data }-  deriving (Eq, Ord, Show)--class AsASCII a where-  dump :: a -> ASCII-  load :: ASCII -> Maybe a--instance AsASCII Addr where-  dump = Char8.pack . show-  load = readMaybe . Char8.unpack--instance AsASCII W256 where-  dump = Char8.pack . show-  load = readMaybe . Char8.unpack--instance AsASCII ByteString where-  dump x = BS16.encodeBase16' x <> "\n"-  load x =-    case BS16.decodeBase16 . mconcat . BS.split 10 $ x of-      Right y -> Just y-      _       -> Nothing--contractFacts :: Addr -> Contract -> Map W256 (Map W256 W256) -> [Fact]-contractFacts a x store = case view bytecode x of-  ConcreteBuf b ->-    storageFacts a store ++-    [ BalanceFact a x.balance-    , NonceFact   a x.nonce-    , CodeFact    a b-    ]-  _ ->-    -- here simply ignore storing the bytecode-    storageFacts a store ++-    [ BalanceFact a x.balance-    , NonceFact   a x.nonce-    ]---storageFacts :: Addr -> Map W256 (Map W256 W256) -> [Fact]-storageFacts a store = map f (Map.toList (Map.findWithDefault Map.empty (into a) store))-  where-    f :: (W256, W256) -> Fact-    f (k, v) = StorageFact-      { addr  = a-      , what  = v-      , which = k-      }--cacheFacts :: Cache -> Set Fact-cacheFacts c = Set.fromList $ do-  (k, v) <- Map.toList c.fetchedContracts-  contractFacts k v c.fetchedStorage--vmFacts :: VM -> Set Fact-vmFacts vm = Set.fromList $ do-  (k, v) <- Map.toList vm.env.contracts-  case vm.env.storage of-    EmptyStore -> contractFacts k v Map.empty-    ConcreteStore s -> contractFacts k v s-    _ -> internalError "cannot serialize an abstract store"---- Somewhat stupidly, this function demands that for each contract,--- the code fact for that contract comes before the other facts for--- that contract.  This is an incidental thing because right now we--- always initialize contracts starting with the code (to calculate--- the code hash and so on).------ Therefore, we need to make sure to sort the fact set in such a way.-apply1 :: VM -> Fact -> VM-apply1 vm fact =-  case fact of-    CodeFact    {..} -> flip execState vm $ do-      assign (#env % #contracts % at addr) (Just (EVM.initialContract (RuntimeCode (ConcreteRuntimeCode blob))))-      when (vm.state.contract == addr) $ EVM.loadContract addr-    StorageFact {..} ->-      vm & over (#env % #storage) (writeStorage (litAddr addr) (Lit which) (Lit what))-    BalanceFact {..} ->-      vm & set (#env % #contracts % ix addr % #balance) what-    NonceFact   {..} ->-      vm & set (#env % #contracts % ix addr % #nonce) what--apply2 :: VM -> Fact -> VM-apply2 vm fact =-  case fact of-    CodeFact    {..} -> flip execState vm $ do-      assign (#cache % #fetchedContracts % at addr) (Just (EVM.initialContract (RuntimeCode (ConcreteRuntimeCode blob))))-      when (vm.state.contract == addr) $ EVM.loadContract addr-    StorageFact {..} -> let-        store = vm.cache.fetchedStorage-        ctrct = Map.findWithDefault Map.empty (into addr) store-      in-        vm & set (#cache % #fetchedStorage) (Map.insert (into addr) (Map.insert which what ctrct) store)-    BalanceFact {..} ->-      vm & set (#cache % #fetchedContracts % ix addr % #balance) what-    NonceFact   {..} ->-      vm & set (#cache % #fetchedContracts % ix addr % #nonce) what---- Sort facts in the right order for `apply1` to work.-instance Ord Fact where-  compare = comparing f-    where-    f :: Fact -> (Int, Addr, W256)-    f (CodeFact a _)      = (0, a, 0)-    f (BalanceFact a _)   = (1, a, 0)-    f (NonceFact a _)     = (2, a, 0)-    f (StorageFact a _ x) = (3, a, x)---- Applies a set of facts to a VM.-apply :: VM -> Set Fact -> VM-apply =-  -- The set's ordering is relevant; see `apply1`.-  foldl apply1------ Applies a set of facts to a VM.-applyCache :: VM -> Set Fact -> VM-applyCache =-  -- The set's ordering is relevant; see `apply1`.-  foldl apply2--factToFile :: Fact -> File-factToFile fact = case fact of-  StorageFact {..} -> mk ["storage"] (dump which) what-  BalanceFact {..} -> mk []          "balance"    what-  NonceFact   {..} -> mk []          "nonce"      what-  CodeFact    {..} -> mk []          "code"       blob-  where-    mk :: AsASCII a => [ASCII] -> ASCII -> a -> File-    mk prefix base a =-      File (Path (dump fact.addr : prefix) base)-           (Data $ dump a)---- This lets us easier pattern match on serialized things.--- Uses language extensions: `PatternSynonyms` and `ViewPatterns`.-pattern Load :: AsASCII a => a -> ASCII-pattern Load x <- (load -> Just x)--fileToFact :: File -> Maybe Fact-fileToFact = \case-  File (Path [Load a] "code")    (Data (Load x))-    -> Just (CodeFact a x)-  File (Path [Load a] "balance") (Data (Load x))-    -> Just (BalanceFact a x)-  File (Path [Load a] "nonce")   (Data (Load x))-    -> Just (NonceFact a x)-  File (Path [Load a, "storage"] (Load x)) (Data (Load y))-    -> Just (StorageFact a y x)-  _-    -> Nothing
− src/EVM/Facts/Git.hs
@@ -1,48 +0,0 @@---- This is a backend for the fact representation that uses a Git--- repository as the store.--module EVM.Facts.Git-  ( saveFacts-  , loadFacts-  , RepoAt (..)-  ) where--import EVM.Facts (Fact (..), File (..), Path (..), Data (..), fileToFact, factToFile)--import Optics.Core-import Data.Set   (Set)-import Data.Set qualified as Set-import Data.Maybe (catMaybes)-import Restless.Git qualified as Git--newtype RepoAt = RepoAt String-  deriving (Eq, Ord, Show)---- For modularity reasons, we have our own file data type that is--- isomorphic with the one in the `restless-git` library.  We declare--- the isomorphism so we can go between them easily.-fileRepr :: Iso' File Git.File-fileRepr = iso f g-  where-    f :: File -> Git.File-    f (File     (Path ps p)     (Data x)) =-      Git.File (Git.Path ps p) x--    g :: Git.File -> File-    g (Git.File (Git.Path ps p) x) =-      File     (Path ps p)     (Data x)--saveFacts :: RepoAt -> Set Fact -> IO ()-saveFacts (RepoAt repo) facts =-  Git.save repo "hevm execution"-    (Set.map (view fileRepr . factToFile) facts)--prune :: Ord a => Set (Maybe a) -> Set a-prune = Set.fromList . catMaybes . Set.toList--loadFacts :: RepoAt -> IO (Set Fact)-loadFacts (RepoAt src) =-  fmap-    (prune . Set.map (fileToFact . review fileRepr))-    (Git.load src)
src/EVM/FeeSchedule.hs view
@@ -53,58 +53,36 @@   , g_access_list_storage_key :: n   } deriving Show --- For the purposes of this module, we define an EIP as just a fee--- schedule modification.-type EIP n = Num n => FeeSchedule n -> FeeSchedule n---- EIP150: Gas cost changes for IO-heavy operations--- <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md>-eip150 :: EIP n-eip150 fees = fees-  { g_extcode = 700-  , g_balance = 400-  , g_sload = 200-  , g_call = 700-  , g_selfdestruct = 5000-  , g_selfdestruct_newaccount = 25000-  }---- EIP160: EXP cost increase--- <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-160.md>-eip160 :: EIP n-eip160 fees = fees-  { g_expbyte = 50 }--homestead :: Num n => FeeSchedule n-homestead = FeeSchedule+feeSchedule :: Num n => FeeSchedule n+feeSchedule = FeeSchedule   { g_zero = 0   , g_base = 2   , g_verylow = 3   , g_low = 5   , g_mid = 8   , g_high = 10-  , g_extcode = 20-  , g_balance = 20-  , g_sload = 50+  , g_extcode = 2600+  , g_balance = 2600+  , g_sload = 100   , g_jumpdest = 1   , g_sset = 20000-  , g_sreset = 5000+  , g_sreset = 2900   , r_sclear = 15000-  , g_selfdestruct = 0-  , g_selfdestruct_newaccount = 0+  , g_selfdestruct = 5000+  , g_selfdestruct_newaccount = 25000   , r_selfdestruct = 24000   , g_create = 32000   , g_codedeposit = 200-  , g_call = 40+  , g_call = 2600   , g_callvalue = 9000   , g_callstipend = 2300   , g_newaccount = 25000   , g_exp = 10-  , g_expbyte = 10+  , g_expbyte = 50   , g_memory = 3   , g_txcreate = 32000   , g_txdatazero = 4-  , g_txdatanonzero = 68+  , g_txdatanonzero = 16   , g_transaction = 21000   , g_log = 375   , g_logdata = 8@@ -114,12 +92,12 @@   , g_initcodeword = 2   , g_copy = 3   , g_blockhash = 20-  , g_extcodehash = 400+  , g_extcodehash = 2600   , g_quaddivisor = 20-  , g_ecadd = 500-  , g_ecmul = 40000-  , g_pairing_point = 80000-  , g_pairing_base = 100000+  , g_ecadd = 150+  , g_ecmul = 6000+  , g_pairing_point = 34000+  , g_pairing_base = 45000   , g_fround = 1   , r_block = 2000000000000000000   , g_cold_sload = 2100@@ -128,60 +106,3 @@   , g_access_list_address = 2400   , g_access_list_storage_key = 1900   }--metropolis :: Num n => FeeSchedule n-metropolis = eip160 . eip150 $ homestead---- EIP1108: Reduce alt_bn128 precompile gas costs--- <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1108.md>-eip1108 :: EIP n-eip1108 fees = fees-  { g_ecadd = 150-  , g_ecmul = 6000-  , g_pairing_point = 34000-  , g_pairing_base = 45000-  }---- EIP1884: Repricing for trie-size-dependent opcodes--- <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1884.md>-eip1884 :: EIP n-eip1884 fees = fees-  { g_sload = 800-  , g_balance = 700-  , g_extcodehash = 700-  }---- EIP2028: Transaction data gas cost reduction--- <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2028.md>-eip2028 :: EIP n-eip2028 fees = fees-  { g_txdatanonzero = 16-  }---- EIP2200: Structured definitions for gas metering--- <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2200.md>-eip2200 :: EIP n-eip2200 fees = fees-  { g_sload = 800-  , g_sset = 20000   -- not changed-  , g_sreset = 5000  -- not changed-  , r_sclear = 15000 -- not changed-  }--istanbul :: Num n => FeeSchedule n-istanbul = eip1108 . eip1884 . eip2028 . eip2200 $ metropolis--  -- EIP2929: Gas cost increases for state access opcodes-  -- <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2929.md>-eip2929 :: EIP n-eip2929 fees = fees-  { g_sload = 100-  , g_sreset = 5000 - 2100-  , g_call = 2600-  , g_balance = 2600-  , g_extcode = 2600-  , g_extcodehash = 2600-  }--berlin :: Num n => FeeSchedule n-berlin = eip2929 istanbul
src/EVM/Fetch.hs view
@@ -2,13 +2,14 @@  module EVM.Fetch where -import EVM (initialContract)+import EVM (initialContract, unknownContract) import EVM.ABI-import EVM.FeeSchedule qualified as FeeSchedule+import EVM.FeeSchedule (feeSchedule) import EVM.Format (hexText) import EVM.SMT import EVM.Solvers import EVM.Types+import EVM (emptyContract)  import Optics.Core @@ -32,7 +33,7 @@   QueryCode    :: Addr         -> RpcQuery BS.ByteString   QueryBlock   ::                 RpcQuery Block   QueryBalance :: Addr         -> RpcQuery W256-  QueryNonce   :: Addr         -> RpcQuery W256+  QueryNonce   :: Addr         -> RpcQuery W64   QuerySlot    :: Addr -> W256 -> RpcQuery W256   QueryChainId ::                 RpcQuery W256 @@ -109,7 +110,7 @@  parseBlock :: (AsValue s, Show s) => s -> Maybe Block parseBlock j = do-  coinbase   <- readText <$> j ^? key "miner" % _String+  coinbase   <- LitAddr . readText <$> j ^? key "miner" % _String   timestamp  <- Lit . readText <$> j ^? key "timestamp" % _String   number     <- readText <$> j ^? key "number" % _String   gasLimit   <- readText <$> j ^? key "gasLimit" % _String@@ -127,7 +128,7 @@      (Nothing, Just _, Just d) -> d      _ -> internalError "block contains both difficulty and prevRandao"   -- default codesize, default gas limit, default feescedule-  pure $ Block coinbase timestamp number prd gasLimit (fromMaybe 0 baseFee) 0xffffffff FeeSchedule.berlin+  pure $ Block coinbase timestamp number prd gasLimit (fromMaybe 0 baseFee) 0xffffffff feeSchedule  fetchWithSession :: Text -> Session -> Value -> IO (Maybe Value) fetchWithSession url sess x = do@@ -141,14 +142,14 @@     fetch :: Show a => RpcQuery a -> IO (Maybe a)     fetch = fetchQuery n (fetchWithSession url sess) -  theCode    <- MaybeT $ fetch (QueryCode addr)-  theNonce   <- MaybeT $ fetch (QueryNonce addr)-  theBalance <- MaybeT $ fetch (QueryBalance addr)+  code    <- MaybeT $ fetch (QueryCode addr)+  nonce   <- MaybeT $ fetch (QueryNonce addr)+  balance <- MaybeT $ fetch (QueryBalance addr)    pure $-    initialContract (RuntimeCode (ConcreteRuntimeCode theCode))-      & set #nonce    theNonce-      & set #balance  theBalance+    initialContract (RuntimeCode (ConcreteRuntimeCode code))+      & set #nonce    (Just nonce)+      & set #balance  (Lit balance)       & set #external True  fetchSlotWithSession@@ -181,18 +182,18 @@   sess <- Session.newAPISession   fetchQuery Latest (fetchWithSession url sess) QueryChainId -http :: Natural -> Maybe Natural -> BlockNumber -> Text -> Fetcher+http :: Natural -> Maybe Natural -> BlockNumber -> Text -> Fetcher s http smtjobs smttimeout n url q =   withSolvers Z3 smtjobs smttimeout $ \s ->     oracle s (Just (n, url)) q -zero :: Natural -> Maybe Natural -> Fetcher+zero :: Natural -> Maybe Natural -> Fetcher s zero smtjobs smttimeout q =   withSolvers Z3 smtjobs smttimeout $ \s ->     oracle s Nothing q  -- smtsolving + (http or zero)-oracle :: SolverGroup -> RpcInfo -> Fetcher+oracle :: SolverGroup -> RpcInfo -> Fetcher s oracle solvers info q = do   case q of     PleaseDoFFI vals continue -> case vals of@@ -207,12 +208,14 @@          -- Is is possible to satisfy the condition?          continue <$> checkBranch solvers (branchcondition ./= (Lit 0)) pathconds -    -- if we are using a symbolic storage model,-    -- we generate a new array to the fetched contract here-    PleaseFetchContract addr continue -> do+    PleaseFetchContract addr base continue -> do       contract <- case info of-                    Nothing -> pure $ Just $ initialContract (RuntimeCode (ConcreteRuntimeCode ""))-                    Just (n, url) -> fetchContractFrom n url addr+        Nothing -> let+          c = case base of+            AbstractBase -> unknownContract (LitAddr addr)+            EmptyBase -> emptyContract+          in pure $ Just c+        Just (n, url) -> fetchContractFrom n url addr       case contract of         Just x -> pure $ continue x         Nothing -> internalError $ "oracle error: " ++ show q@@ -226,7 +229,7 @@            Nothing ->              internalError $ "oracle error: " ++ show q -type Fetcher = Query -> IO (EVM ())+type Fetcher s = Query s -> IO (EVM s ())  -- | Checks which branches are satisfiable, checking the pathconditions for consistency -- if the third argument is true.@@ -235,20 +238,20 @@ -- will be pruned anyway. checkBranch :: SolverGroup -> Prop -> Prop -> IO BranchCondition checkBranch solvers branchcondition pathconditions = do-  checkSat solvers (assertProps [(branchcondition .&& pathconditions)]) >>= \case+  checkSat solvers (assertProps abstRefineDefault [(branchcondition .&& pathconditions)]) >>= \case     -- the condition is unsatisfiable     Unsat -> -- if pathconditions are consistent then the condition must be false       pure $ Case False     -- Sat means its possible for condition to hold     Sat _ -> -- is its negation also possible?-      checkSat solvers (assertProps [(pathconditions .&& (PNeg branchcondition))]) >>= \case+      checkSat solvers (assertProps abstRefineDefault [(pathconditions .&& (PNeg branchcondition))]) >>= \case         -- No. The condition must hold         Unsat -> pure $ Case True         -- Yes. Both branches possible         Sat _ -> pure EVM.Types.Unknown         -- Explore both branches in case of timeout         EVM.Solvers.Unknown -> pure EVM.Types.Unknown-        Error e -> error $ "Internal Error: SMT Solver pureed with an error: " <> T.unpack e+        Error e -> internalError $ "SMT Solver pureed with an error: " <> T.unpack e     -- If the query times out, we simply explore both paths     EVM.Solvers.Unknown -> pure EVM.Types.Unknown     Error e -> internalError $ "SMT Solver pureed with an error: " <> T.unpack e
src/EVM/Format.hs view
@@ -30,14 +30,16 @@   , hexByteString   , hexText   , bsToHex+  , showVal   ) where +import Prelude hiding (LT, GT)+ import EVM.Types-import EVM (cheatCode, traceForest, traceForest', traceContext)+import EVM (traceForest, traceForest', traceContext, cheatCode) import EVM.ABI (getAbiSeq, parseTypeName, AbiValue(..), AbiType(..), SolError(..), Indexed(..), Event(..)) import EVM.Dapp (DappContext(..), DappInfo(..), showTraceLocation) import EVM.Expr qualified as Expr-import EVM.Hexdump (prettyHex, paddedShowHex) import EVM.Solidity (SolcContract(..), Method(..), contractName, abiMap)  import Control.Arrow ((>>>))@@ -60,17 +62,18 @@ import Data.Text.Encoding qualified as T import Data.Tree.View (showTree) import Data.Vector (Vector)+import Hexdump (prettyHex) import Numeric (showHex) import Data.ByteString.Char8 qualified as Char8 import Data.ByteString.Base16 qualified as BS16-import Witch (into, unsafeInto)+import Witch (into, unsafeInto, tryFrom)  data Signedness = Signed | Unsigned   deriving (Show)  showDec :: Signedness -> W256 -> Text showDec signed (W256 w)-  | i == into cheatCode = "<hevm cheat address>"+  | Right i' <- tryFrom i, LitAddr i' == cheatCode = "<hevm cheat address>"   | (i :: Integer) == 2 ^ (256 :: Integer) - 1 = "MAX_UINT256"   | otherwise = T.pack (show (i :: Integer))   where@@ -78,7 +81,7 @@           Signed   -> into (signedWord w)           Unsigned -> into w -showWordExact :: W256 -> Text+showWordExact :: Integral i => i -> Text showWordExact w = humanizeInteger (toInteger w)  showWordExplanation :: W256 -> DappInfo -> Text@@ -110,7 +113,7 @@ showAbiValue (AbiAddress addr) =   let dappinfo = ?context.info       contracts = ?context.env-      name = case Map.lookup addr contracts of+      name = case Map.lookup (LitAddr addr) contracts of         Nothing -> ""         Just contract ->           let hash = maybeLitWord contract.codehash@@ -142,7 +145,7 @@  showCall :: (?context :: DappContext) => [AbiType] -> Expr Buf -> Text showCall ts (ConcreteBuf bs) = showValues ts $ ConcreteBuf (BS.drop 4 bs)-showCall _ _ = "<symbolic>"+showCall _ _ = "(<symbolic>)"  showError :: (?context :: DappContext) => Expr Buf -> Text showError (ConcreteBuf bs) =@@ -187,14 +190,14 @@ formatSBinary (AbstractBuf t) = "<" <> t <> " abstract buf>" formatSBinary _ = internalError "formatSBinary: implement me" -showTraceTree :: DappInfo -> VM -> Text+showTraceTree :: DappInfo -> VM s -> Text showTraceTree dapp vm =   let forest = traceForest vm       traces = fmap (fmap (unpack . showTrace dapp (vm.env.contracts))) forest   in pack $ concatMap showTree traces  showTraceTree' :: DappInfo -> Expr End -> Text-showTraceTree' _ (ITE {}) = error "Internal Error: ITE does not contain a trace"+showTraceTree' _ (ITE {}) = internalError "ITE does not contain a trace" showTraceTree' dapp leaf =   let forest = traceForest' leaf       traces = fmap (fmap (unpack . showTrace dapp (traceContext leaf))) forest@@ -203,7 +206,7 @@ unindexed :: [(Text, AbiType, Indexed)] -> [AbiType] unindexed ts = [t | (_, t, NotIndexed) <- ts] -showTrace :: DappInfo -> Map Addr Contract -> Trace -> Text+showTrace :: DappInfo -> Map (Expr EAddr) Contract -> Trace -> Text showTrace dapp env trace =   let ?context = DappContext { info = dapp, env = env }   in let@@ -299,12 +302,12 @@     FrameTrace (CreationContext addr (Lit hash) _ _ ) -> -- FIXME: irrefutable pattern       "create "       <> maybeContractName (preview (ix hash % _2) dapp.solcByHash)-      <> "@" <> pack (show addr)+      <> "@" <> formatAddr addr       <> pos     FrameTrace (CreationContext addr _ _ _ ) ->       "create "       <> "<unknown contract>"-      <> "@" <> pack (show addr)+      <> "@" <> formatAddr addr       <> pos     FrameTrace (CallContext target context _ _ hash abi calldata _ _) ->       let calltype = if target == context@@ -315,8 +318,8 @@         Nothing ->           calltype             <> case target of-                 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D -> "HEVM"-                 _ -> pack (show target)+                 LitAddr 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D -> "HEVM"+                 _ -> formatAddr target             <> pack "::"             <> case Map.lookup (unsafeInto (fromMaybe 0x00 abi)) fullAbiMap of                  Just m  ->@@ -342,6 +345,12 @@             <> "\x1b[0m"             <> pos +formatAddr :: Expr EAddr -> Text+formatAddr = \case+  LitAddr a -> pack (show a)+  SymAddr a -> "symbolic(" <> a <> ")"+  GVar _ -> internalError "Unexpected GVar"+ getAbiTypes :: Text -> [Maybe AbiType] getAbiTypes abi = map (parseTypeName mempty) types   where@@ -427,7 +436,8 @@       , indent 2 $ T.unlines . fmap formatSomeExpr $ args       ]     ]-  MaxIterationsReached pc addr -> T.pack $ "Max Iterations Reached in contract: " <> show addr <> " pc: " <> show pc+  MaxIterationsReached pc addr -> "Max Iterations Reached in contract: " <> formatAddr addr <> " pc: " <> pack (show pc)+  JumpIntoSymbolicCode pc idx -> "Encountered a jump into a potentially symbolic code region while executing initcode. pc: " <> pack (show pc) <> " jump dst: " <> pack (show idx)  formatSomeExpr :: SomeExpr -> Text formatSomeExpr (SomeExpr e) = formatExpr e@@ -436,35 +446,54 @@ formatExpr = go   where     go :: Expr a -> Text-    go = \case-      Lit w -> T.pack $ show w+    go x = T.stripEnd $ case x of+      Lit w -> T.pack $ show (into w :: Integer)+      (Var v) -> "(Var " <> T.pack (show v) <> ")"+      (GVar v) -> "(GVar " <> T.pack (show v) <> ")"       LitByte w -> T.pack $ show w -      ITE c t f -> rstrip . T.unlines $-        [ "(ITE (" <> formatExpr c <> ")"-        , indent 2 (formatExpr t)-        , indent 2 (formatExpr f)+      ITE c t f -> T.unlines+        [ "(ITE"+        , indent 2 $ T.unlines+          [ formatExpr c+          , formatExpr t+          , formatExpr f+          ]         , ")"]       Success asserts _ buf store -> T.unlines-        [ "(Return"+        [ "(Success"         , indent 2 $ T.unlines           [ "Data:"           , indent 2 $ formatExpr buf           , ""-          , "Store:"-          , indent 2 $ formatExpr store+          , "State:"+          , indent 2 $ T.unlines (fmap (\(k,v) ->+              T.unlines+                [ formatExpr k <> ":"+                , indent 2 $ formatExpr v+                ]) (Map.toList store))           , "Assertions:"-          , indent 2 $ T.pack $ show asserts+          , indent 2 . T.unlines $ fmap formatProp asserts           ]         , ")"         ]+      Partial asserts _ err -> T.unlines+        [ "(Partial"+        , indent 2 $ T.unlines+          [ "Reason:"+          , indent 2 $ formatPartial err+          , "Assertions:"+          , indent 2 . T.unlines $ fmap formatProp asserts+          ]+        , ")"+        ]       Failure asserts _ err -> T.unlines         [ "(Failure"         , indent 2 $ T.unlines           [ "Error:"           , indent 2 $ formatError err           , "Assertions:"-          , indent 2 $ T.pack $ show asserts+          , indent 2 . T.unlines $ fmap formatProp asserts           ]         , ")"         ]@@ -489,35 +518,153 @@           ]         , ")"         ]+      ReadByte idx buf -> T.unlines+        [ "(ReadByte"+        , indent 2 $ T.unlines+          [ "idx:"+          , indent 2 $ formatExpr idx+          , "buf: "+          , indent 2 $ formatExpr buf+          ]+        , ")"+        ] -      And a b -> T.unlines-        [ "(And"+      Add a b -> fmt "Add" [a, b]+      Sub a b -> fmt "Sub" [a, b]+      Mul a b -> fmt "Mul" [a, b]+      Div a b -> fmt "Div" [a, b]+      SDiv a b -> fmt "SDiv" [a, b]+      Mod a b -> fmt "Mod" [a, b]+      SMod a b -> fmt "SMod" [a, b]+      AddMod a b c -> fmt "AddMod" [a, b, c]+      MulMod a b c -> fmt "MulMod" [a, b, c]+      Exp a b -> fmt "Exp" [a, b]+      SEx a b -> fmt "SEx" [a, b]+      Min a b -> fmt "Min" [a, b]+      Max a b -> fmt "Max" [a, b]++      LT a b -> fmt "LT" [a, b]+      GT a b -> fmt "GT" [a, b]+      LEq a b -> fmt "LEq" [a, b]+      GEq a b -> fmt "GEq" [a, b]+      SLT a b -> fmt "SLT" [a, b]+      SGT a b -> fmt "SGT" [a, b]+      Eq a b -> fmt "Eq" [a, b]+      EqByte a b -> fmt "EqByte" [a, b]+      IsZero a -> fmt "IsZero" [a]++      And a b -> fmt "And" [a, b]+      Or a b -> fmt "Or" [a, b]+      Xor a b -> fmt "Xor" [a, b]+      Not a -> fmt "Not" [a]+      SHL a b -> fmt "SHL" [a, b]+      SHR a b -> fmt "SHR" [a, b]+      SAR a b -> fmt "SAR" [a, b]++      e@Origin -> T.pack (show e)+      e@Coinbase -> T.pack (show e)+      e@Timestamp -> T.pack (show e)+      e@BlockNumber -> T.pack (show e)+      e@PrevRandao -> T.pack (show e)+      e@GasLimit -> T.pack (show e)+      e@ChainId -> T.pack (show e)+      e@BaseFee -> T.pack (show e)+      e@TxValue -> T.pack (show e)+      e@(Gas {}) -> "(" <> T.pack (show e) <> ")"++      BlockHash a -> fmt "BlockHash" [a]+      Balance a -> fmt "Balance" [a]+      CodeSize a -> fmt "CodeSize" [a]+      CodeHash a -> fmt "CodeHash" [a]+++      JoinBytes zero 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 -> fmt "JoinBytes"+        [ zero+        , 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+        ]++      LogEntry addr dat topics -> T.unlines+        [ "(LogEntry"         , indent 2 $ T.unlines-          [ formatExpr a-          , formatExpr b+          [ "addr:"+          , indent 2 $ formatExpr addr+          , "data:"+          , indent 2 $ formatExpr dat+          , "topics:"+          , indent 2 . T.unlines $ fmap formatExpr topics           ]         , ")"         ] +      a@(SymAddr {}) -> "(" <> T.pack (show a) <> ")"+      LitAddr a -> T.pack (show a)+      WAddr a -> fmt "WAddr" [a]++      BufLength b -> fmt "BufLength" [b]++      C code store bal nonce -> T.unlines+        [ "(Contract"+        , indent 2 $ T.unlines+          [ "code:"+          , indent 2 $ formatCode code+          , "storage:"+          , indent 2 $ formatExpr store+          , "balance:"+          , indent 2 $ formatExpr bal+          , "nonce:"+          , indent 2 $ formatNonce nonce+          ]+        , ")"+        ]+       -- Stores-      SLoad addr slot store -> T.unlines+      SLoad slot storage -> T.unlines         [ "(SLoad"         , indent 2 $ T.unlines-          [ "addr:"-          , indent 2 $ formatExpr addr-          , "slot:"+          [ "slot:"           , indent 2 $ formatExpr slot-          , "store:"-          , indent 2 $ formatExpr store+          , "storage:"+          , indent 2 $ formatExpr storage           ]         , ")"         ]-      SStore addr slot val prev -> T.unlines+      SStore slot val prev -> T.unlines         [ "(SStore"         , indent 2 $ T.unlines-          [ "addr:"-          , indent 2 $ formatExpr addr-          , "slot:"+          [ "slot:"           , indent 2 $ formatExpr slot           , "val:"           , indent 2 $ formatExpr val@@ -525,11 +672,18 @@         , ")"         , formatExpr prev         ]-      ConcreteStore s -> T.unlines-        [ "(ConcreteStore"-        , indent 2 $ T.unlines $ fmap (T.pack . show) $ Map.toList $ fmap (T.pack . show . Map.toList) s-        , ")"-        ]+      AbstractStore a ->+        "(AbstractStore " <> formatExpr a <> ")"+      ConcreteStore s -> if null s+        then "(ConcreteStore <empty>)"+        else T.unlines+          [ "(ConcreteStore"+          , indent 2 $ T.unlines+            [ "vals:"+            , indent 2 $ T.unlines $ fmap (T.pack . show) $ Map.toList s+            ]+          , ")"+          ]        -- Buffers @@ -569,20 +723,67 @@         "" -> "(ConcreteBuf \"\")"         _ -> T.unlines           [ "(ConcreteBuf"-          , indent 2 $ T.pack $ prettyHex 0 bs+          , indent 2 $ T.pack $ prettyHex bs           , ")"           ]-+      b@(AbstractBuf _) -> "(" <> T.pack (show b) <> ")"        -- Hashes-      Keccak b -> T.unlines-       [ "(Keccak"-       , indent 2 $ formatExpr b-       , ")"-       ]+      Keccak b -> fmt "Keccak" [b]+      SHA256 b -> fmt "SHA256" [b]+      where+        fmt nm args = T.unlines+          [ "(" <> nm+          , indent 2 $ T.unlines $ fmap formatExpr args+          , ")"+          ] -      a -> T.pack $ show a+formatProp :: Prop -> Text+formatProp x = T.stripEnd $ case x of+  PEq a b -> fmt "PEq" [a, b]+  PLT a b -> fmt "PLT" [a, b]+  PGT a b -> fmt "PGT" [a, b]+  PGEq a b -> fmt "PGEq" [a, b]+  PLEq a b -> fmt "PLEq" [a, b]+  PNeg a -> fmt' "PNeg" [a]+  PAnd a b -> fmt' "PAnd" [a, b]+  POr a b -> fmt' "POr" [a, b]+  PImpl a b -> fmt' "PImpl" [a, b]+  PBool a -> T.pack (show a)+  where+    fmt nm args = T.unlines+      [ "(" <> nm+      , indent 2 $ T.unlines $ fmap formatExpr args+      , ")"+      ]+    fmt' nm args = T.unlines+      [ "(" <> nm+      , indent 2 $ T.unlines $ fmap formatProp args+      , ")"+      ] +formatNonce :: Maybe W64 -> Text+formatNonce = \case+  Just w -> T.pack $ show w+  Nothing -> "symbolic"++formatCode :: ContractCode -> Text+formatCode = \case+  UnknownCode _ -> "Unknown"+  InitCode c d -> T.unlines+    [ "(InitCode"+    , indent 2 $ T.unlines+      [ "code: " <> T.pack (bsToHex c)+      , "data: " <> formatExpr d+      ]+    , ")"+    ]+  RuntimeCode (ConcreteRuntimeCode c)+    -> "(RuntimeCode " <> T.pack (bsToHex c) <> ")"+  RuntimeCode (SymbolicRuntimeCode bs)+    -> "(RuntimeCode " <> T.pack (show (fmap formatExpr bs)) <> ")"++ strip0x :: ByteString -> ByteString strip0x bs = if "0x" `Char8.isPrefixOf` bs then Char8.drop 2 bs else bs @@ -604,4 +805,7 @@ bsToHex :: ByteString -> String bsToHex bs = concatMap (paddedShowHex 2) (BS.unpack bs) -+showVal :: AbiValue -> Text+showVal (AbiBytes _ bs) = formatBytes bs+showVal (AbiAddress addr) = T.pack  . show $ addr+showVal v = T.pack . show $ v
− src/EVM/Hexdump.hs
@@ -1,129 +0,0 @@--- Copyright (c) 2011, Galois Inc.  All rights reserved.--- --- Redistribution and use in source and binary forms, with or without--- modification, are permitted provided that the following conditions are met:--- ---     * Redistributions of source code must retain the above copyright---       notice, this list of conditions and the following disclaimer.--- ---     * Redistributions in binary form must reproduce the above---       copyright notice, this list of conditions and the following---       disclaimer in the documentation and/or other materials provided---       with the distribution.--- ---     * Neither the name of Trevor Elliott nor the names of other---       contributors may be used to endorse or promote products derived---       from this software without specific prior written permission.--- --- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS--- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT--- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR--- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT--- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,--- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT--- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,--- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY--- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT--- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE--- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.-----module EVM.Hexdump (prettyHex, simpleHex, paddedShowHex) where--import Data.ByteString                       (ByteString)-import qualified Data.ByteString       as B  (length, unpack)-import qualified Data.ByteString.Char8 as B8 (unpack)-import Data.Char                             (isAscii, isControl)-import Data.List                             (intercalate, transpose, unfoldr)-import Numeric                               (showHex)--byteWidth :: Num a => a-byteWidth    = 2  -- Width of an padded 'Word8'--numWordBytes :: Num a => a-numWordBytes = 4  -- Number of bytes to group into a 32-bit word---- |'prettyHex' renders a 'ByteString' as a multi-line 'String' complete with--- addressing, hex digits, and ASCII representation.------ Sample output------ @Length: 100 (0x64) bytes---0000:   4b c1 ad 8a  5b 47 d7 57  48 64 e7 cc  5e b5 2f 6e   K...[G.WHd..^./n---0010:   c5 b3 a4 73  44 3b 97 53  99 2d 54 e7  1b 2f 91 12   ...sD;.S.-T../..---0020:   c8 1a ff c4  3b 2b 72 ea  97 e2 9f e2  93 ad 23 79   ....;+r.......#y---0030:   e8 0f 08 54  02 14 fa 09  f0 2d 34 c9  08 6b e1 64   ...T.....-4..k.d---0040:   d1 c5 98 7e  d6 a1 98 e2  97 da 46 68  4e 60 11 15   ...~......FhN`..---0050:   d8 32 c6 0b  70 f5 2e 76  7f 8d f2 3b  ed de 90 c6   .2..p..v...;....---0060:   93 12 9c e1                                          ....@-prettyHex :: Int -> ByteString -> String-prettyHex hexDisplayWidth bs = unlines (header : body)- where-  numLineWords    = 4  -- Number of words to group onto a line-  addressWidth    = 4  -- Minimum width of a padded address-  numLineBytes    = numLineWords * numWordBytes -- Number of bytes on a line-  replacementChar = '.' -- 'Char' to use for non-printable characters--  header = "Length: " ++ show    (B.length bs)-        ++ " (0x"     ++ showHex (B.length bs) ") bytes"--  body = map (intercalate "   ")-       $ transpose [mkLineNumbers, mkHexDisplay bs, mkAsciiDump bs]--  mkHexDisplay-    = padLast hexDisplayWidth-    . map (intercalate "  ") . group numLineWords-    . map (intercalate " ")  . group numWordBytes-    . map (paddedShowHex byteWidth)-    . B.unpack--  mkAsciiDump = group numLineBytes . cleanString . B8.unpack--  cleanString = map go-   where-    go x | isWorthPrinting x = x-         | otherwise         = replacementChar--  mkLineNumbers = [paddedShowHex addressWidth (x * numLineBytes) ++ ":"-                   | x <- [0 .. (B.length bs - 1) `div` numLineBytes] ]--  padLast w [x]         = [x ++ replicate (w - length x) ' ']-  padLast w (x:xs)      = x : padLast w xs-  padLast _ []          = []---- |'paddedShowHex' displays a number in hexidecimal and pads the number--- with 0 so that it has a minimum length of @w@.-paddedShowHex :: (Show a, Integral a) => Int -> a -> String-paddedShowHex w n = pad ++ str-    where-     str = showHex n ""-     pad = replicate (w - length str) '0'----- |'simpleHex' converts a 'ByteString' to a 'String' showing the octets--- grouped in 32-bit words.------ Sample output------ @4b c1 ad 8a  5b 47 d7 57@-simpleHex :: ByteString -> String-simpleHex = intercalate "  "-          . map (intercalate " ") . group numWordBytes-          . map (paddedShowHex byteWidth)-          . B.unpack---- |'isWorthPrinting' returns 'True' for non-control ascii characters.--- These characters will all fit in a single character when rendered.-isWorthPrinting :: Char -> Bool-isWorthPrinting x = isAscii x && not (isControl x)---- |'group' breaks up a list into sublists of size @n@. The last group--- may be smaller than @n@ elements. When @n@ less not positive the--- list is returned as one sublist.-group :: Int -> [a] -> [[a]]-group n- | n <= 0    = (:[])- | otherwise = unfoldr go-  where-    go [] = Nothing-    go xs = Just (splitAt n xs)
src/EVM/Keccak.hs view
@@ -4,7 +4,7 @@     Module: EVM.Keccak     Description: Expr passes to determine Keccak assumptions -}-module EVM.Keccak (keccakAssumptions) where+module EVM.Keccak (keccakAssumptions, keccakCompute) where  import Control.Monad.State import Data.Set (Set)@@ -12,8 +12,9 @@  import EVM.Traversals import EVM.Types+import EVM.Expr -data BuilderState = BuilderState+newtype BuilderState = BuilderState   { keccaks :: Set (Expr EWord) }   deriving (Show) @@ -50,12 +51,12 @@       combine' xs (xcomb:acc)  minProp :: Expr EWord -> Prop-minProp k@(Keccak _) = PGT k (Lit 50)+minProp k@(Keccak _) = PGT k (Lit 256) minProp _ = internalError "expected keccak expression"  injProp :: (Expr EWord, Expr EWord) -> Prop injProp (k1@(Keccak b1), k2@(Keccak b2)) =-  POr (PEq b1 b2) (PNeg (PEq k1 k2))+  POr ((b1 .== b2) .&& (bufLength b1 .== bufLength b2)) (PNeg (PEq k1 k2)) injProp _ = internalError "expected keccak expression"  -- Takes a list of props, find all keccak occurences and generates two kinds of assumptions:@@ -72,3 +73,24 @@      injectivity = fmap injProp $ combine (Set.toList st.keccaks)     minValue = fmap minProp (Set.toList st.keccaks)++compute :: forall a. Expr a -> [Prop]+compute = \case+  e@(Keccak buf) -> do+    let b = simplify buf+    case keccak b of+      lit@(Lit _) -> [PEq e lit]+      _ -> []+  _ -> []++computeKeccakExpr :: forall a. Expr a -> [Prop]+computeKeccakExpr e = foldExpr compute [] e++computeKeccakProp :: Prop -> [Prop]+computeKeccakProp p = foldProp compute [] p++keccakCompute :: [Prop] -> [Expr Buf] -> [Expr Storage] -> [Prop]+keccakCompute ps buf stores =+  concatMap computeKeccakProp ps <>+  concatMap computeKeccakExpr buf <>+  concatMap computeKeccakExpr stores
− src/EVM/Patricia.hs
@@ -1,226 +0,0 @@-module EVM.Patricia where--import EVM.RLP-import EVM.Types--import Control.Monad.Free-import Control.Monad.State-import Data.ByteString (ByteString)-import Data.ByteString qualified as BS-import Data.Foldable (toList)-import Data.List (stripPrefix)-import Data.Map qualified as Map-import Data.Sequence (Seq)-import Data.Sequence qualified as Seq-import Witch (into)--data KV k v a-  = Put k v a-  | Get k (v -> a)-  deriving (Functor)--newtype DB k v a = DB (Free (KV k v) a)-  deriving (Functor, Applicative, Monad)--insertDB :: k -> v -> DB k v ()-insertDB k v = DB $ liftF $ Put k v ()--lookupDB :: k -> DB k v v-lookupDB k = DB $ liftF $ Get k id---- Collapses a series of puts and gets down to the monad of your choice-runDB :: Monad m-      => (k -> v -> m ()) -- ^ The 'put' function for our desired monad-      -> (k -> m v)       -- ^ The 'get' function for the same monad-      -> DB k v a         -- ^ The puts and gets to execute-      -> m a-runDB putt gett (DB ops) = go ops-  where-    go (Pure a) = pure a-    go (Free (Put k v next)) = putt k v >> go next-    go (Free (Get k handler)) = gett k >>= go . handler--type Path = [Nibble]--data Ref = Hash ByteString | Literal Node-  deriving (Eq)--instance Show Ref where-  show (Hash d) = show (ByteStringS d)-  show (Literal n) = show n--data Node = Empty-          | Shortcut Path (Either Ref ByteString)-          | Full (Seq Ref) ByteString-  deriving (Show, Eq)---- the function HP from Appendix C of yellow paper-encodePath :: Path -> Bool -> ByteString-encodePath p isTerminal | even (length p)-  = packNibbles $ Nibble flag : Nibble 0 : p-                        | otherwise-  = packNibbles $ Nibble (flag + 1) : p-  where flag  = if isTerminal then 2 else 0--rlpRef :: Ref -> RLP-rlpRef (Hash d) = BS d-rlpRef (Literal n) = rlpNode n--rlpNode :: Node -> RLP-rlpNode Empty = BS mempty-rlpNode (Shortcut path (Right val)) = List [BS $ encodePath path True, BS val]-rlpNode (Shortcut path (Left  ref)) = List [BS $ encodePath path False, rlpRef ref]-rlpNode (Full refs val) = List $ toList (fmap rlpRef refs) <> [BS val]--type NodeDB = DB ByteString Node--instance Show (NodeDB Node) where-  show = show--putNode :: Node -> NodeDB Ref-putNode node =-  let bytes = rlpencode $ rlpNode node-      digest = word256Bytes $ keccak' bytes-  in if BS.length bytes < 32-    then pure $ Literal node-    else do-      insertDB digest node-      pure $ Hash digest--getNode :: Ref -> NodeDB Node-getNode (Hash d) = lookupDB d-getNode (Literal n) = pure n--lookupPath :: Ref -> Path -> NodeDB ByteString-lookupPath root path = getNode root >>= getVal path--getVal :: Path -> Node -> NodeDB ByteString-getVal _ Empty = pure BS.empty-getVal path (Shortcut nodePath ref) =-  case (stripPrefix nodePath path, ref) of-    (Just [], Right value) -> pure value-    (Just remaining, Left key) -> lookupPath key remaining-    _ -> pure BS.empty--getVal [] (Full _ val) = pure val-getVal (p:ps) (Full refs _) = lookupPath (refs `Seq.index` (into p)) ps--emptyRef :: Ref-emptyRef = Literal Empty--emptyRefs :: Seq Ref-emptyRefs = Seq.replicate 16 emptyRef--addPrefix :: Path -> Node -> NodeDB Node-addPrefix _ Empty = pure Empty-addPrefix [] node = pure node-addPrefix path (Shortcut p v) = pure $ Shortcut (path <> p) v-addPrefix path n = Shortcut path . Left <$> putNode n--insertRef :: Ref -> Path -> ByteString -> NodeDB Ref-insertRef ref p val = do root <- getNode ref-                         newNode <- if val == BS.empty-                                    then delete root p-                                    else update root p val-                         putNode newNode--update :: Node -> Path -> ByteString -> NodeDB Node-update Empty p new  = pure $ Shortcut p (Right new)-update (Full refs _) [] new = pure (Full refs new)-update (Full refs old) (p:ps) new = do-  newRef <- insertRef (refs `Seq.index` (into p)) ps new-  pure $ Full (Seq.update (into p) newRef refs) old-update (Shortcut (o:os) (Right old)) [] new = do-  newRef <- insertRef emptyRef os old-  pure $ Full (Seq.update (into o) newRef emptyRefs) new-update (Shortcut [] (Right old)) (p:ps) new = do-  newRef <- insertRef emptyRef ps new-  pure $ Full (Seq.update (into p) newRef emptyRefs) old-update (Shortcut [] (Right _)) [] new =-  pure $ Shortcut [] (Right new)-update (Shortcut (o:os) to) (p:ps) new | o == p-  = update (Shortcut os to) ps new >>= addPrefix [o]-                                       | otherwise = do-  oldRef <- case to of-              (Left ref)  -> getNode ref >>= addPrefix os >>= putNode-              (Right val) -> insertRef emptyRef os val-  newRef <- insertRef emptyRef ps new-  let refs = Seq.update (into p) newRef $ Seq.update (into o) oldRef emptyRefs-  pure $ Full refs BS.empty-update (Shortcut (o:os) (Left ref)) [] new = do-  newRef <- getNode ref >>= addPrefix os >>= putNode-  pure $ Full (Seq.update (into o) newRef emptyRefs) new-update (Shortcut cut (Left ref)) ps new = do-  newRef <- insertRef ref ps new-  pure $ Shortcut cut (Left newRef)--delete :: Node -> Path -> NodeDB Node-delete Empty _ = pure Empty-delete (Shortcut [] (Right _)) [] = pure Empty-delete n@(Shortcut [] (Right _)) _ = pure n-delete (Shortcut [] (Left ref)) p = do node <- getNode ref-                                       delete node p-delete n@(Shortcut _ _) [] = pure n-delete n@(Shortcut (o:os) to) (p:ps) | p == o-  = delete (Shortcut os to) ps >>= addPrefix [o]-                                     | otherwise-  = pure n-delete (Full refs _) [] | refs == emptyRefs-  = pure Empty-                        | otherwise-  = pure (Full refs BS.empty)-delete (Full refs val) (p:ps) = do-  newRef <- insertRef (refs `Seq.index` (into p)) ps BS.empty-  let newRefs = Seq.update (into p) newRef refs-      nonEmpties = filter (\(_, ref) -> ref /= emptyRef) $ zip [0..15] $ toList newRefs-  case (nonEmpties, BS.null val) of-    ([], True)         -> pure Empty-    ([(n, ref)], True) -> getNode ref >>= addPrefix [Nibble n]-    _                  -> pure $ Full newRefs val--insert :: Ref -> ByteString -> ByteString -> NodeDB Ref-insert ref key = insertRef ref (unpackNibbles key)--lookupIn :: Ref -> ByteString -> NodeDB ByteString-lookupIn ref bs = lookupPath ref $ unpackNibbles bs--type Trie = StateT Ref NodeDB--runTrie :: DB ByteString ByteString a -> Trie a-runTrie = runDB putDB getDB-  where-    putDB key val = do-      ref <- get-      newRef <- lift $ insert ref key val-      put newRef-    getDB key = do-      ref <- get-      lift $ lookupIn ref key--type MapDB k v a = StateT (Map.Map k v) Maybe a--runMapDB :: Ord k => DB k v a -> MapDB k v a-runMapDB = runDB putDB getDB-  where-    getDB key = do-      mmap <- get-      lift $ Map.lookup key mmap-    putDB key value = do-      mmap <- get-      let newMap = Map.insert key value mmap-      put newMap---insertValues :: [(ByteString, ByteString)] -> Maybe Ref-insertValues inputs =-  let trie = runTrie $ mapM_ insertPair inputs-      mapDB = runMapDB $ runStateT trie (Literal Empty)-      result = snd <$> evalStateT mapDB Map.empty-      insertPair (key, value) = insertDB key value-  in result--calcRoot :: [(ByteString, ByteString)] -> Maybe ByteString-calcRoot vs = case insertValues vs of-     Just (Hash b) -> Just b-     Just (Literal n) -> Just $ word256Bytes $ keccak' $ rlpencode $ rlpNode n-     Nothing -> Nothing
src/EVM/SMT.hs view
@@ -12,15 +12,16 @@ import Control.Monad import Data.Containers.ListUtils (nubOrd) import Data.ByteString (ByteString)-import Data.Bifunctor (second) import Data.ByteString qualified as BS import Data.List qualified as List import Data.List.NonEmpty (NonEmpty((:|))) import Data.List.NonEmpty qualified as NonEmpty import Data.String.Here-import Data.Maybe (fromJust)-import Data.Map (Map)-import Data.Map qualified as Map+import Data.Maybe (fromJust, fromMaybe)+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Set (Set)+import Data.Set qualified as Set import Data.Word (Word8) import Data.Text.Lazy (Text) import Data.Text qualified as TS@@ -30,11 +31,12 @@ import Language.SMT2.Syntax (Symbol, SpecConstant(..), GeneralRes(..), Term(..), QualIdentifier(..), Identifier(..), Sort(..), Index(..), VarBinding(..)) import Numeric (readHex, readBin) import Witch (into, unsafeInto)+import Control.Monad.State  import EVM.CSE import EVM.Expr (writeByte, bufLengthEnv, containsNode, bufLength, minLength, inRange) import EVM.Expr qualified as Expr-import EVM.Keccak (keccakAssumptions)+import EVM.Keccak (keccakAssumptions, keccakCompute) import EVM.Traversals import EVM.Types @@ -42,22 +44,30 @@ -- ** Encoding ** ----------------------------------------------------------------------------------  --- variable names in SMT that we want to get values for+-- | Data that we need to construct a nice counterexample data CexVars = CexVars-  { calldata     :: [Text]-  , buffers      :: Map Text (Expr EWord) -- buffers and guesses at their maximum size-  , storeReads   :: [(Expr EWord, Expr EWord)] -- a list of relevant store reads+  { -- | variable names that we need models for to reconstruct calldata+    calldata     :: [Text]+    -- | symbolic address names+  , addrs        :: [Text]+    -- | buffer names and guesses at their maximum size+  , buffers      :: Map Text (Expr EWord)+    -- | reads from abstract storage+  , storeReads   :: Map (Expr EAddr) (Set (Expr EWord))+    -- | the names of any block context variables   , blockContext :: [Text]+    -- | the names of any tx context variables   , txContext    :: [Text]   }   deriving (Eq, Show)  instance Semigroup CexVars where-  (CexVars a b c d e) <> (CexVars a2 b2 c2 d2 e2) = CexVars (a <> a2) (b <> b2) (c <> c2) (d <> d2) (e <> e2)+  (CexVars a b c d e f) <> (CexVars a2 b2 c2 d2 e2 f2) = CexVars (a <> a2) (b <> b2) (c <> c2) (d <> d2) (e <> e2) (f <> f2)  instance Monoid CexVars where     mempty = CexVars       { calldata = mempty+      , addrs = mempty       , buffers = mempty       , storeReads = mempty       , blockContext = mempty@@ -83,13 +93,19 @@ -- | a final post shrinking cex, buffers here are all represented as concrete bytestrings data SMTCex = SMTCex   { vars :: Map (Expr EWord) W256+  , addrs :: Map (Expr EAddr) Addr   , buffers :: Map (Expr Buf) BufModel-  , store :: Map W256 (Map W256 W256)+  , store :: Map (Expr EAddr) (Map W256 W256)   , blockContext :: Map (Expr EWord) W256   , txContext :: Map (Expr EWord) W256   }   deriving (Eq, Show) ++-- | Used for abstraction-refinement of the SMT formula. Contains assertions that make our query fully precise. These will be added to the assertion stack if we get `sat` with the abstracted query.+newtype RefinementEqs = RefinementEqs [Builder]+  deriving (Eq, Show, Monoid, Semigroup)+ flattenBufs :: SMTCex -> Maybe SMTCex flattenBufs cex = do   bs <- mapM collapse cex.buffers@@ -109,17 +125,17 @@ getVar :: SMTCex -> TS.Text -> W256 getVar cex name = fromJust $ Map.lookup (Var name) cex.vars -data SMT2 = SMT2 [Builder] CexVars+data SMT2 = SMT2 [Builder] RefinementEqs CexVars   deriving (Eq, Show)  instance Semigroup SMT2 where-  (SMT2 a b) <> (SMT2 a2 b2) = SMT2 (a <> a2) (b <> b2)+  (SMT2 a (RefinementEqs b) c) <> (SMT2 a2 (RefinementEqs b2) c2) = SMT2 (a <> a2) (RefinementEqs (b <> b2)) (c <> c2)  instance Monoid SMT2 where-  mempty = SMT2 mempty mempty+  mempty = SMT2 mempty mempty mempty  formatSMT2 :: SMT2 -> Text-formatSMT2 (SMT2 ls _) = T.unlines (fmap toLazyText ls)+formatSMT2 (SMT2 ls _ _) = T.unlines (fmap toLazyText ls)  -- | Reads all intermediate variables from the builder state and produces SMT declaring them as constants declareIntermediates :: BufEnv -> StoreEnv -> SMT2@@ -128,113 +144,175 @@       encBs = Map.mapWithKey encodeBuf bufs       sorted = List.sortBy compareFst $ Map.toList $ encSs <> encBs       decls = fmap snd sorted-  in SMT2 ([fromText "; intermediate buffers & stores"] <> decls) mempty+      smt2 = (SMT2 [fromText "; intermediate buffers & stores"] mempty mempty):decls+  in  foldr (<>) (SMT2[""] mempty mempty) smt2   where     compareFst (l, _) (r, _) = compare l r     encodeBuf n expr =-      "(define-const buf" <> (fromString . show $ n) <> " Buf " <> exprToSMT expr <> ")\n" <> encodeBufLen n expr+      SMT2 ["(define-const buf" <> (fromString . show $ n) <> " Buf " <> exprToSMT expr <> ")\n"]  mempty mempty <> encodeBufLen n expr     encodeBufLen n expr =-      "(define-const buf" <> (fromString . show $ n) <>"_length" <> " (_ BitVec 256) " <> exprToSMT (bufLengthEnv bufs True expr) <> ")"+      SMT2 ["(define-const buf" <> (fromString . show $ n) <>"_length" <> " (_ BitVec 256) " <> exprToSMT (bufLengthEnv bufs True expr) <> ")"] mempty mempty     encodeStore n expr =-       "(define-const store" <> (fromString . show $ n) <> " Storage " <> exprToSMT expr <> ")"+      SMT2 ["(define-const store" <> (fromString . show $ n) <> " Storage " <> exprToSMT expr <> ")"] mempty mempty -assertProps :: [Prop] -> SMT2-assertProps ps =-  let encs = map propToSMT ps_elim+data AbstState = AbstState+  { words :: Map (Expr EWord) Int+  , count :: Int+  }+  deriving (Show)++abstractAwayProps :: AbstRefineConfig -> [Prop] -> ([Prop], AbstState)+abstractAwayProps abstRefineConfig ps = runState (mapM abstrAway ps) (AbstState mempty 0)+  where+    abstrAway :: Prop -> State AbstState Prop+    abstrAway prop = mapPropM go prop+    go :: Expr a -> State AbstState (Expr a)+    go x = case x of+        e@(Mod{})       | abstRefineConfig.arith  -> abstrExpr e+        e@(SMod{})      | abstRefineConfig.arith  -> abstrExpr e+        e@(MulMod{})    | abstRefineConfig.arith  -> abstrExpr e+        e@(AddMod{})    | abstRefineConfig.arith  -> abstrExpr e+        e@(Mul{})       | abstRefineConfig.arith  -> abstrExpr e+        e@(Div{})       | abstRefineConfig.arith  -> abstrExpr e+        e@(SDiv {})     | abstRefineConfig.arith  -> abstrExpr e+        e@(ReadWord {}) | abstRefineConfig.mem -> abstrExpr e+        e -> pure e+        where+            abstrExpr e = do+              s <- get+              case Map.lookup e s.words of+                Just v -> pure (Var (TS.pack ("abst_" ++ show v)))+                Nothing -> do+                  let+                    next = s.count+                    bs' = Map.insert e next s.words+                  put $ s{words=bs', count=next+1}+                  pure $ Var (TS.pack ("abst_" ++ show next))++smt2Line :: Builder -> SMT2+smt2Line txt = SMT2 [txt] mempty mempty++assertProps :: AbstRefineConfig -> [Prop] -> SMT2+assertProps conf ps = assertPropsNoSimp conf (Expr.simplifyProps ps)++-- Note: we need a version that does NOT call simplify or simplifyProps,+-- because we make use of it to verify the correctness of our simplification+-- passes through property-based testing.+assertPropsNoSimp :: AbstRefineConfig -> [Prop] -> SMT2+assertPropsNoSimp abstRefineConfig ps =+  let encs = map propToSMT psElimAbst+      abstSMT = map propToSMT abstProps       intermediates = declareIntermediates bufs stores in   prelude-  <> (declareBufs ps_elim bufs stores)-  <> SMT2 [""] mempty+  <> (declareAbstractStores abstractStores)+  <> smt2Line ""+  <> (declareAddrs addresses)+  <> smt2Line ""+  <> (declareBufs psElim bufs stores)+  <> smt2Line ""   <> (declareVars . nubOrd $ foldl (<>) [] allVars)-  <> SMT2 [""] mempty+  <> smt2Line ""   <> (declareFrameContext . nubOrd $ foldl (<>) [] frameCtx)-  <> SMT2 [""] mempty+  <> smt2Line ""   <> (declareBlockContext . nubOrd $ foldl (<>) [] blockCtx)-  <> SMT2 [""] mempty+  <> smt2Line ""   <> intermediates-  <> SMT2 [""] mempty+  <> smt2Line ""   <> keccakAssumes   <> readAssumes-  <> SMT2 [""] mempty-  <> SMT2 (fmap (\p -> "(assert " <> p <> ")") encs) mempty-  <> SMT2 [] mempty{ storeReads = storageReads }+  <> smt2Line ""+  <> SMT2 (fmap (\p -> "(assert " <> p <> ")") encs) mempty mempty+  <> SMT2 mempty (RefinementEqs $ fmap (\p -> "(assert " <> p <> ")") abstSMT) mempty+  <> SMT2 mempty mempty mempty { storeReads = storageReads }    where-    (ps_elim, bufs, stores) = eliminateProps ps+    (psElim, bufs, stores) = eliminateProps ps+    (psElimAbst, abst@(AbstState abstExprToInt _)) = if abstRefineConfig.arith || abstRefineConfig.mem+      then abstractAwayProps abstRefineConfig psElim+      else (psElim, AbstState mempty 0) -    allVars = fmap referencedVars' ps_elim <> fmap referencedVars bufVals <> fmap referencedVars storeVals-    frameCtx = fmap referencedFrameContext' ps_elim <> fmap referencedFrameContext bufVals <> fmap referencedFrameContext storeVals-    blockCtx = fmap referencedBlockContext' ps_elim <> fmap referencedBlockContext bufVals <> fmap referencedBlockContext storeVals+    abstProps = map toProp (Map.toList abstExprToInt)+      where+      toProp :: (Expr EWord, Int) -> Prop+      toProp (e, num) = PEq e (Var (TS.pack ("abst_" ++ (show num)))) +    allVars = fmap referencedVars psElim <> fmap referencedVars bufVals <> fmap referencedVars storeVals <> [abstrVars abst]+    frameCtx = fmap referencedFrameContext psElim <> fmap referencedFrameContext bufVals <> fmap referencedFrameContext storeVals+    blockCtx = fmap referencedBlockContext psElim <> fmap referencedBlockContext bufVals <> fmap referencedBlockContext storeVals++    abstrVars :: AbstState -> [Builder]+    abstrVars (AbstState b _) = map ((\v->fromString ("abst_" ++ show v)) . snd) (Map.toList b)+     bufVals = Map.elems bufs     storeVals = Map.elems stores--    storageReads = nubOrd $ concatMap findStorageReads ps+    storageReads = Map.unionsWith (<>) $ fmap findStorageReads ps+    abstractStores = Set.toList $ Set.unions (fmap referencedAbstractStores ps)+    addresses = Set.toList $ Set.unions (fmap referencedWAddrs ps)      keccakAssumes-      = SMT2 ["; keccak assumptions"] mempty-      <> SMT2 (fmap (\p -> "(assert " <> propToSMT p <> ")") (keccakAssumptions ps_elim bufVals storeVals)) mempty+      = smt2Line "; keccak assumptions"+      <> SMT2 (fmap (\p -> "(assert " <> propToSMT p <> ")") (keccakAssumptions psElim bufVals storeVals)) mempty mempty+      <> smt2Line "; keccak computations"+      <> SMT2 (fmap (\p -> "(assert " <> propToSMT p <> ")") (keccakCompute psElim bufVals storeVals)) mempty mempty      readAssumes-      = SMT2 ["; read assumptions"] mempty-        <> SMT2 (fmap (\p -> "(assert " <> propToSMT p <> ")") (assertReads ps_elim bufs stores)) mempty--referencedBufsGo :: Expr a -> [Builder]-referencedBufsGo = \case-  AbstractBuf s -> [fromText s]-  _ -> []--referencedBufs :: Expr a -> [Builder]-referencedBufs expr = nubOrd $ foldExpr referencedBufsGo [] expr--referencedBufs' :: Prop -> [Builder]-referencedBufs' prop = nubOrd $ foldProp referencedBufsGo [] prop--referencedVarsGo :: Expr a -> [Builder]-referencedVarsGo = \case-  Var s -> [fromText s]-  _ -> []--referencedVars :: Expr a -> [Builder]-referencedVars expr = nubOrd $ foldExpr referencedVarsGo [] expr--referencedVars' :: Prop -> [Builder]-referencedVars' prop = nubOrd $ foldProp referencedVarsGo [] prop--referencedFrameContextGo :: Expr a -> [(Builder, [Prop])]-referencedFrameContextGo = \case-  CallValue a -> [(fromLazyText $ T.append "callvalue_" (T.pack . show $ a), [])]-  Caller a -> [(fromLazyText $ T.append "caller_" (T.pack . show $ a), [inRange 160 (Caller a)])]-  Address a -> [(fromLazyText $ T.append "address_" (T.pack . show $ a), [inRange 160 (Address a)])]-  Balance {} -> internalError "TODO: BALANCE"-  SelfBalance {} -> internalError "TODO: SELFBALANCE"-  Gas {} -> internalError "TODO: GAS"-  _ -> []+      = smt2Line "; read assumptions"+        <> SMT2 (fmap (\p -> "(assert " <> propToSMT p <> ")") (assertReads psElim bufs stores)) mempty mempty -referencedFrameContext :: Expr a -> [(Builder, [Prop])]-referencedFrameContext expr = nubOrd $ foldExpr referencedFrameContextGo [] expr+referencedAbstractStores :: TraversableTerm a => a -> Set Builder+referencedAbstractStores term = foldTerm go mempty term+  where+    go = \case+      AbstractStore s -> Set.singleton (storeName s)+      _ -> mempty -referencedFrameContext' :: Prop -> [(Builder, [Prop])]-referencedFrameContext' prop = nubOrd $ foldProp referencedFrameContextGo [] prop+referencedWAddrs :: TraversableTerm a => a -> Set Builder+referencedWAddrs term = foldTerm go mempty term+  where+    go = \case+      WAddr(a@(SymAddr _)) -> Set.singleton (formatEAddr a)+      _ -> mempty +referencedBufs :: TraversableTerm a => a -> [Builder]+referencedBufs expr = nubOrd $ foldTerm go [] expr+  where+    go :: Expr a -> [Builder]+    go = \case+      AbstractBuf s -> [fromText s]+      _ -> [] -referencedBlockContextGo :: Expr a -> [(Builder, [Prop])]-referencedBlockContextGo = \case-  Origin -> [("origin", [inRange 160 Origin])]-  Coinbase -> [("coinbase", [inRange 160 Coinbase])]-  Timestamp -> [("timestamp", [])]-  BlockNumber -> [("blocknumber", [])]-  PrevRandao -> [("prevrandao", [])]-  GasLimit -> [("gaslimit", [])]-  ChainId -> [("chainid", [])]-  BaseFee -> [("basefee", [])]-  _ -> []+referencedVars :: TraversableTerm a => a -> [Builder]+referencedVars expr = nubOrd $ foldTerm go [] expr+  where+    go :: Expr a -> [Builder]+    go = \case+      Var s -> [fromText s]+      _ -> [] -referencedBlockContext :: Expr a -> [(Builder, [Prop])]-referencedBlockContext expr = nubOrd $ foldExpr referencedBlockContextGo [] expr+referencedFrameContext :: TraversableTerm a => a -> [(Builder, [Prop])]+referencedFrameContext expr = nubOrd $ foldTerm go [] expr+  where+    go :: Expr a -> [(Builder, [Prop])]+    go = \case+      TxValue -> [(fromString "txvalue", [])]+      v@(Balance a) -> [(fromString "balance_" <> formatEAddr a, [PLT v (Lit $ 2 ^ (96 :: Int))])]+      Gas {} -> internalError "TODO: GAS"+      _ -> [] -referencedBlockContext' :: Prop -> [(Builder, [Prop])]-referencedBlockContext' prop = nubOrd $ foldProp referencedBlockContextGo [] prop+referencedBlockContext :: TraversableTerm a => a -> [(Builder, [Prop])]+referencedBlockContext expr = nubOrd $ foldTerm go [] expr+  where+    go :: Expr a -> [(Builder, [Prop])]+    go = \case+      Origin -> [("origin", [inRange 160 Origin])]+      Coinbase -> [("coinbase", [inRange 160 Coinbase])]+      Timestamp -> [("timestamp", [])]+      BlockNumber -> [("blocknumber", [])]+      PrevRandao -> [("prevrandao", [])]+      GasLimit -> [("gaslimit", [])]+      ChainId -> [("chainid", [])]+      BaseFee -> [("basefee", [])]+      _ -> []  -- | This function overapproximates the reads from the abstract -- storage. Potentially, it can return locations that do not read a@@ -242,18 +320,18 @@ -- the store (e.g, SLoad addr idx (SStore addr idx val AbstractStore)). -- However, we expect that most of such reads will have been -- simplified away.-findStorageReads :: Prop -> [(Expr EWord, Expr EWord)]-findStorageReads = foldProp go []+findStorageReads :: Prop -> Map (Expr EAddr) (Set (Expr EWord))+findStorageReads p = Map.fromListWith (<>) $ foldProp go mempty p   where-    go :: Expr a -> [(Expr EWord, Expr EWord)]+    go :: Expr a -> [(Expr EAddr, Set (Expr EWord))]     go = \case-      SLoad addr slot storage -> [(addr, slot) | containsNode isAbstractStore storage]+      SLoad slot store ->+        [((fromMaybe (error $ "Internal Error: could not extract address from: " <> show store) (Expr.getAddr store)), Set.singleton slot) | containsNode isAbstractStore store]       _ -> [] -    isAbstractStore AbstractStore = True+    isAbstractStore (AbstractStore _) = True     isAbstractStore _ = False - findBufferAccess :: TraversableTerm a => [a] -> [(Expr EWord, Expr EWord, Expr Buf)] findBufferAccess = foldl (foldTerm go) mempty   where@@ -295,7 +373,7 @@     allReads = nubOrd $ findBufferAccess props <> findBufferAccess (Map.elems benv) <> findBufferAccess (Map.elems senv)     -- we can have buffers that are not read from but are still mentioned via BufLength in some branch condition     -- we assign a default read hint of 4 to start with in these cases (since in most cases we will need at least 4 bytes to produce a counterexample)-    allBufs = Map.fromList . fmap (, Lit 4) . fmap toLazyText . nubOrd . concat $ fmap referencedBufs' props <> fmap referencedBufs (Map.elems benv) <> fmap referencedBufs (Map.elems senv)+    allBufs = Map.fromList . fmap (, Lit 4) . fmap toLazyText . nubOrd . concat $ fmap referencedBufs props <> fmap referencedBufs (Map.elems benv) <> fmap referencedBufs (Map.elems senv)      bufMap = Map.unionWith Expr.max (foldl addBound mempty allReads) allBufs @@ -317,7 +395,7 @@  -- | Returns an SMT2 object with all buffers referenced from the input props declared, and with the appropriate cex extraction metadata attached declareBufs :: [Prop] -> BufEnv -> StoreEnv -> SMT2-declareBufs props bufEnv storeEnv = SMT2 ("; buffers" : fmap declareBuf allBufs <> ("; buffer lengths" : fmap declareLength allBufs)) cexvars+declareBufs props bufEnv storeEnv = SMT2 ("; buffers" : fmap declareBuf allBufs <> ("; buffer lengths" : fmap declareLength allBufs)) mempty cexvars   where     cexvars = (mempty :: CexVars){ buffers = discoverMaxReads props bufEnv storeEnv }     allBufs = fmap fromLazyText $ Map.keys cexvars.buffers@@ -326,30 +404,41 @@  -- Given a list of variable names, create an SMT2 object with the variables declared declareVars :: [Builder] -> SMT2-declareVars names = SMT2 (["; variables"] <> fmap declare names) cexvars+declareVars names = SMT2 (["; variables"] <> fmap declare names) mempty cexvars   where     declare n = "(declare-const " <> n <> " (_ BitVec 256))"     cexvars = (mempty :: CexVars){ calldata = fmap toLazyText names } +-- Given a list of variable names, create an SMT2 object with the variables declared+declareAddrs :: [Builder] -> SMT2+declareAddrs names = SMT2 (["; symbolic addresseses"] <> fmap declare names) mempty cexvars+  where+    declare n = "(declare-const " <> n <> " Addr)"+    cexvars = (mempty :: CexVars){ addrs = fmap toLazyText names }  declareFrameContext :: [(Builder, [Prop])] -> SMT2-declareFrameContext names = SMT2 (["; frame context"] <> concatMap declare names) cexvars+declareFrameContext names = SMT2 (["; frame context"] <> concatMap declare names) mempty cexvars   where     declare (n,props) = [ "(declare-const " <> n <> " (_ BitVec 256))" ]                         <> fmap (\p -> "(assert " <> propToSMT p <> ")") props     cexvars = (mempty :: CexVars){ txContext = fmap (toLazyText . fst) names } +declareAbstractStores :: [Builder] -> SMT2+declareAbstractStores names = SMT2 (["; abstract base stores"] <> fmap declare names) mempty mempty+  where+    declare n = "(declare-const " <> n <> " Storage)"  declareBlockContext :: [(Builder, [Prop])] -> SMT2-declareBlockContext names = SMT2 (["; block context"] <> concatMap declare names) cexvars+declareBlockContext names = SMT2 (["; block context"] <> concatMap declare names) mempty cexvars   where     declare (n, props) = [ "(declare-const " <> n <> " (_ BitVec 256))" ]                          <> fmap (\p -> "(assert " <> propToSMT p <> ")") props     cexvars = (mempty :: CexVars){ blockContext = fmap (toLazyText . fst) names } - prelude :: SMT2-prelude =  (flip SMT2) mempty $ fmap (fromLazyText . T.drop 2) . T.lines $ [i|+prelude =  SMT2 src mempty mempty+  where+  src = fmap (fromLazyText . T.drop 2) . T.lines $ [i|   ; logic   ; TODO: this creates an error when used with z3?   ;(set-logic QF_AUFBV)@@ -358,15 +447,15 @@   ; types   (define-sort Byte () (_ BitVec 8))   (define-sort Word () (_ BitVec 256))+  (define-sort Addr () (_ BitVec 160))   (define-sort Buf () (Array Word Byte)) -  ; address -> slot -> value-  (define-sort Storage () (Array Word (Array Word Word)))+  ; slot -> value+  (define-sort Storage () (Array Word Word))    ; hash functions   (declare-fun keccak (Buf) Word)   (declare-fun sha256 (Buf) Word)-   (define-fun max ((a (_ BitVec 256)) (b (_ BitVec 256))) (_ BitVec 256) (ite (bvult a b) b a))    ; word indexing@@ -442,9 +531,6 @@     ))))))))))))))))))))))))))))))))   ) -  ; buffers-  (define-const emptyBuf Buf ((as const Buf) #b00000000))-   (define-fun readWord ((idx Word) (buf Buf)) Word     (concat       (select buf idx)@@ -521,6 +607,7 @@    ; block context   (declare-fun blockhash (Word) Word)+  (declare-fun codesize (Addr) Word)    ; macros   (define-fun signext ( (b Word) (val Word)) Word@@ -555,23 +642,14 @@     (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))))))))))))))))))))))))))))))))--  ; storage-  (declare-const abstractStore Storage)-  (define-const emptyStore Storage ((as const Storage) ((as const (Array (_ BitVec 256) (_ BitVec 256))) #x0000000000000000000000000000000000000000000000000000000000000000)))--  (define-fun sstore ((addr Word) (key Word) (val Word) (storage Storage)) Storage (store storage addr (store (select storage addr) key val)))--  (define-fun sload ((addr Word) (key Word) (storage Storage)) Word (select (select storage addr) key))   |] - exprToSMT :: Expr a -> Builder exprToSMT = \case   Lit w -> fromLazyText $ "(_ bv" <> (T.pack $ show (into w :: Integer)) <> " 256)"   Var s -> fromText s-  GVar (BufVar n) -> fromLazyText $ "buf" <> (T.pack . show $ n)-  GVar (StoreVar n) -> fromLazyText $ "store" <> (T.pack . show $ n)+  GVar (BufVar n) -> fromString $ "buf" <> (show n)+  GVar (StoreVar n) -> fromString $ "store" <> (show n)   JoinBytes     z o two three four five six seven     eight nine ten eleven twelve thirteen fourteen fifteen@@ -660,14 +738,16 @@     let enc = exprToSMT a in     "(sha256 " <> enc <> ")" -  CallValue a -> fromLazyText $ T.append "callvalue_" (T.pack . show $ a)-  Caller a -> fromLazyText $ T.append "caller_" (T.pack . show $ a)-  Address a -> fromLazyText $ T.append "address_" (T.pack . show $ a)+  TxValue -> fromString "txvalue"+  Balance a -> fromString "balance_" <> formatEAddr a    Origin ->  "origin"   BlockHash a ->     let enc = exprToSMT a in     "(blockhash " <> enc <> ")"+  CodeSize a ->+    let enc = exprToSMT a in+    "(codesize " <> enc <> ")"   Coinbase -> "coinbase"   Timestamp -> "timestamp"   BlockNumber -> "blocknumber"@@ -676,6 +756,9 @@   ChainId -> "chainid"   BaseFee -> "basefee" +  a@(SymAddr _) -> formatEAddr a+  WAddr(a@(SymAddr _)) -> "(concat (_ bv0 96)" `sp` exprToSMT a `sp` ")"+   LitByte b -> fromLazyText $ "(_ bv" <> T.pack (show (into b :: Integer)) <> " 8)"   IndexWord idx w -> case idx of     Lit n -> if n >= 0 && n < 32@@ -686,7 +769,7 @@     _ -> op2 "indexWord" idx w   ReadByte idx src -> op2 "select" src idx -  ConcreteBuf "" -> "emptyBuf"+  ConcreteBuf "" -> "((as const Buf) #b00000000)"   ConcreteBuf bs -> writeBytes bs mempty   AbstractBuf s -> fromText s   ReadWord idx prev -> op2 "readWord" idx prev@@ -705,16 +788,14 @@     "(writeWord " <> encIdx `sp` encVal `sp` encPrev <> ")"   CopySlice srcIdx dstIdx size src dst ->     copySlice srcIdx dstIdx size (exprToSMT src) (exprToSMT dst)-  EmptyStore -> "emptyStore"   ConcreteStore s -> encodeConcreteStore s-  AbstractStore -> "abstractStore"-  SStore addr idx val prev ->-    let encAddr = exprToSMT addr-        encIdx = exprToSMT idx+  AbstractStore a -> storeName a+  SStore idx val prev ->+    let encIdx = exprToSMT idx         encVal = exprToSMT val         encPrev = exprToSMT prev in-    "(sstore" `sp` encAddr `sp` encIdx `sp` encVal `sp` encPrev <> ")"-  SLoad addr idx store -> op3 "sload" addr idx store+    "(store" `sp` encPrev `sp` encIdx `sp` encVal <> ")"+  SLoad idx store -> op2 "select" store idx    a -> internalError $ "TODO: implement: " <> show a   where@@ -725,11 +806,6 @@       let aenc = exprToSMT a           benc = exprToSMT b in       "(" <> op `sp` aenc `sp` benc <> ")"-    op3 op a b c =-      let aenc = exprToSMT a-          benc = exprToSMT b-          cenc = exprToSMT c in-      "(" <> op `sp` aenc `sp` benc `sp` cenc <> ")"     op2CheckZero op a b =       let aenc = exprToSMT a           benc = exprToSMT b in@@ -825,20 +901,28 @@           idxSMT = exprToSMT . Lit . unsafeInto $ idx         in (idx + 1, "(store " <> inner `sp` idxSMT `sp` byteSMT <> ")") -encodeConcreteStore :: Map W256 (Map W256 W256) -> Builder-encodeConcreteStore s = foldl encodeWrite "emptyStore" writes+encodeConcreteStore :: Map W256 W256 -> Builder+encodeConcreteStore s = foldl encodeWrite "((as const Storage) #x0000000000000000000000000000000000000000000000000000000000000000)" (Map.toList s)   where-    asList = fmap (second Map.toList) $ Map.toList s-    writes = concatMap (\(addr, ws) -> fmap (\(k, v) -> (addr, k, v)) ws) asList-    encodeWrite prev (addr, key, val) = let-        encAddr = exprToSMT (Lit addr)+    encodeWrite prev (key, val) = let         encKey = exprToSMT (Lit key)         encVal = exprToSMT (Lit val)-      in "(sstore " <> encAddr `sp` encKey `sp` encVal `sp` prev <> ")"+      in "(store " <> prev `sp` encKey `sp` encVal <> ")" +storeName :: Expr EAddr -> Builder+storeName a = fromString ("baseStore_") <> formatEAddr a +formatEAddr :: Expr EAddr -> Builder+formatEAddr = \case+  LitAddr a -> fromString ("litaddr_" <> show a)+  SymAddr a -> fromText ("symaddr_" <> a)+  GVar _ -> internalError "Unexpected GVar"++ -- ** Cex parsing ** -------------------------------------------------------------------------------- +parseAddr :: SpecConstant -> Addr+parseAddr = parseSC  parseW256 :: SpecConstant -> W256 parseW256 = parseSC@@ -860,6 +944,12 @@ parseVar :: TS.Text -> Expr EWord parseVar = Var +parseEAddr :: TS.Text -> Expr EAddr+parseEAddr name+  | Just a <- TS.stripPrefix "litaddr_" name = LitAddr (read (TS.unpack a))+  | Just a <- TS.stripPrefix "symaddr_" name = SymAddr a+  | otherwise = internalError $ "cannot parse: " <> show name+ parseBlockCtx :: TS.Text -> Expr EWord parseBlockCtx "origin" = Origin parseBlockCtx "coinbase" = Coinbase@@ -871,33 +961,35 @@ parseBlockCtx "basefee" = BaseFee parseBlockCtx t = internalError $ "cannot parse " <> (TS.unpack t) <> " into an Expr" -parseFrameCtx :: TS.Text -> Expr EWord-parseFrameCtx name = case TS.unpack name of-  ('c':'a':'l':'l':'v':'a':'l':'u':'e':'_':frame) -> CallValue (read frame)-  ('c':'a':'l':'l':'e':'r':'_':frame) -> Caller (read frame)-  ('a':'d':'d':'r':'e':'s':'s':'_':frame) -> Address (read frame)-  t -> internalError $ "cannot parse " <> t <> " into an Expr"+parseTxCtx :: TS.Text -> Expr EWord+parseTxCtx name+  | name == "txvalue" = TxValue+  | Just a <- TS.stripPrefix "balance_" name = Balance (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+ getVars :: (TS.Text -> Expr EWord) -> (Text -> IO Text) -> [TS.Text] -> IO (Map (Expr EWord) W256)-getVars parseFn getVal names = Map.mapKeys parseFn <$> foldM getOne mempty names-  where-    getOne :: Map TS.Text W256 -> TS.Text -> IO (Map TS.Text W256)-    getOne 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 parseW256 sc-                 else internalError "solver did not return model for requested value"-          r -> parseErr r-      pure $ Map.insert name val acc+getVars parseName getVal names = Map.mapKeys parseName <$> foldM (getOne parseW256 getVal) mempty names +getOne :: (SpecConstant -> a) -> (Text -> IO Text) -> Map TS.Text a -> TS.Text -> IO (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+  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)@@ -973,35 +1065,35 @@                             <> " in environment mapping"           p -> parseErr p -getStore :: (Text -> IO Text) -> [(Expr EWord, Expr EWord)] -> IO (Map W256 (Map W256 W256))-getStore getVal sreads = do-  raw <- getVal "abstractStore"-  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 == "abstractStore"-                then interpret2DArray 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-  foldM (\m (addr, slot) -> do-            addr' <- queryValue getVal addr-            slot' <- queryValue getVal slot-            pure $ addElem addr' slot' m fun) Map.empty sreads--  where--    addElem :: W256 -> W256 -> Map W256 (Map W256 W256) -> (W256 -> W256 -> W256) -> Map W256 (Map W256 W256)-    addElem addr slot store fun =-      case Map.lookup addr store of-        Just m -> Map.insert addr (Map.insert slot (fun addr slot) m) store-        Nothing -> Map.insert addr (Map.singleton slot (fun addr slot)) store+-- | Takes a Map containing all reads from a store with an abstract base, as+-- well as the conrete part of the storage prestate and returns a fully+-- concretized storage+getStore+  :: (Text -> IO Text)+  -> Map (Expr EAddr) (Set (Expr EWord))+  -> IO (Map (Expr EAddr) (Map W256 W256))+getStore getVal abstractReads =+  fmap Map.fromList $ forM (Map.toList abstractReads) $ \(addr, slots) -> do+    let name = toLazyText (storeName addr)+    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+    store <- foldM (\m slot -> do+      slot' <- queryValue getVal slot+      pure $ Map.insert slot' (fun slot') m) Map.empty slots+    pure (addr, store) +-- | Ask the solver to give us the concrete value of an arbitrary abstract word queryValue :: (Text -> IO Text) -> Expr EWord -> IO W256 queryValue _ (Lit w) = pure w queryValue getVal w = do@@ -1014,8 +1106,6 @@         _ -> internalError $ "cannot parse model for: " <> show w     r -> parseErr r -- -- | Interpret an N-dimensional array as a value of type a. -- Parameterized by an interpretation function for array values. interpretNDArray :: (Map Symbol Term -> Term -> a) -> (Map Symbol Term) -> Term -> (W256 -> a)@@ -1057,7 +1147,3 @@         Just t -> interpretW256 env t         Nothing -> internalError "unknown identifier, cannot parse array"     interpretW256 _ t = internalError $ "cannot parse array value. Unexpected term: " <> (show t)---- | Interpret an 2-dimensional array as a function-interpret2DArray :: (Map Symbol Term) -> Term -> (W256 -> W256 -> W256)-interpret2DArray = interpretNDArray interpret1DArray
src/EVM/Solidity.hs view
@@ -1,13 +1,10 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DerivingStrategies #-}-{-# LANGUAGE QuasiQuotes #-}  module EVM.Solidity   ( solidity   , solcRuntime-  , solidity'-  , yul'   , yul   , yulRuntime   , JumpType (..)@@ -72,11 +69,10 @@ import Data.Maybe import Data.Semigroup import Data.Sequence (Seq)-import Data.String.Here qualified as Here import Data.Text (pack, intercalate) import Data.Text qualified as T import Data.Text.Encoding (encodeUtf8, decodeUtf8)-import Data.Text.IO (readFile, writeFile)+import Data.Text.IO (readFile) import Data.Vector (Vector) import Data.Vector qualified as Vector import Data.Word (Word8)@@ -84,8 +80,6 @@ import Prelude hiding (readFile, writeFile) import System.FilePattern.Directory import System.FilePath.Posix-import System.IO hiding (readFile, writeFile)-import System.IO.Temp import System.Process import Text.Read (readMaybe) import Witch (unsafeInto)@@ -357,38 +351,41 @@         pure (Right (BuildOutput contracts sourceCache))  yul :: Text -> Text -> IO (Maybe ByteString)-yul contract src = do-  (json, path) <- yul' src-  let f = (json ^?! key "contracts") ^?! key (Key.fromText path)-      c = f ^?! key (Key.fromText $ if T.null contract then "object" else contract)+yul contractName src = do+  json <- solc Yul src+  let f = (json ^?! key "contracts") ^?! key (Key.fromText "hevm.sol")+      c = f ^?! key (Key.fromText $ if T.null contractName then "object" else contractName)       bytecode = c ^?! key "evm" ^?! key "bytecode" ^?! key "object" % _String-  pure $ toCode <$> (Just bytecode)+  pure $ (toCode contractName) <$> (Just bytecode)  yulRuntime :: Text -> Text -> IO (Maybe ByteString)-yulRuntime contract src = do-  (json, path) <- yul' src-  let f = (json ^?! key "contracts") ^?! key (Key.fromText path)-      c = f ^?! key (Key.fromText $ if T.null contract then "object" else contract)+yulRuntime contractName src = do+  json <- solc Yul src+  let f = (json ^?! key "contracts") ^?! key (Key.fromText "hevm.sol")+      c = f ^?! key (Key.fromText $ if T.null contractName then "object" else contractName)       bytecode = c ^?! key "evm" ^?! key "deployedBytecode" ^?! key "object" % _String-  pure $ toCode <$> (Just bytecode)+  pure $ (toCode contractName) <$> (Just bytecode)  solidity :: Text -> Text -> IO (Maybe ByteString) solidity contract src = do-  (json, path) <- solidity' src+  json <- solc Solidity src   let (Contracts sol, _, _) = fromJust $ readStdJSON json-  pure $ Map.lookup (path <> ":" <> contract) sol <&> (.creationCode)+  pure $ Map.lookup ("hevm.sol:" <> contract) sol <&> (.creationCode)  solcRuntime :: Text -> Text -> IO (Maybe ByteString) solcRuntime contract src = do-  (json, path) <- solidity' src-  let (Contracts sol, _, _) = fromJust $ readStdJSON json-  pure $ Map.lookup (path <> ":" <> contract) sol <&> (.runtimeCode)+  json <- solc Solidity src+  case readStdJSON json of+    Just (Contracts sol, _, _) -> pure $ Map.lookup ("hevm.sol:" <> contract) sol <&> (.runtimeCode)+    Nothing -> internalError $ "unable to parse solidity output:\n" <> (T.unpack json)  functionAbi :: Text -> IO Method functionAbi f = do-  (json, path) <- solidity' ("contract ABI { function " <> f <> " public {}}")-  let (Contracts sol, _, _) = fromJust $ readStdJSON json-  case Map.toList $ (fromJust (Map.lookup (path <> ":ABI") sol)).abiMap of+  json <- solc Solidity ("contract ABI { function " <> f <> " public {}}")+  let (Contracts sol, _, _) = fromMaybe+                                (internalError . T.unpack $ "unable to parse solc output:\n" <> json)+                                (readStdJSON json)+  case Map.toList $ (fromJust (Map.lookup "hevm.sol:ABI" sol)).abiMap of      [(_,b)] -> pure b      _ -> internalError "unexpected abi format" @@ -404,12 +401,16 @@ readFoundryJSON :: Text -> Text -> Maybe (Contracts, Asts, Sources) readFoundryJSON contractName json = do   runtime <- json ^? key "deployedBytecode"-  runtimeCode <- toCode . strip0x'' <$> runtime ^? key "object" % _String-  runtimeSrcMap <- makeSrcMaps =<< runtime ^? key "sourceMap" % _String+  runtimeCode <- (toCode contractName) . strip0x'' <$> runtime ^? key "object" % _String+  runtimeSrcMap <- case runtime ^? key "sourceMap" % _String of+    Nothing -> makeSrcMaps ""+    smap -> makeSrcMaps =<< smap    creation <- json ^? key "bytecode"-  creationCode <- toCode . strip0x'' <$> creation ^? key "object" % _String-  creationSrcMap <- makeSrcMaps =<< creation ^? key "sourceMap" % _String+  creationCode <- (toCode contractName) . strip0x'' <$> creation ^? key "object" % _String+  creationSrcMap <- case creation ^? key "sourceMap" % _String of+    Nothing -> makeSrcMaps ""+    smap -> makeSrcMaps =<< smap    ast <- json ^? key "ast"   path <- ast ^? key "absolutePath" % _String@@ -430,7 +431,7 @@         , runtimeSrcmap       = runtimeSrcMap         , creationSrcmap      = creationSrcMap         , constructorInputs   = mkConstructor abi-        , storageLayout       = mempty -- TODO: foundry doesn't expose this?+        , storageLayout       = mkStorageLayout $ json ^? key "storageLayout"         , immutableReferences = mempty -- TODO: foundry doesn't expose this?         }   pure ( Contracts $ Map.singleton (path <> ":" <> contractName) contract@@ -460,10 +461,11 @@     h s (c, x) =       let         evmstuff = x ^?! key "evm"+        sc = s <> ":" <> c         runtime = evmstuff ^?! key "deployedBytecode"         creation =  evmstuff ^?! key "bytecode"-        theRuntimeCode = toCode $ fromMaybe "" $ runtime ^? key "object" % _String-        theCreationCode = toCode $ fromMaybe "" $ creation ^? key "object" % _String+        theRuntimeCode = (toCode sc) $ fromMaybe "" $ runtime ^? key "object" % _String+        theCreationCode = (toCode sc) $ fromMaybe "" $ creation ^? key "object" % _String         srcContents :: Maybe (HMap.HashMap Text Text)         srcContents = do metadata <- x ^? key "metadata" % _String                          srcs <- KeyMap.toHashMapText <$> metadata ^? key "sources" % _Object@@ -472,14 +474,14 @@                            (HMap.filter (isJust . preview (key "content")) srcs)         abis = force ("abi key not found in " <> show x) $           toList <$> x ^? key "abi" % _Array-      in (s <> ":" <> c, (SolcContract {+      in (sc, (SolcContract {         runtimeCode      = theRuntimeCode,         creationCode     = theCreationCode,         runtimeCodehash  = keccak' (stripBytecodeMetadata theRuntimeCode),         creationCodehash = keccak' (stripBytecodeMetadata theCreationCode),         runtimeSrcmap    = force "srcmap-runtime" (makeSrcMaps (runtime ^?! key "sourceMap" % _String)),         creationSrcmap   = force "srcmap" (makeSrcMaps (creation ^?! key "sourceMap" % _String)),-        contractName = s <> ":" <> c,+        contractName = sc,         constructorInputs = mkConstructor abis,         abiMap        = mkAbiMap abis,         eventMap      = mkEventMap abis,@@ -508,8 +510,8 @@     f x = Map.fromList . HMap.toList $ HMap.mapWithKey g x     g s x =       let-        theRuntimeCode = toCode (x ^?! key "bin-runtime" % _String)-        theCreationCode = toCode (x ^?! key "bin" % _String)+        theRuntimeCode = (toCode s) (x ^?! key "bin-runtime" % _String)+        theCreationCode = (toCode s) (x ^?! key "bin" % _String)         abis = toList $ case (x ^?! key "abi") ^? _Array of                  Just v -> v                                       -- solc >= 0.8                  Nothing -> (x ^?! key "abi" % _String) ^?! _Array -- solc <  0.8@@ -646,87 +648,19 @@ containsLinkerHole :: Text -> Bool containsLinkerHole = regexMatches "__\\$[a-z0-9]{34}\\$__" -toCode :: Text -> ByteString-toCode t = case BS16.decodeBase16 (encodeUtf8 t) of+toCode :: Text -> Text -> ByteString+toCode contractName t = case BS16.decodeBase16 (encodeUtf8 t) of   Right d -> d   Left e -> if containsLinkerHole t-            then error "Error: unlinked libraries detected in bytecode"-            else error $ T.unpack e--solidity' :: Text -> IO (Text, Text)-solidity' src = withSystemTempFile "hevm.sol" $ \path handle -> do-  hClose handle-  writeFile path ("//SPDX-License-Identifier: UNLICENSED\n" <> "pragma solidity ^0.8.6;\n" <> src)-  writeFile (path <> ".json")-    [Here.i|-    {-      "language": "Solidity",-      "sources": {-        ${path}: {-          "urls": [-            ${path}-          ]-        }-      },-      "settings": {-        "outputSelection": {-          "*": {-            "*": [-              "metadata",-              "evm.bytecode",-              "evm.deployedBytecode",-              "abi",-              "storageLayout",-              "evm.bytecode.sourceMap",-              "evm.bytecode.linkReferences",-              "evm.bytecode.generatedSources",-              "evm.deployedBytecode.sourceMap",-              "evm.deployedBytecode.linkReferences",-              "evm.deployedBytecode.generatedSources"-            ],-            "": [-              "ast"-            ]-          }-        }-      }-    }-    |]-  x <- pack <$>-    readProcess-      "solc"-      ["--allow-paths", path, "--standard-json", (path <> ".json")]-      ""-  pure (x, pack path)--yul' :: Text -> IO (Text, Text)-yul' src = withSystemTempFile "hevm.yul" $ \path handle -> do-  hClose handle-  writeFile path src-  writeFile (path <> ".json")-    [Here.i|-    {-      "language": "Yul",-      "sources": { ${path}: { "urls": [ ${path} ] } },-      "settings": { "outputSelection": { "*": { "*": ["*"], "": [ "*" ] } } }-    }-    |]-  x <- pack <$>-    readProcess-      "solc"-      ["--allow-paths", path, "--standard-json", (path <> ".json")]-      ""-  pure (x, pack path)+            then error $ T.unpack ("Error toCode: unlinked libraries detected in bytecode, in " <> contractName)+            else error $ T.unpack ("Error toCode:" <> e <> ", in " <> contractName)  solc :: Language -> Text -> IO Text solc lang src =-  withSystemTempFile "hevm.sol" $ \path handle -> do-    hClose handle-    writeFile path (stdjson lang src)-    T.pack <$> readProcess-      "solc"-      ["--standard-json", path]-      ""+  T.pack <$> readProcess+    "solc"+    ["--standard-json"]+    (T.unpack $ stdjson lang src)  data Language = Solidity | Yul   deriving (Show)
src/EVM/Solvers.hs view
@@ -112,21 +112,31 @@       _ <- forkIO $ runTask task inst avail       orchestrate queue avail -    runTask (Task (SMT2 cmds cexvars) r) inst availableInstances = do+    runTask (Task (SMT2 cmds (RefinementEqs refineEqs) cexvars) r) inst availableInstances = do       -- reset solver and send all lines of provided script-      out <- sendScript inst (SMT2 ("(reset)" : cmds) cexvars)+      out <- sendScript inst (SMT2 ("(reset)" : cmds) mempty mempty)       case out of         -- if we got an error then return it         Left e -> writeChan r (Error ("error while writing SMT to solver: " <> T.toStrict e))         -- otherwise call (check-sat), parse the result, and send it down the result channel         Right () -> do           sat <- sendLine inst "(check-sat)"-          res <- case sat of-            "sat" -> Sat <$> getModel inst cexvars-            "unsat" -> pure Unsat-            "timeout" -> pure Unknown-            "unknown" -> pure Unknown-            _ -> pure . Error $ T.toStrict $ "Unable to parse solver output: " <> sat+          res <- do+              case sat of+                "unsat" -> pure Unsat+                "timeout" -> pure Unknown+                "unknown" -> pure Unknown+                "sat" -> if null refineEqs then Sat <$> getModel inst cexvars+                         else do+                              _ <- sendScript inst (SMT2 refineEqs mempty mempty)+                              sat2 <- sendLine inst "(check-sat)"+                              case sat2 of+                                "unsat" -> pure Unsat+                                "timeout" -> pure Unknown+                                "unknown" -> pure Unknown+                                "sat" -> Sat <$> getModel inst cexvars+                                _ -> pure . Error $ T.toStrict $ "Unable to parse solver output: " <> sat2+                _ -> pure . Error $ T.toStrict $ "Unable to parse solver output: " <> sat           writeChan r res        -- put the instance back in the list of available instances@@ -147,11 +157,12 @@     getRaw :: IO SMTCex     getRaw = do       vars <- getVars parseVar (getValue inst) (fmap T.toStrict cexvars.calldata)+      addrs <- getAddrs parseEAddr (getValue inst) (fmap T.toStrict cexvars.addrs)       buffers <- getBufs (getValue inst) (Map.keys cexvars.buffers)       storage <- getStore (getValue inst) cexvars.storeReads       blockctx <- getVars parseBlockCtx (getValue inst) (fmap T.toStrict cexvars.blockContext)-      txctx <- getVars parseFrameCtx (getValue inst) (fmap T.toStrict cexvars.txContext)-      pure $ SMTCex vars buffers storage blockctx txctx+      txctx <- getVars parseTxCtx (getValue inst) (fmap T.toStrict cexvars.txContext)+      pure $ SMTCex vars addrs buffers storage blockctx txctx      -- sometimes the solver might give us back a model for the max read index     -- that is too high to be a useful cex (e.g. in the case of reads from a@@ -182,17 +193,17 @@     shrinkBuf buf hint = do       let encBound = "(_ bv" <> (T.pack $ show (into hint :: Integer)) <> " 256)"       sat <- liftIO $ do-        sendLine' inst "(push)"-        sendLine' inst $ "(assert (bvule " <> buf <> "_length " <> encBound <> "))"+        checkCommand inst "(push)"+        checkCommand inst $ "(assert (bvule " <> buf <> "_length " <> encBound <> "))"         sendLine inst "(check-sat)"       case sat of         "sat" -> do           model <- liftIO getRaw           put model         "unsat" -> do-          liftIO $ sendLine' inst "(pop)"+          liftIO $ checkCommand inst "(pop)"           shrinkBuf buf (if hint == 0 then hint + 1 else hint * 2)-        _ -> internalError "SMT solver returned unexpected result (neither sat/unsat), which HEVM currently cannot handle"+        e -> internalError $ "Unexpected solver output: " <> (T.unpack e)      -- Collapses the abstract description of a models buffers down to a bytestring     mkConcrete :: SMTCex -> SMTCex@@ -224,6 +235,8 @@   CVC5 ->     [ "--lang=smt"     , "--produce-models"+    , "--print-success"+    , "--interactive"     , "--tlimit-per=" <> mkTimeout timeout     ]   Custom _ -> []@@ -235,10 +248,12 @@   (Just stdin, Just stdout, Just stderr, process) <- createProcess cmd   hSetBuffering stdin (BlockBuffering (Just 1000000))   let solverInstance = SolverInstance solver stdin stdout stderr process+   case solver of     CVC5 -> pure solverInstance     _ -> do       _ <- sendLine' solverInstance $ "(set-option :timeout " <> mkTimeout timeout <> ")"+      _ <- sendLine solverInstance "(set-option :print-success true)"       pure solverInstance  -- | Cleanly shutdown a running solver instnace@@ -247,10 +262,24 @@  -- | Sends a list of commands to the solver. Returns the first error, if there was one. sendScript :: SolverInstance -> SMT2 -> IO (Either Text ())-sendScript solver (SMT2 cmds _) = do-  sendLine' solver (splitSExpr $ fmap toLazyText cmds)-  pure $ Right()+sendScript solver (SMT2 cmds _ _) = do+  let sexprs = splitSExpr $ fmap toLazyText cmds+  go sexprs+  where+    go [] = pure $ Right ()+    go (c:cs) = do+      out <- sendCommand solver c+      case out of+        "success" -> go cs+        e -> pure $ Left $ "Solver returned an error:\n" <> e <> "\nwhile sending the following line: " <> c +checkCommand :: SolverInstance -> Text -> IO ()+checkCommand inst cmd = do+  res <- sendCommand inst cmd+  case res of+    "success" -> pure ()+    _ -> internalError $ "Unexpected solver output: " <> T.unpack res+ -- | Sends a single command to the solver, returns the first available line from the output buffer sendCommand :: SolverInstance -> Text -> IO Text sendCommand inst cmd = do@@ -302,11 +331,11 @@  -- From a list of lines, take each separate SExpression and put it in -- its own list, after removing comments.-splitSExpr :: [Text] -> Text+splitSExpr :: [Text] -> [Text] splitSExpr ls =   -- split lines, strip comments, and append everything to a single line   let text = T.intercalate " " $ T.takeWhile (/= ';') <$> concatMap T.lines ls in-  T.unlines $ filter (/= "") $ go text []+  filter (/= "") $ go text []   where     go "" acc = reverse acc     go text acc =
src/EVM/Stepper.hs view
@@ -11,7 +11,6 @@   , ask   , evm   , evmIO-  , entering   , enter   , interpret   )@@ -24,60 +23,58 @@ -- as the framework for monadic interpretation.  import Control.Monad.Operational (Program, ProgramViewT(..), ProgramView, singleton, view)-import Control.Monad.State.Strict (StateT, execState, runState, runStateT)+import Control.Monad.State.Strict (execStateT, runStateT, get) import Data.Text (Text)  import EVM qualified import EVM.Exec qualified import EVM.Fetch qualified as Fetch import EVM.Types+import Control.Monad.ST (stToIO, RealWorld)  -- | The instruction type of the operational monad-data Action a where+data Action s a where    -- | Keep executing until an intermediate result is reached-  Exec :: Action VMResult--  -- | Keep executing until an intermediate state is reached-  Run :: Action VM+  Exec :: Action s (VMResult s)    -- | Wait for a query to be resolved-  Wait :: Query -> Action ()+  Wait :: Query s -> Action s ()    -- | Multiple things can happen-  Ask :: Choose -> Action ()+  Ask :: Choose s -> Action s ()    -- | Embed a VM state transformation-  EVM  :: EVM a -> Action a+  EVM  :: EVM s a -> Action s a    -- | Perform an IO action-  IOAct :: StateT VM IO a -> Action a -- they should all just be this?+  IOAct :: IO a -> Action s a  -- | Type alias for an operational monad of @Action@-type Stepper a = Program Action a+type Stepper s a = Program (Action s) a  -- Singleton actions -exec :: Stepper VMResult+exec :: Stepper s (VMResult s) exec = singleton Exec -run :: Stepper VM-run = singleton Run+run :: Stepper s (VM s)+run = exec >> evm get -wait :: Query -> Stepper ()+wait :: Query s -> Stepper s () wait = singleton . Wait -ask :: Choose -> Stepper ()+ask :: Choose s -> Stepper s () ask = singleton . Ask -evm :: EVM a -> Stepper a+evm :: EVM s a -> Stepper s a evm = singleton . EVM -evmIO :: StateT VM IO a -> Stepper a+evmIO :: IO a -> Stepper s a evmIO = singleton . IOAct  -- | Run the VM until final result, resolving all queries-execFully :: Stepper (Either EvmError (Expr Buf))+execFully :: Stepper s (Either EvmError (Expr Buf)) execFully =   exec >>= \case     HandleEffect (Query q) ->@@ -92,7 +89,7 @@       -> internalError $ "partial execution encountered during concrete execution: " <> show x  -- | Run the VM until its final state-runFully :: Stepper VM+runFully :: Stepper s (VM s) runFully = do   vm <- run   case vm.result of@@ -104,41 +101,33 @@     Just _ ->       pure vm -entering :: Text -> Stepper a -> Stepper a-entering t stepper = do-  evm (EVM.pushTrace (EntryTrace t))-  x <- stepper-  evm EVM.popTrace-  pure x--enter :: Text -> Stepper ()+enter :: Text -> Stepper s () enter t = evm (EVM.pushTrace (EntryTrace t)) -interpret :: Fetch.Fetcher -> VM -> Stepper a -> IO a+interpret :: Fetch.Fetcher RealWorld -> VM RealWorld -> Stepper RealWorld a -> IO a interpret fetcher vm = eval . view   where-    eval :: ProgramView Action a -> IO a+    eval :: ProgramView (Action RealWorld) a -> IO a     eval (Return x) = pure x     eval (action :>>= k) =       case action of-        Exec ->-          let (r, vm') = runState EVM.Exec.exec vm-          in interpret fetcher vm' (k r)-        Run ->-          let vm' = execState EVM.Exec.run vm-          in interpret fetcher vm' (k vm')-        Wait (PleaseAskSMT (Lit c) _ continue) ->-          let (r, vm') = runState (continue (Case (c > 0))) vm-          in interpret fetcher vm' (k r)+        Exec -> do+          (r, vm') <- stToIO $ runStateT EVM.Exec.exec vm+          interpret fetcher vm' (k r)+        Wait (PleaseAskSMT (Lit c) _ continue) -> do+          (r, vm') <- stToIO $ runStateT (continue (Case (c > 0))) vm+          interpret fetcher vm' (k r)+        Wait (PleaseAskSMT c _ _) ->+          error $ "cannot handle symbolic branch conditions in this interpreter: " <> show c         Wait q -> do           m <- fetcher q-          let vm' = execState m vm+          vm' <- stToIO $ execStateT m vm           interpret fetcher vm' (k ())         Ask _ ->           internalError "cannot make choices with this interpreter"         IOAct m -> do-          (r, vm') <- runStateT m vm+          r <- m+          interpret fetcher vm (k r)+        EVM m -> do+          (r, vm') <- stToIO $ runStateT m vm           interpret fetcher vm' (k r)-        EVM m ->-          let (r, vm') = runState m vm-          in interpret fetcher vm' (k r)
− src/EVM/StorageLayout.hs
@@ -1,152 +0,0 @@-module EVM.StorageLayout where---- Figures out the layout of storage slots for Solidity contracts.--import EVM.Dapp (DappInfo(..))-import EVM.Solidity (SolcContract, creationSrcmap, SlotType(..))-import EVM.ABI (AbiType (..), parseTypeName)--import Optics.Core-import Data.Aeson (Value (..))-import Data.Aeson.Optics-import Data.Foldable (toList)-import Data.List.NonEmpty qualified as NonEmpty-import Data.Map qualified as Map-import Data.Maybe (fromMaybe, isJust)-import Data.Sequence qualified as Seq-import Data.Text (Text, unpack, pack, words)-import EVM.Types (internalError)--import Prelude hiding (words)---- A contract has all the slots of its inherited contracts.------ The slot order is determined by the inheritance linearization order,--- so we first have to calculate that.------ This information is available in the abstract syntax tree.--findContractDefinition :: DappInfo -> SolcContract -> Maybe Value-findContractDefinition dapp solc =-  -- The first source mapping in the contract's creation code-  -- corresponds to the source field of the contract definition.-  case Seq.viewl solc.creationSrcmap of-    firstSrcMap Seq.:< _ ->-      dapp.astSrcMap firstSrcMap-    _ ->-      Nothing--storageLayout :: DappInfo -> SolcContract -> [Text]-storageLayout dapp solc =-  let-    root :: Value-    root =-      fromMaybe Null-        (findContractDefinition dapp solc)-  in-    case preview ( key "attributes"-                 % key "linearizedBaseContracts"-                 % _Array-                 ) root of-      Nothing ->-        []-      Just ((reverse . toList) -> linearizedBaseContracts) ->-        flip concatMap linearizedBaseContracts-          (\case-             Number i -> fromMaybe (internalError "malformed AST JSON") $-               storageVariablesForContract =<<-                 Map.lookup (floor i) dapp.astIdMap-             _ ->-               internalError "malformed AST JSON")--storageVariablesForContract :: Value -> Maybe [Text]-storageVariablesForContract node = do-  name <- preview (ix "attributes" % key "name" % _String) node-  vars <--    fmap-      (filter isStorageVariableDeclaration . toList)-      (preview (ix "children" % _Array) node)--  pure . flip map vars $-    \x ->-      case preview (key "attributes" % key "name" % _String) x of-        Just variableName ->-          mconcat-            [ variableName-            , " (", name, ")"-            , "\n", "  Type: "-            , pack $ show (slotTypeForDeclaration x)-            ]-        Nothing ->-          internalError "malformed variable declaration"--nodeIs :: Text -> Value -> Bool-nodeIs t x = isSourceNode && hasRightName-  where-    isSourceNode =-      isJust (preview (key "src") x)-    hasRightName =-      Just t == preview (key "name" % _String) x--isStorageVariableDeclaration :: Value -> Bool-isStorageVariableDeclaration x =-  nodeIs "VariableDeclaration" x-    && preview (key "attributes" % key "constant" % _Bool) x /= Just True--slotTypeForDeclaration :: Value -> SlotType-slotTypeForDeclaration node =-  case toList <$> preview (key "children" % _Array) node of-    Just (x:_) ->-      grokDeclarationType x-    _ ->-      internalError "malformed AST"--grokDeclarationType :: Value -> SlotType-grokDeclarationType x =-  case preview (key "name" % _String) x of-    Just "Mapping" ->-      case preview (key "children" % _Array) x of-        Just (toList -> xs) ->-          grokMappingType xs-        _ ->-          internalError "malformed AST"-    Just _ ->-      StorageValue (grokValueType x)-    _ ->-      internalError ("malformed AST " ++ show x)--grokMappingType :: [Value] -> SlotType-grokMappingType [s, t] =-  case (grokDeclarationType s, grokDeclarationType t) of-    (StorageValue s', StorageMapping t' x) ->-      StorageMapping (NonEmpty.cons s' t') x-    (StorageValue s', StorageValue t') ->-      StorageMapping (pure s') t'-    (StorageMapping _ _, _) ->-      internalError "unexpected mapping as mapping key"-grokMappingType _ =-  internalError "unexpected AST child count for mapping"--grokValueType :: Value -> AbiType-grokValueType x =-  case ( preview (key "name" % _String) x-       , preview (key "children" % _Array) x-       , preview (key "attributes" % key "type" % _String) x-       ) of-    (Just "ElementaryTypeName", _, Just typeName) ->-      fromMaybe (internalError $ "ungrokked value type: " ++ show typeName)-        (parseTypeName mempty (head (words typeName)))-    (Just "UserDefinedTypeName", _, _) ->-      AbiAddressType-    (Just "ArrayTypeName", fmap toList -> Just [t], _)->-      AbiArrayDynamicType (grokValueType t)-    (Just "ArrayTypeName", fmap toList -> Just [t, n], _)->-      case ( preview (key "name" % _String) n-           , preview (key "attributes" % key "value" % _String) n-           ) of-        (Just "Literal", Just ((read . unpack) -> i)) ->-          AbiArrayType i (grokValueType t)-        _ ->-          internalError "malformed AST"-    _ ->-      internalError ("unknown value type " ++ show x)
src/EVM/SymExec.hs view
@@ -7,6 +7,7 @@ import Control.Concurrent.Spawn (parMapIO, pool) import Control.Concurrent.STM (atomically, TVar, readTVarIO, readTVar, newTVarIO, writeTVar) import Control.Monad.Operational qualified as Operational+import Control.Monad.ST (RealWorld, stToIO, ST) import Control.Monad.State.Strict import Data.Bifunctor (second) import Data.ByteString (ByteString)@@ -15,8 +16,9 @@ import Data.DoubleWord (Word256) import Data.List (foldl', sortBy) import Data.Maybe (fromMaybe, mapMaybe)-import Data.Map (Map)-import Data.Map qualified as Map+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Map.Merge.Strict qualified as Map import Data.Set (Set, isSubsetOf, size) import Data.Set qualified as Set import Data.Text (Text)@@ -26,12 +28,12 @@ import Data.Text.Lazy.IO qualified as TL import Data.Tree.Zipper qualified as Zipper import Data.Tuple (swap)-import EVM (makeVm, initialContract, getCodeLocation, isValidJumpDest)+import EVM (makeVm, abstractContract, initialContract, getCodeLocation, isValidJumpDest) import EVM.Exec import EVM.Fetch qualified as Fetch import EVM.ABI import EVM.Expr qualified as Expr-import EVM.Format (formatExpr, formatPartial)+import EVM.Format (formatExpr, formatPartial, showVal, bsToHex) import EVM.SMT (SMTCex(..), SMT2(..), assertProps, formatSMT2) import EVM.SMT qualified as SMT import EVM.Solvers@@ -39,8 +41,7 @@ import EVM.Stepper qualified as Stepper import EVM.Traversals import EVM.Types-import EVM.Concrete (createAddress)-import EVM.FeeSchedule qualified as FeeSchedule+import EVM.FeeSchedule (feeSchedule) import EVM.Format (indent, formatBinary) import GHC.Conc (getNumProcessors) import GHC.Generics (Generic)@@ -48,9 +49,6 @@ import Options.Generic (ParseField, ParseFields, ParseRecord) import Witch (into, unsafeInto) --- | A method name, and the (ordered) types of it's arguments-data Sig = Sig Text [AbiType]- data LoopHeuristic   = Naive   | StackBased@@ -79,6 +77,7 @@   , maxIter :: Maybe Integer   , askSmtIters :: Integer   , loopHeuristic :: LoopHeuristic+  , abstRefineConfig :: AbstRefineConfig   , rpcInfo :: Fetch.RpcInfo   }   deriving (Eq, Show)@@ -90,6 +89,7 @@   , maxIter = Nothing   , askSmtIters = 1   , loopHeuristic = StackBased+  , abstRefineConfig = abstRefineDefault   , rpcInfo = Nothing   } @@ -99,6 +99,9 @@ debugVeriOpts :: VeriOpts debugVeriOpts = defaultVeriOpts { debug = True } +debugAbstVeriOpts :: VeriOpts+debugAbstVeriOpts = defaultVeriOpts { abstRefineConfig = AbstRefineConfig True True }+ extractCex :: VerifyResult -> Maybe (Expr End, SMTCex) extractCex (Cex c) = Just c extractCex _ = Nothing@@ -119,7 +122,7 @@     then let v = Var name in St [Expr.inRange n v] v     else internalError "bad type"   AbiBoolType -> let v = Var name in St [bool v] v-  AbiAddressType -> let v = Var name in St [Expr.inRange 160 v] v+  AbiAddressType -> St [] (WAddr (SymAddr name))   AbiBytesType n ->     if n > 0 && n <= 32     then let v = Var name in St [Expr.inRange (n * 8) v] v@@ -189,37 +192,35 @@ abstractVM   :: (Expr Buf, [Prop])   -> ByteString-  -> Maybe Precondition-  -> Expr Storage-  -> VM-abstractVM cd contractCode maybepre store = finalVm-  where-    caller' = Caller 0-    value' = CallValue 0-    code' = RuntimeCode (ConcreteRuntimeCode contractCode)-    vm' = loadSymVM code' store caller' value' cd-    precond = case maybepre of+  -> Maybe (Precondition s)+  -> Bool+  -> ST s (VM s)+abstractVM cd contractCode maybepre create = do+  let value = TxValue+  let code = if create then InitCode contractCode (fst cd) else RuntimeCode (ConcreteRuntimeCode contractCode)+  vm <- loadSymVM code value (if create then mempty else cd) create+  let precond = case maybepre of                 Nothing -> []-                Just p -> [p vm']-    finalVm = vm' & over #constraints (<> precond)+                Just p -> [p vm]+  pure $ vm & over #constraints (<> precond)  loadSymVM   :: ContractCode-  -> Expr Storage   -> Expr EWord-  -> Expr EWord   -> (Expr Buf, [Prop])-  -> VM-loadSymVM x initStore addr callvalue' cd =+  -> Bool+  -> ST s (VM s)+loadSymVM x callvalue cd create =   (makeVm $ VMOpts-    { contract = initialContract x+    { contract = if create then initialContract x else abstractContract x (SymAddr "entrypoint")+    , otherContracts = []     , calldata = cd-    , value = callvalue'-    , initialStorage = initStore-    , address = createAddress ethrunAddress 1-    , caller = addr-    , origin = ethrunAddress --todo: generalize-    , coinbase = 0+    , value = callvalue+    , baseState = AbstractBase+    , address = SymAddr "entrypoint"+    , caller = SymAddr "caller"+    , origin = SymAddr "origin"+    , coinbase = SymAddr "coinbase"     , number = 0     , timestamp = Lit 0     , blockGaslimit = 0@@ -230,29 +231,28 @@     , baseFee = 0     , priorityFee = 0     , maxCodeSize = 0xffffffff-    , schedule = FeeSchedule.berlin+    , schedule = feeSchedule     , chainId = 1-    , create = False+    , create = create     , txAccessList = mempty     , allowFFI = False-    }) & set (#env % #contracts % at (createAddress ethrunAddress 1))-             (Just (initialContract x))+    })  -- | Interpreter which explores all paths at branching points. Returns an -- 'Expr End' representing the possible executions. interpret-  :: Fetch.Fetcher+  :: Fetch.Fetcher RealWorld   -> Maybe Integer -- max iterations   -> Integer -- ask smt iterations   -> LoopHeuristic-  -> VM-  -> Stepper (Expr End)+  -> VM RealWorld+  -> Stepper RealWorld (Expr End)   -> IO (Expr End) interpret fetcher maxIter askSmtIters heuristic vm =   eval . Operational.view   where   eval-    :: Operational.ProgramView Stepper.Action (Expr End)+    :: Operational.ProgramView (Stepper.Action RealWorld) (Expr End)     -> IO (Expr End)    eval (Operational.Return x) = pure x@@ -260,30 +260,34 @@   eval (action Operational.:>>= k) =     case action of       Stepper.Exec -> do-        let (r, vm') = runState exec vm+        (r, vm') <- stToIO $ runStateT exec vm         interpret fetcher maxIter askSmtIters heuristic vm' (k r)-      Stepper.Run -> do-        let vm' = execState exec vm-        interpret fetcher maxIter askSmtIters heuristic vm' (k vm')       Stepper.IOAct q -> do-        (r, vm') <- runStateT q vm-        interpret fetcher maxIter askSmtIters heuristic vm' (k r)+        r <- q+        interpret fetcher maxIter askSmtIters heuristic vm (k r)       Stepper.Ask (PleaseChoosePath cond continue) -> do         (a, b) <- concurrently-          (let (ra, vma) = runState (continue True) vm { result = Nothing }-           in interpret fetcher maxIter askSmtIters heuristic vma (k ra))-          (let (rb, vmb) = runState (continue False) vm { result = Nothing }-           in interpret fetcher maxIter askSmtIters heuristic vmb (k rb))+          (do+            (ra, vma) <- stToIO $ runStateT (continue True) vm { result = Nothing }+            interpret fetcher maxIter askSmtIters heuristic vma (k ra)+          )+          (do+            (rb, vmb) <- stToIO $ runStateT (continue False) vm { result = Nothing }+            interpret fetcher maxIter askSmtIters heuristic vmb (k rb)+          )         pure $ ITE cond a b       Stepper.Wait q -> do         let performQuery = do               m <- liftIO (fetcher q)-              let (r, vm') = runState m vm+              (r, vm') <- stToIO $ runStateT m vm               interpret fetcher maxIter askSmtIters heuristic vm' (k r)          case q of-          PleaseAskSMT cond _ continue -> do-            case cond of+          PleaseAskSMT cond preconds continue -> do+            let+              simpCond = Expr.simplify cond+              simpProps = Expr.simplifyProps ((simpCond ./= Lit 0):preconds)+            case simpCond of               -- is the condition concrete?               Lit c ->                 -- have we reached max iterations, are we inside a loop?@@ -292,9 +296,9 @@                   (Just _, Just True) ->                     pure $ Partial vm.keccakEqs (Traces (Zipper.toForest vm.traces) vm.env.contracts) $ MaxIterationsReached vm.state.pc vm.state.contract                   -- No. keep executing-                  _ ->-                    let (r, vm') = runState (continue (Case (c > 0))) vm-                    in interpret fetcher maxIter askSmtIters heuristic vm' (k r)+                  _ -> do+                    (r, vm') <- stToIO $ runStateT (continue (Case (c > 0))) vm+                    interpret fetcher maxIter askSmtIters heuristic vm' (k r)                -- the condition is symbolic               _ ->@@ -304,25 +308,27 @@                   (Just True, _, Just n) -> do                     -- continue execution down the opposite branch than the one that                     -- got us to this point and return a partial leaf for the other side-                    let (r, vm') = runState (continue (Case $ not n)) vm+                    (r, vm') <- stToIO $ runStateT (continue (Case $ not n)) vm                     a <- interpret fetcher maxIter askSmtIters heuristic vm' (k r)                     pure $ ITE cond a (Partial vm.keccakEqs (Traces (Zipper.toForest vm.traces) vm.env.contracts) (MaxIterationsReached vm.state.pc vm.state.contract))                   -- we're in a loop and askSmtIters has been reached                   (Just True, True, _) ->                     -- ask the smt solver about the loop condition                     performQuery-                  -- otherwise just try both branches and don't ask the solver-                  _ ->-                    let (r, vm') = runState (continue EVM.Types.Unknown) vm-                    in interpret fetcher maxIter askSmtIters heuristic vm' (k r)-+                  _ -> do+                    (r, vm') <- case simpProps of+                      -- if we can statically determine unsatisfiability then we skip exploring the jump+                      [PBool False] -> stToIO $ runStateT (continue (Case False)) vm+                      -- otherwise we explore both branches+                      _ -> stToIO $ runStateT (continue EVM.Types.Unknown) vm+                    interpret fetcher maxIter askSmtIters heuristic vm' (k r)           _ -> performQuery        Stepper.EVM m -> do-        let (r, vm') = runState m vm+        (r, vm') <- stToIO $ runStateT m vm         interpret fetcher maxIter askSmtIters heuristic vm' (k r) -maxIterationsReached :: VM -> Maybe Integer -> Maybe Bool+maxIterationsReached :: VM s -> Maybe Integer -> Maybe Bool maxIterationsReached _ Nothing = Nothing maxIterationsReached vm (Just maxIter) =   let codelocation = getCodeLocation vm@@ -331,7 +337,7 @@      then Map.lookup (codelocation, iters - 1) vm.cache.path      else Nothing -askSmtItersReached :: VM -> Integer -> Bool+askSmtItersReached :: VM s -> Integer -> Bool askSmtItersReached vm askSmtIters = let     codelocation = getCodeLocation vm     (iters, _) = view (at codelocation % non (0, [])) vm.iterations@@ -345,7 +351,7 @@   This heuristic is not perfect, and can certainly be tricked, but should generally be good enough for most compiler generated and non pathological user generated loops.  -}-isLoopHead :: LoopHeuristic -> VM -> Maybe Bool+isLoopHead :: LoopHeuristic -> VM s -> Maybe Bool isLoopHead Naive _ = Just True isLoopHead StackBased vm = let     loc = getCodeLocation vm@@ -356,8 +362,8 @@        Just (_, oldStack) -> Just $ filter isValid oldStack == filter isValid vm.state.stack        Nothing -> Nothing -type Precondition = VM -> Prop-type Postcondition = VM -> Expr End -> Prop+type Precondition s = VM s -> Prop+type Postcondition s = VM s -> Expr End -> Prop  checkAssert   :: SolverGroup@@ -368,8 +374,20 @@   -> VeriOpts   -> IO (Expr End, [VerifyResult]) checkAssert solvers errs c signature' concreteArgs opts =-  verifyContract solvers c signature' concreteArgs opts AbstractStore Nothing (Just $ checkAssertions errs)+  verifyContract solvers c signature' concreteArgs opts Nothing (Just $ checkAssertions errs) +getExpr+  :: SolverGroup+  -> ByteString+  -> Maybe Sig+  -> [String]+  -> VeriOpts+  -> IO (Expr End)+getExpr solvers c signature' concreteArgs opts = do+      preState <- stToIO $ abstractVM (mkCalldata signature' concreteArgs) c Nothing False+      exprInter <- interpret (Fetch.oracle solvers opts.rpcInfo) opts.maxIter opts.askSmtIters opts.loopHeuristic preState runExpr+      if opts.simp then (pure $ Expr.simplify exprInter) else pure exprInter+ {- | Checks if an assertion violation has been encountered    hevm recognises the following as an assertion violation:@@ -389,7 +407,7 @@    see: https://docs.soliditylang.org/en/v0.8.6/control-structures.html?highlight=Panic#panic-via-assert-and-error-via-require -}-checkAssertions :: [Word256] -> Postcondition+checkAssertions :: [Word256] -> Postcondition s checkAssertions errs _ = \case   Failure _ _ (Revert (ConcreteBuf msg)) -> PBool $ msg `notElem` (fmap panicMsg errs)   Failure _ _ (Revert b) -> foldl' PAnd (PBool True) (fmap (PNeg . PEq b . ConcreteBuf . panicMsg) errs)@@ -426,25 +444,28 @@   -> Maybe Sig   -> [String]   -> VeriOpts-  -> Expr Storage-  -> Maybe Precondition-  -> Maybe Postcondition+  -> Maybe (Precondition RealWorld)+  -> Maybe (Postcondition RealWorld)   -> IO (Expr End, [VerifyResult])-verifyContract solvers theCode signature' concreteArgs opts initStore maybepre maybepost =-  let preState = abstractVM (mkCalldata signature' concreteArgs) theCode maybepre initStore-  in verify solvers opts preState maybepost+verifyContract solvers theCode signature' concreteArgs opts maybepre maybepost = do+  preState <- stToIO $ abstractVM (mkCalldata signature' concreteArgs) theCode maybepre False+  verify solvers opts preState maybepost  -- | Stepper that parses the result of Stepper.runFully into an Expr End-runExpr :: Stepper.Stepper (Expr End)+runExpr :: Stepper.Stepper RealWorld (Expr End) runExpr = do   vm <- Stepper.runFully   let asserts = vm.keccakEqs <> vm.constraints+      traces = Traces (Zipper.toForest vm.traces) vm.env.contracts   pure $ case vm.result of-    Just (VMSuccess buf) -> Success asserts (Traces (Zipper.toForest vm.traces) vm.env.contracts) buf vm.env.storage-    Just (VMFailure e) -> Failure asserts (Traces (Zipper.toForest vm.traces) vm.env.contracts) e-    Just (Unfinished p) -> Partial asserts (Traces (Zipper.toForest vm.traces) vm.env.contracts) p+    Just (VMSuccess buf) -> Success asserts traces buf (fmap toEContract vm.env.contracts)+    Just (VMFailure e) -> Failure asserts traces e+    Just (Unfinished p) -> Partial asserts traces p     _ -> internalError "vm in intermediate state after call to runFully" +toEContract :: Contract -> Expr EContract+toEContract c = C c.code c.storage c.balance c.nonce+ -- | Converts a given top level expr into a list of final states and the -- associated path conditions for each state. flattenExpr :: Expr End -> [Expr End]@@ -453,9 +474,9 @@     go :: [Prop] -> Expr End -> [Expr End]     go pcs = \case       ITE c t f -> go (PNeg ((PEq c (Lit 0))) : pcs) t <> go (PEq c (Lit 0) : pcs) f-      Success ps trace msg store -> [Success (ps <> pcs) trace msg store]-      Failure ps trace e -> [Failure (ps <> pcs) trace e]-      Partial ps trace p -> [Partial (ps <> pcs) trace p]+      Success ps trace msg store -> [Success (nubOrd $ ps <> pcs) trace msg store]+      Failure ps trace e -> [Failure (nubOrd $ ps <> pcs) trace e]+      Partial ps trace p -> [Partial (nubOrd $ ps <> pcs) trace p]       GVar _ -> internalError "cannot flatten an Expr containing a GVar"  -- | Strips unreachable branches from a given expr@@ -491,47 +512,13 @@               (Nothing, Nothing) -> Nothing         pure (fst tres <> fst fres, subexpr)       leaf -> do-        let query = assertProps pcs+        let query = assertProps abstRefineDefault pcs         res <- checkSat solvers query         case res of           Sat _ -> pure ([query], Just leaf)           Unsat -> pure ([query], Nothing)           r -> internalError $ "Invalid solver result: " <> show r ---- | Evaluate the provided proposition down to its most concrete result-evalProp :: Prop -> Prop-evalProp = \case-  o@(PBool _) -> o-  o@(PNeg p)  -> case p of-              (PBool b) -> PBool (not b)-              _ -> o-  o@(PEq l r) -> if l == r-                 then PBool True-                 else o-  o@(PLT (Lit l) (Lit r)) -> if l < r-                             then PBool True-                             else o-  o@(PGT (Lit l) (Lit r)) -> if l > r-                             then PBool True-                             else o-  o@(PGEq (Lit l) (Lit r)) -> if l >= r-                              then PBool True-                              else o-  o@(PLEq (Lit l) (Lit r)) -> if l <= r-                              then PBool True-                              else o-  o@(PAnd l r) -> case (evalProp l, evalProp r) of-                    (PBool True, PBool True) -> PBool True-                    (PBool _, PBool _) -> PBool False-                    _ -> o-  o@(POr l r) -> case (evalProp l, evalProp r) of-                   (PBool False, PBool False) -> PBool False-                   (PBool _, PBool _) -> PBool True-                   _ -> o-  o -> o-- -- | Extract contraints stored in Expr End nodes extractProps :: Expr End -> [Prop] extractProps = \case@@ -558,8 +545,8 @@ verify   :: SolverGroup   -> VeriOpts-  -> VM-  -> Maybe Postcondition+  -> VM RealWorld+  -> Maybe (Postcondition RealWorld)   -> IO (Expr End, [VerifyResult]) verify solvers opts preState maybepost = do   putStrLn "Exploring contract"@@ -568,7 +555,7 @@   when opts.debug $ T.writeFile "unsimplified.expr" (formatExpr exprInter)    putStrLn "Simplifying expression"-  expr <- if opts.simp then (pure $ Expr.simplify exprInter) else pure exprInter+  let expr = if opts.simp then (Expr.simplify exprInter) else exprInter   when opts.debug $ T.writeFile "simplified.expr" (formatExpr expr)    putStrLn $ "Explored contract (" <> show (Expr.numBranches expr) <> " branches)"@@ -586,12 +573,12 @@       let         -- Filter out any leaves that can be statically shown to be safe         canViolate = flip filter flattened $-          \leaf -> case evalProp (post preState leaf) of+          \leaf -> case Expr.simplifyProp (post preState leaf) of             PBool True -> False             _ -> True         assumes = preState.constraints         withQueries = canViolate <&> \leaf ->-          (assertProps (PNeg (post preState leaf) : assumes <> extractProps leaf), leaf)+          (assertProps opts.abstRefineConfig (PNeg (post preState leaf) : assumes <> extractProps leaf), leaf)       putStrLn $ "Checking for reachability of "                    <> show (length withQueries)                    <> " potential property violation(s)"@@ -610,11 +597,21 @@   where     toVRes :: (CheckSatResult, Expr End) -> VerifyResult     toVRes (res, leaf) = case res of-      Sat model -> Cex (leaf, model)+      Sat model -> Cex (leaf, expandCex preState model)       EVM.Solvers.Unknown -> Timeout leaf       Unsat -> Qed ()       Error e -> internalError $ "solver responded with error: " <> show e +expandCex :: VM s -> SMTCex -> SMTCex+expandCex prestate c = c { store = Map.union c.store concretePreStore }+  where+    concretePreStore = Map.mapMaybe (maybeConcreteStore . (.storage))+                     . Map.filter (\v -> Expr.containsNode isConcreteStore v.storage)+                     $ (prestate.env.contracts)+    isConcreteStore = \case+      ConcreteStore _ -> True+      _ -> False+ type UnsatCache = TVar [Set Prop]  -- | Compares two contract runtimes for trace equivalence by running two VMs@@ -641,9 +638,8 @@     -- decompiles the given bytecode into a list of branches     getBranches :: ByteString -> IO [Expr End]     getBranches bs = do-      let-        bytecode = if BS.null bs then BS.pack [0] else bs-        prestate = abstractVM calldata bytecode Nothing AbstractStore+      let bytecode = if BS.null bs then BS.pack [0] else bs+      prestate <- stToIO $ abstractVM calldata bytecode Nothing False       expr <- interpret (Fetch.oracle solvers Nothing) opts.maxIter opts.askSmtIters opts.loopHeuristic prestate runExpr       let simpl = if opts.simp then (Expr.simplify expr) else expr       pure $ flattenExpr simpl@@ -694,10 +690,10 @@     -- used or not     check :: UnsatCache -> (Set Prop) -> Int -> IO (EquivResult, Bool)     check knownUnsat props idx = do-      let smt = assertProps $ Set.toList props+      let smt = assertProps opts.abstRefineConfig (Set.toList props)       -- if debug is on, write the query to a file-      when opts.debug $ TL.writeFile-        ("equiv-query-" <> show idx <> ".smt2") (formatSMT2 smt <> "\n\n(check-sat)")+      let filename = "equiv-query-" <> show idx <> ".smt2"+      when opts.debug $ TL.writeFile filename (formatSMT2 smt <> "\n\n(check-sat)")        ku <- readTVarIO knownUnsat       res <- if subsetAny props ku@@ -714,7 +710,7 @@               atomically $ readTVar knownUnsat >>= writeTVar knownUnsat . (props :)               pure (Qed (), False)         (_, EVM.Solvers.Unknown) -> pure (Timeout (), False)-        (_, Error txt) -> internalError $ "issue while running solver: `" <> T.unpack txt -- <> "` SMT file was: `" <> filename <> "`"+        (_, Error txt) -> internalError $ "solver returned: " <> (T.unpack txt) <> if opts.debug then "\n SMT file was: " <> filename <> "" else ""      -- Allows us to run it in parallel. Note that this (seems to) run it     -- from left-to-right, and with a max of K threads. This is in contrast to@@ -733,23 +729,7 @@     -- a differing result in each branch     distinct :: Expr End -> Expr End -> Maybe (Set Prop)     distinct aEnd bEnd =-      let-        differingResults = case (aEnd, bEnd) of-          (Success _ _ aOut aStore, Success _ _ bOut bStore) ->-            if aOut == bOut && aStore == bStore-            then PBool False-            else aStore ./= bStore .|| aOut ./= bOut-          (Failure _ _ (Revert a), Failure _ _ (Revert b)) -> if a == b then PBool False else a ./= b-          (Failure _ _ a, Failure _ _ b) -> if a == b then PBool False else PBool True-          -- partial end states can't be compared to actual end states, so we always ignore them-          (Partial {}, _) -> PBool False-          (_, Partial {}) -> PBool False-          (ITE _ _ _, _) -> internalError "Expressions must be flattened"-          (_, ITE _ _ _) -> internalError "Expressions must be flattened"-          (a, b) -> if a == b-                    then PBool False-                    else PBool True-      in case differingResults of+      case resultsDiffer aEnd bEnd of         -- if the end states are the same, then they can never produce a         -- different result under any circumstances         PBool False -> Nothing@@ -760,15 +740,56 @@         -- if we cannot statically determine whether or not the end states         -- differ, then we ask the solver if the end states can differ if both         -- sets of path conditions are satisfiable-        _ -> Just . Set.fromList $ differingResults : extractProps aEnd <> extractProps bEnd+        _ -> Just . Set.fromList $ resultsDiffer aEnd bEnd : extractProps aEnd <> extractProps bEnd +    resultsDiffer :: Expr End -> Expr End -> Prop+    resultsDiffer aEnd bEnd = case (aEnd, bEnd) of+      (Success _ _ aOut aState, Success _ _ bOut bState) ->+        case (aOut == bOut, aState == bState) of+          (True, True) -> PBool False+          (False, True) -> aOut ./= bOut+          (True, False) -> statesDiffer aState bState+          (False, False) -> statesDiffer aState bState .|| aOut ./= bOut+      (Failure _ _ (Revert a), Failure _ _ (Revert b)) -> if a == b then PBool False else a ./= b+      (Failure _ _ a, Failure _ _ b) -> if a == b then PBool False else PBool True+      -- partial end states can't be compared to actual end states, so we always ignore them+      (Partial {}, _) -> PBool False+      (_, Partial {}) -> PBool False+      (ITE _ _ _, _) -> internalError "Expressions must be flattened"+      (_, ITE _ _ _) -> internalError "Expressions must be flattened"+      (a, b) -> if a == b+                then PBool False+                else PBool True++    statesDiffer :: Map (Expr EAddr) (Expr EContract) -> Map (Expr EAddr) (Expr EContract) -> Prop+    statesDiffer aState bState+      = if Set.fromList (Map.keys aState) /= Set.fromList (Map.keys bState)+        -- TODO: consider possibility of aliased symbolic addresses+        then PBool True+        else let+          merged = (Map.merge Map.dropMissing Map.dropMissing (Map.zipWithMatched (\_ x y -> (x,y))) aState bState)+        in Map.foldl' (\a (ac, bc) -> a .|| contractsDiffer ac bc) (PBool False) merged++    contractsDiffer :: Expr EContract -> Expr EContract -> Prop+    contractsDiffer ac bc = let+        balsDiffer = case (ac.balance, bc.balance) of+          (Lit ab, Lit bb) -> PBool $ ab /= bb+          (ab, bb) -> if ab == bb then PBool False else ab ./= bb+        -- TODO: is this sound? do we need a more sophisticated nonce representation?+        noncesDiffer = PBool (ac.nonce /= bc.nonce)+        storesDiffer = case (ac.storage, bc.storage) of+          (ConcreteStore as, ConcreteStore bs) -> PBool $ as /= bs+          (as, bs) -> if as == bs then PBool False else as ./= bs+      in balsDiffer .|| storesDiffer .|| noncesDiffer++ both' :: (a -> b) -> (a, a) -> (b, b) both' f (x, y) = (f x, f y)  produceModels :: SolverGroup -> Expr End -> IO [(Expr End, CheckSatResult)] produceModels solvers expr = do   let flattened = flattenExpr expr-      withQueries = fmap (\e -> (assertProps . extractProps $ e, e)) flattened+      withQueries = fmap (\e -> ((assertProps abstRefineDefault) . extractProps $ e, e)) flattened   results <- flip mapConcurrently withQueries $ \(query, leaf) -> do     res <- checkSat solvers query     pure (res, leaf)@@ -791,7 +812,7 @@       putStrLn ""       putStrLn "Inputs:"       putStrLn ""-      T.putStrLn $ indent 2 $ formatCex cd cex+      T.putStrLn $ indent 2 $ formatCex cd Nothing cex       putStrLn ""       putStrLn "End State:"       putStrLn ""@@ -799,8 +820,8 @@       putStrLn ""  -formatCex :: Expr Buf -> SMTCex -> Text-formatCex cd m@(SMTCex _ _ store blockContext txContext) = T.unlines $+formatCex :: Expr Buf -> Maybe Sig -> SMTCex -> Text+formatCex cd sig m@(SMTCex _ _ _ store blockContext txContext) = T.unlines $   [ "Calldata:"   , indent 2 cd'   , ""@@ -816,7 +837,9 @@     -- it for branches that do not refer to calldata at all (e.g. the top level     -- callvalue check inserted by solidity in contracts that don't have any     -- payable functions).-    cd' = prettyBuf $ Expr.simplify $ subModel m cd+    cd' = case sig of+      Nothing -> prettyBuf . Expr.simplify . defaultSymbolicValues $ subModel m cd+      Just (Sig n ts) -> prettyCalldata m cd n ts      storeCex :: [Text]     storeCex@@ -824,7 +847,7 @@       | otherwise =           [ "Storage:"           , indent 2 $ T.unlines $ Map.foldrWithKey (\key val acc ->-              ("Addr " <> (T.pack $ show (unsafeInto key :: Addr))+              ("Addr " <> (T.pack . show $ key)                 <> ": " <> (T.pack $ show (Map.toList val))) : acc             ) mempty store           , ""@@ -843,9 +866,7 @@      -- strips the frame arg from frame context vars to make them easier to read     showTxCtx :: Expr EWord -> Text-    showTxCtx (CallValue _) = "CallValue"-    showTxCtx (Caller _) = "Caller"-    showTxCtx (Address _) = "Address"+    showTxCtx (TxValue) = "TxValue"     showTxCtx x = T.pack $ show x      -- strips all frame context that doesn't come from the top frame@@ -853,11 +874,8 @@     filterSubCtx = Map.filterWithKey go       where         go :: Expr EWord -> W256 -> Bool-        go (CallValue x) _ = x == 0-        go (Caller x) _ = x == 0-        go (Address x) _ = x == 0+        go (TxValue) _ = True         go (Balance {}) _ = internalError "TODO: BALANCE"-        go (SelfBalance {}) _ = internalError "TODO: SELFBALANCE"         go (Gas {}) _ = internalError "TODO: Gas"         go _ _ = False @@ -875,23 +893,73 @@     prettyBuf :: Expr Buf -> Text     prettyBuf (ConcreteBuf "") = "Empty"     prettyBuf (ConcreteBuf bs) = formatBinary bs-    prettyBuf _ = "Any"+    prettyBuf b = internalError $ "Unexpected symbolic buffer:\n" <> T.unpack (formatExpr b) --- | Takes a buffer and a Cex and replaces all abstract values in the buf with+prettyCalldata :: SMTCex -> Expr Buf -> Text -> [AbiType] -> Text+prettyCalldata cex buf sig types = head (T.splitOn "(" sig) <> "(" <> body <> ")"+  where+    argdata = Expr.drop 4 . Expr.simplify . defaultSymbolicValues $ subModel cex buf+    body = case decodeBuf types argdata of+      CAbi v -> T.intercalate "," (fmap showVal v)+      NoVals -> case argdata of+          ConcreteBuf c -> T.pack (bsToHex c)+          _ -> err+      SAbi _ -> err+    err = internalError $ "unable to produce a concrete model for calldata: " <> show buf++-- | If the expression contains any symbolic values, default them to some+-- concrete value The intuition here is that if we still have symbolic values+-- in our calldata expression after substituing in our cex, then they can have+-- any value and we can safely pick a random value. This is a bit unsatisfying,+-- we should really be doing smth like: https://github.com/ethereum/hevm/issues/334+-- but it's probably good enough for now+defaultSymbolicValues :: Expr a -> Expr a+defaultSymbolicValues e = subBufs (foldTerm symbufs mempty e)+                        . subVars (foldTerm symwords mempty e)+                        . subAddrs (foldTerm symaddrs mempty e) $ e+  where+    symaddrs :: Expr a -> Map (Expr EAddr) Addr+    symaddrs = \case+      a@(SymAddr _) -> Map.singleton a (Addr 0x1312)+      _ -> mempty+    symbufs :: Expr a -> Map (Expr Buf) ByteString+    symbufs = \case+      a@(AbstractBuf _) -> Map.singleton a ""+      _ -> mempty+    symwords :: Expr a -> Map (Expr EWord) W256+    symwords = \case+      a@(Var _) -> Map.singleton a 0+      a@Origin -> Map.singleton a 0+      a@Coinbase -> Map.singleton a 0+      a@Timestamp -> Map.singleton a 0+      a@BlockNumber -> Map.singleton a 0+      a@PrevRandao -> Map.singleton a 0+      a@GasLimit -> Map.singleton a 0+      a@ChainId -> Map.singleton a 0+      a@BaseFee -> Map.singleton a 0+      _ -> mempty++-- | Takes an expression and a Cex and replaces all abstract values in the buf with -- concrete ones from the Cex. subModel :: SMTCex -> Expr a -> Expr a-subModel c expr =-  subBufs (fmap forceFlattened c.buffers) . subVars c.vars . subStore c.store-  . subVars c.blockContext . subVars c.txContext $ expr+subModel c+  = subBufs (fmap forceFlattened c.buffers)+  . subStores c.store+  . subVars c.vars+  . subVars c.blockContext+  . subVars c.txContext+  . subAddrs c.addrs   where     forceFlattened (SMT.Flat bs) = bs     forceFlattened b@(SMT.Comp _) = forceFlattened $       fromMaybe (internalError $ "cannot flatten buffer: " <> show b)                 (SMT.collapse b) -    subVars model b = Map.foldlWithKey subVar b model+subVars :: Map (Expr EWord) W256 -> Expr a -> Expr a+subVars model b = Map.foldlWithKey subVar b model+  where     subVar :: Expr a -> Expr EWord -> W256 -> Expr a-    subVar b var val = mapExpr go b+    subVar a var val = mapExpr go a       where         go :: Expr a -> Expr a         go = \case@@ -900,9 +968,24 @@                       else v           e -> e -    subBufs model b = Map.foldlWithKey subBuf b model+subAddrs :: Map (Expr EAddr) Addr -> Expr a -> Expr a+subAddrs model b = Map.foldlWithKey subAddr b model+  where+    subAddr :: Expr a -> Expr EAddr -> Addr -> Expr a+    subAddr a var val = mapExpr go a+      where+        go :: Expr a -> Expr a+        go = \case+          v@(SymAddr _) -> if v == var+                      then LitAddr val+                      else v+          e -> e++subBufs :: Map (Expr Buf) ByteString -> Expr a -> Expr a+subBufs model b = Map.foldlWithKey subBuf b model+  where     subBuf :: Expr a -> Expr Buf -> ByteString -> Expr a-    subBuf b var val = mapExpr go b+    subBuf x var val = mapExpr go x       where         go :: Expr a -> Expr a         go = \case@@ -911,10 +994,24 @@                       else a           e -> e -    subStore :: Map W256 (Map W256 W256) -> Expr a -> Expr a-    subStore m b = mapExpr go b+subStores :: Map (Expr EAddr) (Map W256 W256) -> Expr a -> Expr a+subStores model b = Map.foldlWithKey subStore b model+  where+    subStore :: Expr a -> Expr EAddr -> Map W256 W256 -> Expr a+    subStore x var val = mapExpr go x       where         go :: Expr a -> Expr a         go = \case-          AbstractStore -> ConcreteStore m+          v@(AbstractStore a)+            -> if a == var+               then ConcreteStore val+               else v           e -> e++getCex :: ProofResult a b c -> Maybe b+getCex (Cex c) = Just c+getCex _ = Nothing++getTimeout :: ProofResult a b c -> Maybe c+getTimeout (Timeout c) = Just c+getTimeout _ = Nothing
− src/EVM/TTY.hs
@@ -1,1047 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE ImplicitParams #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE UndecidableInstances #-}--module EVM.TTY where--import Brick-import Brick.Widgets.Border-import Brick.Widgets.Center-import Brick.Widgets.List--import EVM-import EVM.ABI (decodeAbiValue, emptyAbi, abiTypeSolidity, AbiType(..))-import EVM.SymExec (maxIterationsReached, symCalldata)-import EVM.Expr (simplify)-import EVM.Dapp (DappInfo(..), emptyDapp, dappInfo, Test, extractSig, Test(..), srcMap, unitTestMethods)-import EVM.Debug-import EVM.Fetch (Fetcher)-import EVM.Fetch qualified as Fetch-import EVM.Format (showWordExact, showWordExplanation, contractNamePart,-  contractPathPart, showTraceTree, prettyIfConcreteWord, formatExpr)-import EVM.Hexdump (prettyHex)-import EVM.Solvers (SolverGroup)-import EVM.Op-import EVM.Solidity hiding (storageLayout)-import EVM.Types hiding (padRight, Max)-import EVM.UnitTest-import EVM.Stepper (Stepper)-import EVM.Stepper qualified as Stepper-import EVM.StorageLayout-import EVM.TTYCenteredList qualified as Centered--import Optics.Core-import Optics.State-import Optics.TH--import Control.Monad.Operational qualified as Operational-import Control.Monad.State.Strict hiding (state)-import Data.Aeson.Optics-import Data.ByteString (ByteString)-import Data.ByteString qualified as BS-import Data.List (sort, find)-import Data.Maybe (isJust, fromJust, fromMaybe, isNothing)-import Data.Map (Map, insert, lookupLT, singleton, filter, (!?))-import Data.Map qualified as Map-import Data.Text (Text, pack)-import Data.Text qualified as T-import Data.Text qualified as Text-import Data.Text.Encoding (decodeUtf8)-import Data.Vector qualified as Vec-import Data.Vector.Storable qualified as SVec-import Data.Version (showVersion)-import Graphics.Vty qualified as V-import System.Console.Haskeline qualified as Readline-import Paths_hevm qualified as Paths-import Text.Wrap-import Witch (into)--data Name-  = AbiPane-  | StackPane-  | BytecodePane-  | TracePane-  | SolidityPane-  | TestPickerPane-  | BrowserPane-  | Pager-  deriving (Eq, Show, Ord)--type UiWidget = Widget Name--data UiVmState = UiVmState-  { vm         :: VM-  , step       :: Int-  , snapshots  :: Map Int (VM, Stepper ())-  , stepper    :: Stepper ()-  , showMemory :: Bool-  , testOpts   :: UnitTestOptions-  }--data UiTestPickerState = UiTestPickerState-  { tests :: List Name (Text, Text)-  , dapp  :: DappInfo-  , opts  :: UnitTestOptions-  }--data UiBrowserState = UiBrowserState-  { contracts :: List Name (Addr, Contract)-  , vm        :: UiVmState-  }--data UiState-  = ViewVm UiVmState-  | ViewContracts UiBrowserState-  | ViewPicker UiTestPickerState-  | ViewHelp UiVmState--makeFieldLabelsNoPrefix ''UiVmState-makeFieldLabelsNoPrefix ''UiTestPickerState-makeFieldLabelsNoPrefix ''UiBrowserState-makePrisms ''UiState---- caching VM states lets us backstep efficiently-snapshotInterval :: Int-snapshotInterval = 50--type Pred a = a -> Bool--data StepMode-  = Step !Int                  -- ^ Run a specific number of steps-  | StepUntil (Pred VM)        -- ^ Finish when a VM predicate holds---- | Each step command in the terminal should finish immediately--- with one of these outcomes.-data Continuation a-     = Stopped a              -- ^ Program finished-     | Continue (Stepper a)   -- ^ Took one step; more steps to go----- | This turns a @Stepper@ into a state action usable--- from within the TTY loop, yielding a @StepOutcome@ depending on the @StepMode@.-interpret-  :: (?fetcher :: Fetcher-  ,   ?maxIter :: Maybe Integer)-  => StepMode-  -> Stepper a-  -> StateT UiVmState IO (Continuation a)-interpret mode =--  -- Like the similar interpreters in @EVM.UnitTest@ and @EVM.VMTest@,-  -- this one is implemented as an "operational monad interpreter".--  eval . Operational.view-  where-    eval-      :: Operational.ProgramView Stepper.Action a-      -> StateT UiVmState IO (Continuation a)--    eval (Operational.Return x) =-      pure (Stopped x)--    eval (action Operational.:>>= k) =-      case action of--        Stepper.Run -> do-          -- Have we reached the final result of this action?-          use (#vm % #result) >>= \case-            Just _ -> do-              -- Yes, proceed with the next action.-              vm <- use #vm-              interpret mode (k vm)-            Nothing -> do-              -- No, keep performing the current action-              keepExecuting mode (Stepper.run >>= k)--        -- Stepper wants to keep executing?-        Stepper.Exec -> do-          -- Have we reached the final result of this action?-          use (#vm % #result) >>= \case-            Just r ->-              -- Yes, proceed with the next action.-              interpret mode (k r)-            Nothing -> do-              -- No, keep performing the current action-              keepExecuting mode (Stepper.exec >>= k)--        -- Stepper is waiting for user input from a query-        Stepper.Ask (PleaseChoosePath _ cont) -> do-          -- ensure we aren't stepping past max iterations-          vm <- use #vm-          case maxIterationsReached vm ?maxIter of-            Nothing -> pure $ Continue (k ())-            Just n -> interpret mode (Stepper.evm (cont (not n)) >>= k)--        -- Stepper wants to make a query and wait for the results?-        Stepper.Wait (PleaseAskSMT (Lit c) _ continue) ->-          interpret mode (Stepper.evm (continue (Case (c > 0))) >>= k)-        Stepper.Wait q -> do-          do m <- liftIO (?fetcher q)-             interpret mode (Stepper.evm m >>= k)--        -- Stepper wants to make a query and wait for the results?-        Stepper.IOAct q -> do-          Brick.zoom (toLensVL #vm) (StateT (runStateT q)) >>= interpret mode . k--        -- Stepper wants to modify the VM.-        Stepper.EVM m -> do-          vm <- use #vm-          let (r, vm1) = runState m vm-          assign #vm vm1-          interpret mode (Stepper.exec >> (k r))--keepExecuting :: (?fetcher :: Fetcher-              ,   ?maxIter :: Maybe Integer)-              => StepMode-              -> Stepper a-              -> StateT UiVmState IO (Continuation a)-keepExecuting mode restart = case mode of-  Step 0 -> do-    -- We come here when we've continued while stepping,-    -- either from a query or from a return;-    -- we should pause here and wait for the user.-    pure (Continue restart)--  Step i -> do-    -- Run one instruction and recurse-    stepOneOpcode restart-    interpret (Step (i - 1)) restart--  StepUntil p -> do-    vm <- use #vm-    if p vm-      then-        interpret (Step 0) restart-      else do-        -- Run one instruction and recurse-        stepOneOpcode restart-        interpret (StepUntil p) restart--isUnitTestContract :: Text -> DappInfo -> Bool-isUnitTestContract name dapp =-  elem name (map fst dapp.unitTests)--mkVty :: IO V.Vty-mkVty = do-  vty <- V.mkVty V.defaultConfig-  V.setMode (V.outputIface vty) V.BracketedPaste True-  pure vty--runFromVM :: SolverGroup -> Fetch.RpcInfo -> Maybe Integer -> DappInfo -> VM -> IO VM-runFromVM solvers rpcInfo maxIter' dappinfo vm = do--  let-    opts = UnitTestOptions-      { solvers       = solvers-      , rpcInfo       = rpcInfo-      , verbose       = Nothing-      , maxIter       = maxIter'-      , askSmtIters   = 1-      , smtTimeout    = Nothing-      , smtDebug      = False-      , solver        = Nothing-      , maxDepth      = Nothing-      , match         = ""-      , fuzzRuns      = 1-      , replay        = internalError "irrelevant"-      , vmModifier    = id-      , testParams    = internalError "irrelevant"-      , dapp          = dappinfo-      , ffiAllowed    = False-      , covMatch       = Nothing-      }-    ui0 = initUiVmState vm opts (void Stepper.execFully)--  v <- mkVty-  ui2 <- customMain v mkVty Nothing (app opts) (ViewVm ui0)-  case ui2 of-    ViewVm ui -> pure ui.vm-    _ -> internalError "customMain returned prematurely"---initUiVmState :: VM -> UnitTestOptions -> Stepper () -> UiVmState-initUiVmState vm0 opts script =-  UiVmState-    { vm           = vm0-    , stepper      = script-    , step         = 0-    , snapshots    = singleton 0 (vm0, script)-    , showMemory   = False-    , testOpts     = opts-    }----- filters out fuzztests, unless they have--- explicitly been given an argument by `replay`-debuggableTests :: UnitTestOptions -> (Text, [(Test, [AbiType])]) -> [(Text, Text)]-debuggableTests UnitTestOptions{..} (contractname, tests) = case replay of-  Nothing -> [(contractname, extractSig $ fst x) | x <- tests, not $ isFuzzTest x]-  Just (sig, _) -> [(contractname, extractSig $ fst x) | x <- tests, not (isFuzzTest x) || extractSig (fst x) == sig]--isFuzzTest :: (Test, [AbiType]) -> Bool-isFuzzTest (SymbolicTest _, _) = False-isFuzzTest (ConcreteTest _, []) = False-isFuzzTest (ConcreteTest _, _) = True-isFuzzTest (InvariantTest _, _) = True--main :: UnitTestOptions -> FilePath -> Maybe BuildOutput -> IO ()-main opts root buildOutput = do-  let-    dapp = maybe emptyDapp (dappInfo root) buildOutput-    ui = ViewPicker $ UiTestPickerState-      { tests =-          list-            TestPickerPane-            (Vec.fromList-             (concatMap-              (debuggableTests opts)-              dapp.unitTests))-            1-      , dapp = dapp-      , opts = opts-      }-  v <- mkVty-  _ <- customMain v mkVty Nothing (app opts) (ui :: UiState)-  pure ()--takeStep-  :: (?fetcher :: Fetcher-     ,?maxIter :: Maybe Integer)-  => UiVmState-  -> StepMode-  -> EventM n UiState ()-takeStep ui mode =-  liftIO nxt >>= \case-    (Stopped (), _) ->-      pure ()-    (Continue steps, ui') ->-      put (ViewVm (ui' & set #stepper steps))-  where-    m = interpret mode ui.stepper-    nxt = runStateT m ui--backstepUntil-  :: (?fetcher :: Fetcher-     ,?maxIter :: Maybe Integer)-  => (UiVmState -> Pred VM) -> EventM n UiState ()-backstepUntil p = get >>= \case-  ViewVm s ->-    case s.step of-      0 -> pure ()-      n -> do-        s1 <- liftIO $ backstep s-        let-          -- find a previous vm that satisfies the predicate-          snapshots' = Data.Map.filter (p s1 . fst) s1.snapshots-        case lookupLT n snapshots' of-          -- If no such vm exists, go to the beginning-          Nothing ->-            let-              (step', (vm', stepper')) = fromJust $ lookupLT (n - 1) s.snapshots-              s2 = s1-                & set #vm vm'-                & set (#vm % #cache) s1.vm.cache-                & set #step step'-                & set #stepper stepper'-            in takeStep s2 (Step 0)-          -- step until the predicate doesn't hold-          Just (step', (vm', stepper')) ->-            let-              s2 = s1-                & set #vm vm'-                & set (#vm % #cache) s1.vm.cache-                & set #step step'-                & set #stepper stepper'-            in takeStep s2 (StepUntil (not . p s1))-  _ -> pure ()--backstep-  :: (?fetcher :: Fetcher-     ,?maxIter :: Maybe Integer)-  => UiVmState -> IO UiVmState-backstep s =-  case s.step of-    -- We're already at the first step; ignore command.-    0 -> pure s-    -- To step backwards, we revert to the previous snapshot-    -- and execute n - 1 `mod` snapshotInterval steps from there.--    -- We keep the current cache so we don't have to redo-    -- any blocking queries, and also the memory view.-    n ->-      let-        (step, (vm, stepper)) = fromJust $ lookupLT n s.snapshots-        s1 = s-          & set #vm vm-          & set (#vm % #cache) s.vm.cache-          & set #step step-          & set #stepper stepper-        stepsToTake = n - step - 1--      in-        runStateT (interpret (Step stepsToTake) stepper) s1 >>= \case-          (Continue steps, ui') -> pure $ ui' & set #stepper steps-          _ -> internalError "unexpected end"--appEvent-  :: (?fetcher::Fetcher, ?maxIter :: Maybe Integer) =>-  BrickEvent Name e ->-  EventM Name UiState ()---- Contracts: Down - list down-appEvent (VtyEvent e@(V.EvKey V.KDown [])) = get >>= \case-  ViewContracts _s -> do-    Brick.zoom-      (traverseOf $ _ViewContracts % #contracts)-      (handleListEvent e)-    pure ()-  ViewPicker _s -> do-    Brick.zoom-      (traverseOf $ _ViewPicker % #tests)-      (handleListEvent e)-    pure()-  _ -> pure ()---- Contracts: Up - list up--- Page: Up - scroll-appEvent (VtyEvent e@(V.EvKey V.KUp [])) = get >>= \case-  ViewContracts _s -> do-    Brick.zoom-      (traverseOf $ _ViewContracts % #contracts)-      (handleListEvent e)-  ViewPicker _s -> do-    Brick.zoom-      (traverseOf $ _ViewPicker % #tests)-      (handleListEvent e)-    pure()-  _ -> pure ()---- Vm Overview: Esc - return to test picker or exit--- Any: Esc - return to Vm Overview or Exit-appEvent (VtyEvent (V.EvKey V.KEsc [])) = get >>= \case-  ViewVm s -> do-    let opts = s ^. #testOpts-        dapp = opts.dapp-        tests = concatMap (debuggableTests opts) dapp.unitTests-    case tests of-      [] -> halt-      ts ->-        put $ ViewPicker $ UiTestPickerState-          { tests = list TestPickerPane (Vec.fromList ts) 1-          , dapp  = dapp-          , opts  = opts-          }-  ViewHelp s -> put (ViewVm s)-  ViewContracts s -> put (ViewVm $ s ^. #vm)-  _ -> halt---- Vm Overview: Enter - open contracts view--- UnitTest Picker: Enter - select from list-appEvent (VtyEvent (V.EvKey V.KEnter [])) = get >>= \case-  ViewVm s ->-    put . ViewContracts $ UiBrowserState-      { contracts =-          list-            BrowserPane-            (Vec.fromList (Map.toList s.vm.env.contracts))-            2-      , vm = s-      }-  ViewPicker s ->-    case listSelectedElement s.tests of-      Nothing -> internalError "nothing selected"-      Just (_, x) -> do-        let initVm  = initialUiVmStateForTest s.opts x-        put (ViewVm initVm)-  _ -> pure ()---- Vm Overview: m - toggle memory pane-appEvent (VtyEvent (V.EvKey (V.KChar 'm') [])) = get >>= \case-  ViewVm s -> put (ViewVm $ over #showMemory not s)-  _ -> pure ()---- Vm Overview: h - open help view-appEvent (VtyEvent (V.EvKey (V.KChar 'h') [])) = get >>= \case-  ViewVm s -> put (ViewHelp s)-  _ -> pure ()---- Vm Overview: spacebar - read input-appEvent (VtyEvent (V.EvKey (V.KChar ' ') [])) =-  let-    loop = do-      Readline.getInputLine "% " >>= \case-        Just hey -> Readline.outputStrLn hey-        Nothing  -> pure ()-      Readline.getInputLine "% " >>= \case-        Just hey' -> Readline.outputStrLn hey'-        Nothing   -> pure ()-   in do-    s <- get-    suspendAndResume $ do-      Readline.runInputT Readline.defaultSettings loop-      pure s---- todo refactor to zipper step forward--- Vm Overview: n - step-appEvent (VtyEvent (V.EvKey (V.KChar 'n') [])) = get >>= \case-  ViewVm s ->-    when (isNothing (s ^. #vm % #result)) $-      takeStep s (Step 1)-  _ -> pure ()---- Vm Overview: N - step-appEvent (VtyEvent (V.EvKey (V.KChar 'N') [])) = get >>= \case-  ViewVm s ->-    when (isNothing (s ^. #vm % #result)) $-      takeStep s (StepUntil (isNextSourcePosition s))-  _ -> pure ()---- Vm Overview: C-n - step-appEvent (VtyEvent (V.EvKey (V.KChar 'n') [V.MCtrl])) = get >>= \case-  ViewVm s ->-    when (isNothing (s ^. #vm % #result)) $-      takeStep s (StepUntil (isNextSourcePositionWithoutEntering s))-  _ -> pure ()---- Vm Overview: e - step-appEvent (VtyEvent (V.EvKey (V.KChar 'e') [])) = get >>= \case-  ViewVm s ->-    when (isNothing (s ^. #vm % #result)) $-      takeStep s (StepUntil (isExecutionHalted s))-  _ -> pure ()---- Vm Overview: a - step-appEvent (VtyEvent (V.EvKey (V.KChar 'a') [])) = get >>= \case-  ViewVm s ->-    -- We keep the current cache so we don't have to redo-    -- any blocking queries.-    let-      (vm, stepper) = fromJust (Map.lookup 0 s.snapshots)-      s' = s-        & set #vm vm-        & set (#vm % #cache) s.vm.cache-        & set #step 0-        & set #stepper stepper--    in takeStep s' (Step 0)-  _ -> pure ()---- Vm Overview: p - backstep-appEvent (VtyEvent (V.EvKey (V.KChar 'p') [])) = get >>= \case-  ViewVm s ->-    case s.step of-      0 ->-        -- We're already at the first step; ignore command.-        pure ()-      n -> do-        -- To step backwards, we revert to the previous snapshot-        -- and execute n - 1 `mod` snapshotInterval steps from there.--        -- We keep the current cache so we don't have to redo-        -- any blocking queries, and also the memory view.-        let-          (step, (vm, stepper)) = fromJust $ lookupLT n s.snapshots-          s1 = s-            & set #vm vm -- set the vm to the one from the snapshot-            & set (#vm % #cache) s.vm.cache -- persist the cache-            & set #step step-            & set #stepper stepper-          stepsToTake = n - step - 1--        takeStep s1 (Step stepsToTake)-  _ -> pure ()---- Vm Overview: P - backstep to previous source-appEvent (VtyEvent (V.EvKey (V.KChar 'P') [])) =-  backstepUntil isNextSourcePosition---- Vm Overview: c-p - backstep to previous source avoiding CALL and CREATE-appEvent (VtyEvent (V.EvKey (V.KChar 'p') [V.MCtrl])) =-  backstepUntil isNextSourcePositionWithoutEntering---- Vm Overview: 0 - choose no jump-appEvent (VtyEvent (V.EvKey (V.KChar '0') [])) = get >>= \case-  ViewVm s ->-    case view (#vm % #result) s of-      Just (HandleEffect (Choose (PleaseChoosePath _ contin))) ->-        takeStep (s & set #stepper (Stepper.evm (contin True) >> s.stepper))-          (Step 1)-      _ -> pure ()-  _ -> pure ()---- Vm Overview: 1 - choose jump-appEvent (VtyEvent (V.EvKey (V.KChar '1') [])) = get >>= \case-  ViewVm s ->-    case s.vm.result of-      Just (HandleEffect (Choose (PleaseChoosePath _ contin))) ->-        takeStep (s & set #stepper (Stepper.evm (contin False) >> s.stepper))-          (Step 1)-      _ -> pure ()-  _ -> pure ()---- Page: C-f - Page down-appEvent (VtyEvent (V.EvKey (V.KChar 'f') [V.MCtrl])) =-  vScrollPage (viewportScroll TracePane) Down---- Page: C-b - Page up-appEvent (VtyEvent (V.EvKey (V.KChar 'b') [V.MCtrl])) =-  vScrollPage (viewportScroll TracePane) Up---- UnitTest Picker: (main) - render list-appEvent (VtyEvent e) = do-  Brick.zoom (traverseOf (_ViewPicker % #tests))-    (handleListEvent e)---- Default-appEvent _ = pure ()--app :: UnitTestOptions -> App UiState () Name-app UnitTestOptions{..} =-  let ?fetcher = Fetch.oracle solvers rpcInfo-      ?maxIter = maxIter-  in App-  { appDraw = drawUi-  , appChooseCursor = neverShowCursor-  , appHandleEvent = appEvent-  , appStartEvent = pure ()-  , appAttrMap = const (attrMap V.defAttr myTheme)-  }--initialUiVmStateForTest-  :: UnitTestOptions-  -> (Text, Text)-  -> UiVmState-initialUiVmStateForTest opts@UnitTestOptions{..} (theContractName, theTestName) = initUiVmState vm0 opts script-  where-    cd = case test of-      SymbolicTest _ -> symCalldata theTestName types [] (AbstractBuf "txdata")-      _ -> (internalError "unreachable", error $ internalError "unreachable")-    (test, types) = fromJust $ find (\(test',_) -> extractSig test' == theTestName) $ unitTestMethods testContract-    testContract = fromJust $ Map.lookup theContractName dapp.solcByName-    vm0 =-      initialUnitTestVm opts testContract-    script = do-      Stepper.evm . pushTrace . EntryTrace $-        "test " <> theTestName <> " (" <> theContractName <> ")"-      initializeUnitTest opts testContract-      case test of-        ConcreteTest _ -> do-          let args = case replay of-                       Nothing -> emptyAbi-                       Just (sig, callData) ->-                         if theTestName == sig-                         then decodeAbiValue (AbiTupleType (Vec.fromList types)) callData-                         else emptyAbi-          void (runUnitTest opts theTestName args)-        SymbolicTest _ -> do-          void (execSymTest opts theTestName cd)-        InvariantTest _ -> do-          targets <- getTargetContracts opts-          let randomRun = initialExplorationStepper opts theTestName [] targets (fromMaybe 20 maxDepth)-          void $ case replay of-            Nothing -> randomRun-            Just (sig, cd') ->-              if theTestName == sig-              then initialExplorationStepper opts theTestName (decodeCalls cd') targets (length (decodeCalls cd'))-              else randomRun--myTheme :: [(AttrName, V.Attr)]-myTheme =-  [ (selectedAttr, V.defAttr `V.withStyle` V.standout)-  , (dimAttr, V.defAttr `V.withStyle` V.dim)-  , (borderAttr, V.defAttr `V.withStyle` V.dim)-  , (wordAttr, fg V.yellow)-  , (boldAttr, V.defAttr `V.withStyle` V.bold)-  , (activeAttr, V.defAttr `V.withStyle` V.standout)-  ]--drawUi :: UiState -> [UiWidget]-drawUi (ViewVm s) = drawVm s-drawUi (ViewPicker s) = drawTestPicker s-drawUi (ViewContracts s) = drawVmBrowser s-drawUi (ViewHelp _) = drawHelpView--drawHelpView :: [UiWidget]-drawHelpView =-    [ center . borderWithLabel version .-      padLeftRight 4 . padTopBottom 2 .  str $-        "Esc    Exit the debugger\n\n" <>-        "a      Step to start\n" <>-        "e      Step to end\n" <>-        "n      Step fwds by one instruction\n" <>-        "N      Step fwds to the next source position\n" <>-        "C-n    Step fwds to the next source position skipping CALL & CREATE\n" <>-        "p      Step back by one instruction\n\n" <>-        "P      Step back to the previous source position\n\n" <>-        "C-p    Step back to the previous source position skipping CALL & CREATE\n\n" <>-        "m      Toggle memory pane\n" <>-        "0      Choose the branch which does not jump \n" <>-        "1      Choose the branch which does jump \n" <>-        "Down   Step to next entry in the callstack / Scroll memory pane\n" <>-        "Up     Step to previous entry in the callstack / Scroll memory pane\n" <>-        "C-f    Page memory pane fwds\n" <>-        "C-b    Page memory pane back\n\n" <>-        "Enter  Contracts browser"-    ]-    where-      version =-        txt "Hevm " <+>-        str (showVersion Paths.version) <+>-        txt " - Key bindings"--drawTestPicker :: UiTestPickerState -> [UiWidget]-drawTestPicker ui =-  [ center . borderWithLabel (txt "Unit tests") .-      hLimit 80 $-        renderList-          (\selected (x, y) ->-             withHighlight selected $-               txt " Debug " <+> txt (contractNamePart x) <+> txt "::" <+> txt y)-          True-          ui.tests-  ]--drawVmBrowser :: UiBrowserState -> [UiWidget]-drawVmBrowser ui =-  [ hBox-      [ borderWithLabel (txt "Contracts") .-          hLimit 60 $-            renderList-              (\selected (k, c') ->-                 withHighlight selected . txt . mconcat $-                   [ fromMaybe "<unknown contract>" $-                       Map.lookup (maybeHash c') dapp.solcByHash <&> (.contractName) . snd-                   , "\n"-                   , "  ", pack (show k)-                   ])-              True-              ui.contracts-      , case snd <$> Map.lookup (maybeHash c) dapp.solcByHash of-          Nothing ->-            hBox-              [ borderWithLabel (txt "Contract information") . padBottom Max . padRight Max $ vBox-                  [ txt ("Codehash: " <> pack (show c.codehash))-                  , txt ("Nonce: "    <> showWordExact c.nonce)-                  , txt ("Balance: "  <> showWordExact c.balance)-                  --, txt ("Storage: "  <> storageDisplay (view storage c)) -- TODO: fix this-                  ]-                ]-          Just sol ->-            hBox-              [ borderWithLabel (txt "Contract information") . padBottom Max . padRight (Pad 2) $ vBox-                  [ txt "Name: " <+> txt (contractNamePart sol.contractName)-                  , txt "File: " <+> txt (contractPathPart sol.contractName)-                  , txt " "-                  , txt "Constructor inputs:"-                  , vBox . flip map sol.constructorInputs $-                      \(name, abiType) -> txt ("  " <> name <> ": " <> abiTypeSolidity abiType)-                  , txt "Public methods:"-                  , vBox . flip map (sort (Map.elems sol.abiMap)) $-                      \method -> txt ("  " <> method.methodSignature)-                  --, txt ("Storage:" <> storageDisplay (view storage c)) -- TODO: fix this-                  ]-              , borderWithLabel (txt "Storage slots") . padBottom Max . padRight Max $ vBox-                  (map txt (storageLayout dapp sol))-              ]-      ]-  ]-  where-    dapp = ui.vm.testOpts.dapp-    (_, (_, c)) = fromJust $ listSelectedElement ui.contracts---        currentContract  = view (dappSolcByHash . ix ) dapp-    maybeHash ch = fromJust (internalError "cannot find concrete codehash for partially symbolic code") (maybeLitWord ch.codehash)--drawVm :: UiVmState -> [UiWidget]-drawVm ui =-  -- EVM debugging needs a lot of space because of the 256-bit words-  -- in both the bytecode and the stack .-  ---  -- If on a very tall display, prefer a vertical layout.-  ---  -- Actually the horizontal layout would be preferrable if the display-  -- is both very tall and very wide, but this is okay for now.-  [ ifTallEnough (20 * 4)-      ( vBox-        [ vLimit 20 $ drawBytecodePane ui-        , vLimit 20 $ drawStackPane ui-        , drawSolidityPane ui-        , vLimit 20 $ drawTracePane ui-        , vLimit 2 drawHelpBar-        ]-      )-      ( vBox-        [ hBox-          [ vLimit 20 $ drawBytecodePane ui-          , vLimit 20 $ drawStackPane ui-          ]-        , hBox-          [ drawSolidityPane ui-          , drawTracePane ui-          ]-        , vLimit 2 drawHelpBar-        ]-      )-  ]--drawHelpBar :: UiWidget-drawHelpBar = hBorder <=> hCenter help-  where-    help =-      hBox (map (\(k, v) -> txt k <+> dim (txt (" (" <> v <> ")  "))) helps)--    helps =-      [-        ("n", "step")-      , ("p", "step back")-      , ("a", "step to start")-      , ("e", "step to end")-      , ("m", "toggle memory")-      , ("Esc", "exit")-      , ("h", "more help")-      ]--stepOneOpcode :: Stepper a -> StateT UiVmState IO ()-stepOneOpcode restart = do-  n <- use #step-  when (n > 0 && n `mod` snapshotInterval == 0) $ do-    vm <- use #vm-    modifying #snapshots (insert n (vm, void restart))-  modifying #vm (execState exec1)-  modifying #step (+ 1)--isNewTraceAdded-  :: UiVmState -> Pred VM-isNewTraceAdded ui vm =-  let-    currentTraceTree = length <$> traceForest ui.vm-    newTraceTree = length <$> traceForest vm-  in currentTraceTree /= newTraceTree--isNextSourcePosition-  :: UiVmState -> Pred VM-isNextSourcePosition ui vm =-  let dapp = ui.testOpts.dapp-      initialPosition = currentSrcMap dapp ui.vm-  in currentSrcMap dapp vm /= initialPosition--isNextSourcePositionWithoutEntering-  :: UiVmState -> Pred VM-isNextSourcePositionWithoutEntering ui vm =-  let-    dapp            = ui.testOpts.dapp-    vm0             = ui.vm-    initialPosition = currentSrcMap dapp vm0-    initialHeight   = length vm0.frames-  in-    case currentSrcMap dapp vm of-      Nothing ->-        False-      Just here ->-        let-          moved = Just here /= initialPosition-          deeper = length vm.frames > initialHeight-          boring =-            case srcMapCode dapp.sources here of-              Just bs ->-                BS.isPrefixOf "contract " bs-              Nothing ->-                True-        in-           moved && not deeper && not boring--isExecutionHalted :: UiVmState -> Pred VM-isExecutionHalted _ vm = isJust vm.result--currentSrcMap :: DappInfo -> VM -> Maybe SrcMap-currentSrcMap dapp vm = do-  this <- currentContract vm-  i <- this.opIxMap SVec.!? vm.state.pc-  srcMap dapp this i--drawStackPane :: UiVmState -> UiWidget-drawStackPane ui =-  let-    gasText = showWordExact (into ui.vm.state.gas)-    labelText = txt ("Gas available: " <> gasText <> "; stack:")-    stackList = list StackPane (Vec.fromList $ zip [(1 :: Int)..] (simplify <$> ui.vm.state.stack)) 2-  in hBorderWithLabel labelText <=>-    renderList-      (\_ (i, w) ->-         vBox-           [ withHighlight True (str ("#" ++ show i ++ " "))-               <+> ourWrap (Text.unpack $ prettyIfConcreteWord w)-           , dim (txt ("   " <> case maybeLitWord w of-                       Nothing -> ""-                       Just u -> showWordExplanation u ui.testOpts.dapp))-           ])-      False-      stackList--message :: VM -> String-message vm =-  case vm.result of-    Just (VMSuccess (ConcreteBuf msg)) ->-      "VMSuccess: " <> (show $ ByteStringS msg)-    Just (VMSuccess (msg)) ->-      "VMSuccess: <symbolicbuffer> " <> (show msg)-    Just (VMFailure (Revert msg)) ->-      "VMFailure: " <> (show msg)-    Just (VMFailure err) ->-      "VMFailure: " <> show err-    Just (Unfinished p) ->-      "Could not continue execution: " <> show p-    Just (HandleEffect e) ->-      "Handling side effect: " <> show e-    Nothing ->-      "Executing EVM code in " <> show vm.state.contract---drawBytecodePane :: UiVmState -> UiWidget-drawBytecodePane ui =-  let-    vm = ui.vm-    move = maybe id listMoveTo $ vmOpIx vm-  in-    hBorderWithLabel (str $ message vm) <=>-    Centered.renderList-      (\active x -> if not active-                    then withDefAttr dimAttr (opWidget x)-                    else withDefAttr boldAttr (opWidget x))-      False-      (move $ list BytecodePane-        (maybe mempty (.codeOps) (currentContract vm))-        1)---dim :: Widget n -> Widget n-dim = withDefAttr dimAttr--withHighlight :: Bool -> Widget n -> Widget n-withHighlight False = withDefAttr dimAttr-withHighlight True  = withDefAttr boldAttr--prettyIfConcrete :: Expr Buf -> String-prettyIfConcrete (ConcreteBuf x) = prettyHex 40 x-prettyIfConcrete x = T.unpack $ formatExpr $ simplify x--drawTracePane :: UiVmState -> UiWidget-drawTracePane s =-  let vm = s.vm-      dapp = s.testOpts.dapp-      traceList =-        list-          TracePane-          (Vec.fromList-            . Text.lines-            . showTraceTree dapp-            $ vm)-          1--  in case s.showMemory of-    True -> viewport TracePane Vertical $-        hBorderWithLabel (txt "Calldata")-        <=> ourWrap (prettyIfConcrete vm.state.calldata)-        <=> hBorderWithLabel (txt "Returndata")-        <=> ourWrap (prettyIfConcrete vm.state.returndata)-        <=> hBorderWithLabel (txt "Output")-        <=> ourWrap (maybe "" show vm.result)-        <=> hBorderWithLabel (txt "Cache")-        <=> ourWrap (show vm.cache.path)-        <=> hBorderWithLabel (txt "Path Conditions")-        <=> (ourWrap $ show $ vm.constraints)-        <=> hBorderWithLabel (txt "Memory")-        <=> (ourWrap (prettyIfConcrete vm.state.memory))-    False ->-      hBorderWithLabel (txt "Trace")-      <=> renderList-            (\_ x -> txt x)-            False-            (listMoveTo (length traceList) traceList)--ourWrap :: String -> Widget n-ourWrap = strWrapWith settings-  where-    settings = WrapSettings-      { preserveIndentation = True-      , breakLongWords = True-      , fillStrategy = NoFill-      , fillScope = FillAfterFirst-      }--solidityList :: VM -> DappInfo -> List Name (Int, ByteString)-solidityList vm dapp =-  list SolidityPane-    (case currentSrcMap dapp vm of-        Nothing -> mempty-        Just x ->-          fromMaybe-            (internalError "unable to find line for source map")-            (preview (-              ix x.file-              % to (Vec.imap (,)))-            dapp.sources.lines))-    1--drawSolidityPane :: UiVmState -> UiWidget-drawSolidityPane ui =-  let dapp = ui.testOpts.dapp-      dappSrcs = dapp.sources-      vm = ui.vm-  in case currentSrcMap dapp vm of-    Nothing -> padBottom Max (hBorderWithLabel (txt "<no source map>"))-    Just sm ->-          let-            rows = dappSrcs.lines !? sm.file-            subrange :: Int -> Maybe (Int, Int)-            subrange i = do-              rs <- rows-              lineSubrange rs (sm.offset, sm.length) i-            fileName :: Maybe Text-            fileName = T.pack . fst <$> (dapp.sources.files !? sm.file)-            lineNo :: Maybe Int-            lineNo = ((\a -> Just (a - 1)) . snd) =<< srcMapCodePos dapp.sources sm-          in vBox-            [ hBorderWithLabel $-                txt (fromMaybe "<unknown>" fileName)-                  <+> str (":" ++ (maybe "?" show lineNo))--                  -- Show the AST node type if present-                  <+> txt (" (" <> fromMaybe "?"-                                    (dapp.astSrcMap sm-                                       >>= preview (key "name" % _String)) <> ")")-            , Centered.renderList-                (\_ (i, line) ->-                   let s = case decodeUtf8 line of "" -> " "; y -> y-                   in case subrange i of-                        Nothing -> withHighlight False (txt s)-                        Just (a, b) ->-                          let (x, y, z) = ( Text.take a s-                                          , Text.take b (Text.drop a s)-                                          , Text.drop (a + b) s-                                          )-                          in hBox [ withHighlight False (txt x)-                                  , withHighlight True (txt y)-                                  , withHighlight False (txt z)-                                  ])-                False-                ((maybe id listMoveTo lineNo)-                  (solidityList vm dapp))-            ]--ifTallEnough :: Int -> Widget n -> Widget n -> Widget n-ifTallEnough need w1 w2 =-  Widget Greedy Greedy $ do-    c <- getContext-    if view (lensVL availHeightL) c > need-      then render w1-      else render w2--opWidget :: (Integral a, Show a) => (a, Op) -> Widget n-opWidget = txt . pack . opString--selectedAttr :: AttrName; selectedAttr = attrName "selected"-dimAttr :: AttrName; dimAttr = attrName "dim"-wordAttr :: AttrName; wordAttr = attrName "word"-boldAttr :: AttrName; boldAttr = attrName "bold"-activeAttr :: AttrName; activeAttr = attrName "active"
− src/EVM/TTYCenteredList.hs
@@ -1,70 +0,0 @@-module EVM.TTYCenteredList where---- Hard fork of brick's List that centers the currently highlighted line.--import Optics.Core-import Data.Maybe (fromMaybe)-import Data.Vector qualified as V--import Brick.Types-import Brick.Widgets.Core-import Brick.Widgets.List---- | Turn a list state value into a widget given an item drawing--- function.-renderList :: (Ord n, Show n)-           => (Bool -> e -> Widget n)-           -- ^ Rendering function, True for the selected element-           -> Bool-           -- ^ Whether the list has focus-           -> List n e-           -- ^ The List to be rendered-           -> Widget n-           -- ^ rendered widget-renderList drawElem foc l =-    withDefAttr listAttr $-    drawListElements foc l drawElem--drawListElements :: (Ord n, Show n) => Bool -> List n e -> (Bool -> e -> Widget n) -> Widget n-drawListElements foc l drawElem =-  Widget Greedy Greedy $ do-    c <- getContext--    let es = V.slice start num (l ^. (lensVL listElementsL))-        idx = fromMaybe 0 (l ^. (lensVL listSelectedL))--        start = max 0 $ idx - (initialNumPerHeight `div` 2)-        num = min (numPerHeight * 2) (V.length (l ^. (lensVL listElementsL)) - start)--        -- The number of items to show is the available height divided by-        -- the item height...-        initialNumPerHeight = (c ^. (lensVL availHeightL)) `div` (l ^. (lensVL listItemHeightL))-        -- ... but if the available height leaves a remainder of-        -- an item height then we need to ensure that we render an-        -- extra item to show a partial item at the top or bottom to-        -- give the expected result when an item is more than one-        -- row high. (Example: 5 rows available with item height-        -- of 3 yields two items: one fully rendered, the other-        -- rendered with only its top 2 or bottom 2 rows visible,-        -- depending on how the viewport state changes.)-        numPerHeight = initialNumPerHeight +-                       if initialNumPerHeight * (l ^. (lensVL listItemHeightL)) == c ^. (lensVL availHeightL)-                       then 0-                       else 1--        -- off = start * (l^.listItemHeightL)--        drawnElements = flip V.imap es $ \i e ->-            let isSelected = i == (if start == 0 then idx else div initialNumPerHeight 2)-                elemWidget = drawElem isSelected e-                selItemAttr = if foc-                              then withDefAttr listSelectedFocusedAttr-                              else withDefAttr listSelectedAttr-                makeVisible = if isSelected-                              then visible . selItemAttr-                              else id-            in makeVisible elemWidget--    render $ viewport (l ^. (lensVL listNameL)) Vertical $-             -- translateBy (Location (0, off)) $-             vBox $ V.toList drawnElements
src/EVM/Transaction.hs view
@@ -5,8 +5,8 @@ import EVM.RLP import EVM.Types import EVM.Format (hexText)-import EVM.Expr (litAddr) import EVM.Sign+import qualified EVM.Expr as Expr  import Optics.Core hiding (cons) @@ -61,7 +61,7 @@  instance JSON.ToJSON Transaction where   toJSON t = JSON.object [ ("input",             (JSON.toJSON (ByteStringS t.txdata)))-                         , ("gas",               (JSON.toJSON $ "0x" ++ showHex (toInteger $ t.gasLimit) ""))+                         , ("gas",               (JSON.toJSON $ "0x" ++ showHex (into @Integer $ t.gasLimit) ""))                          , ("gasPrice",          (JSON.toJSON $ show $ fromJust $ t.gasPrice))                          , ("v",                 (JSON.toJSON $ show $ (t.v)-27))                          , ("r",                 (JSON.toJSON $ show $ t.r))@@ -225,28 +225,28 @@   parseJSON invalid =     JSON.typeMismatch "Transaction" invalid -accountAt :: Addr -> Getter (Map Addr Contract) Contract+accountAt :: Expr EAddr -> Getter (Map (Expr EAddr) Contract) Contract accountAt a = (at a) % (to $ fromMaybe newAccount) -touchAccount :: Addr -> Map Addr Contract -> Map Addr Contract+touchAccount :: Expr EAddr -> Map (Expr EAddr) Contract -> Map (Expr EAddr) Contract touchAccount a = Map.insertWith (flip const) a newAccount  newAccount :: Contract-newAccount = initialContract $ RuntimeCode (ConcreteRuntimeCode "")+newAccount = initialContract (RuntimeCode (ConcreteRuntimeCode ""))  -- | Increments origin nonce and pays gas deposit-setupTx :: Addr -> Addr -> W256 -> Word64 -> Map Addr Contract -> Map Addr Contract+setupTx :: Expr EAddr -> Expr EAddr -> W256 -> Word64 -> Map (Expr EAddr) Contract -> Map (Expr EAddr) Contract setupTx origin coinbase gasPrice gasLimit prestate =   let gasCost = gasPrice * (into gasLimit)-  in (Map.adjust ((over #nonce   (+ 1))-               . (over #balance (subtract gasCost))) origin)+  in (Map.adjust ((over #nonce   (fmap ((+) 1)))+               . (over #balance (`Expr.sub` (Lit gasCost)))) origin)     . touchAccount origin     . touchAccount coinbase $ prestate  -- | Given a valid tx loaded into the vm state, -- subtract gas payment from the origin, increment the nonce -- and pay receiving address-initTx :: VM -> VM+initTx :: VM s -> VM s initTx vm = let     toAddr   = vm.state.contract     origin   = vm.tx.origin@@ -258,23 +258,13 @@     preState = setupTx origin coinbase gasPrice gasLimit vm.env.contracts     oldBalance = view (accountAt toAddr % #balance) preState     creation = vm.tx.isCreate-    initState = (case maybeLitWord value of-      Just v -> ((Map.adjust (over #balance (subtract v))) origin)-              . (Map.adjust (over #balance (+ v))) toAddr-      Nothing -> id)+    initState =+        ((Map.adjust (over #balance (`Expr.sub` value))) origin)+      . (Map.adjust (over #balance (Expr.add value))) toAddr       . (if creation-         then Map.insert toAddr (toContract & #balance .~ oldBalance)+         then Map.insert toAddr (toContract & (set #balance oldBalance))          else touchAccount toAddr)       $ preState--    resetConcreteStore s = if creation then Map.insert (into toAddr) mempty s else s--    resetStore (ConcreteStore s) = ConcreteStore (resetConcreteStore s)-    resetStore (SStore a@(Lit _) k v s) = if creation && a == (litAddr toAddr) then resetStore s else (SStore a k v (resetStore s))-    resetStore (SStore {}) = internalError "cannot reset storage if it contains symbolic addresses"-    resetStore s = s     in       vm & #env % #contracts .~ initState          & #tx % #txReversion .~ preState-         & #env % #storage %~ resetStore-         & #env % #origStorage %~ resetConcreteStore
src/EVM/Traversals.hs view
@@ -7,6 +7,8 @@ import Prelude hiding (LT, GT)  import Control.Monad.Identity+import qualified Data.Map.Strict as Map+import Data.List (foldl')  import EVM.Types @@ -26,26 +28,28 @@       POr a b -> go a <> go b       PImpl a b -> go a <> go b -foldTrace :: forall b . Monoid b => (forall a . Expr a -> b) -> b -> Trace -> b-foldTrace f acc t = acc <> (go t)-  where-    go :: Trace -> b-    go (Trace _ _ d) = case d of-      EventTrace a b c -> foldExpr f mempty a <> foldExpr f mempty b <> (foldl (foldExpr f) mempty c)-      FrameTrace a -> go' a-      ErrorTrace _ -> mempty-      EntryTrace _ -> mempty-      ReturnTrace a b -> foldExpr f mempty a <> go' b--    go' :: FrameContext -> b-    go' = \case-      CreationContext _ b _ _ -> foldExpr f mempty b-      CallContext _ _ _ _ e _ g (_, h) _ -> foldExpr f mempty e <> foldExpr f mempty g <> foldExpr f mempty h--foldTraces :: forall b . Monoid b => (forall a . Expr a -> b) -> b -> Traces -> b-foldTraces f acc (Traces a _) = acc <> foldl (foldl (foldTrace f)) mempty a+foldEContract :: forall b . Monoid b => (forall a . Expr a -> b) -> b -> Expr EContract -> b+foldEContract f _ g@(GVar _) = f g+foldEContract f acc (C code storage balance _)+  =  acc+  <> foldCode f code+  <> foldExpr f mempty storage+  <> foldExpr f mempty balance +foldContract :: forall b . Monoid b => (forall a . Expr a -> b) -> b -> Contract -> b+foldContract f acc c+  =  acc+  <> foldCode f c.code+  <> foldExpr f mempty c.storage+  <> foldExpr f mempty c.origStorage+  <> foldExpr f mempty c.balance +foldCode :: forall b . Monoid b => (forall a . Expr a -> b) -> ContractCode -> b+foldCode f = \case+  RuntimeCode (ConcreteRuntimeCode _) -> mempty+  RuntimeCode (SymbolicRuntimeCode c) -> foldl' (foldExpr f) mempty c+  InitCode _ buf -> foldExpr f mempty buf+  UnknownCode addr -> foldExpr f mempty addr  -- | Recursively folds a given function over a given expression -- Recursion schemes do this & a lot more, but defining them over GADT's isn't worth the hassle@@ -62,6 +66,10 @@       e@(Var _) -> f e       e@(GVar _) -> f e +      -- contracts++      e@(C {}) -> foldEContract f acc e+       -- bytes        e@(IndexWord a b) -> f e <> (go a) <> (go b)@@ -86,9 +94,14 @@        -- control flow -      e@(Success a b c d) -> f e <> (foldl (foldProp f) mempty a) <> foldTraces f mempty b <> (go c) <> (go d)-      e@(Failure a b _) -> f e <> (foldl (foldProp f) mempty a) <> foldTraces f mempty b-      e@(Partial a b _) -> f e <> (foldl (foldProp f) mempty a) <> foldTraces f mempty b+      e@(Success a _ c d) -> f e+                          <> foldl (foldProp f) mempty a+                          <> go c+                          <> foldl' (foldExpr f) mempty (Map.keys d)+                          <> foldl' (foldEContract f) mempty d+      e@(Failure a _ (Revert c)) -> f e <> (foldl (foldProp f) mempty a) <> go c+      e@(Failure a _ _) -> f e <> (foldl (foldProp f) mempty a)+      e@(Partial a _ _) -> f e <> (foldl (foldProp f) mempty a)       e@(ITE a b c) -> f e <> (go a) <> (go b) <> (go c)        -- integers@@ -145,89 +158,36 @@       e@(BaseFee) -> f e       e@(BlockHash a) -> f e <> (go a) +      -- tx context++      e@(TxValue) -> f e+       -- frame context -      e@(Caller _) -> f e-      e@(CallValue _) -> f e-      e@(Address _) -> f e-      e@(SelfBalance _ _) -> f e       e@(Gas _ _) -> f e       e@(Balance {}) -> f e        -- code        e@(CodeSize a) -> f e <> (go a)-      e@(ExtCodeHash a) -> f e <> (go a)+      e@(CodeHash a) -> f e <> (go a)        -- logs        e@(LogEntry a b c) -> f e <> (go a) <> (go b) <> (foldl (<>) mempty (fmap f c)) -      -- Contract Creation--      e@(Create a b c d g h)-        -> f e-        <> (go a)-        <> (go b)-        <> (go c)-        <> (go d)-        <> (foldl (<>) mempty (fmap go g))-        <> (go h)-      e@(Create2 a b c d g h i)-        -> f e-        <> (go a)-        <> (go b)-        <> (go c)-        <> (go d)-        <> (go g)-        <> (foldl (<>) mempty (fmap go h))-        <> (go i)--      -- Calls--      e@(Call a b c d g h i j k)-        -> f e-        <> (go a)-        <> (maybe mempty (go) b)-        <> (go c)-        <> (go d)-        <> (go g)-        <> (go h)-        <> (go i)-        <> (foldl (<>) mempty (fmap go j))-        <> (go k)--      e@(CallCode a b c d g h i j k)-        -> f e-        <> (go a)-        <> (go b)-        <> (go c)-        <> (go d)-        <> (go g)-        <> (go h)-        <> (go i)-        <> (foldl (<>) mempty (fmap go j))-        <> (go k)+      -- storage -      e@(DelegeateCall a b c d g h i j k)-        -> f e-        <> (go a)-        <> (go b)-        <> (go c)-        <> (go d)-        <> (go g)-        <> (go h)-        <> (go i)-        <> (foldl (<>) mempty (fmap go j))-        <> (go k)+      e@(LitAddr _) -> f e+      e@(WAddr a) -> f e <> go a+      e@(SymAddr _) -> f e        -- storage -      e@(EmptyStore) -> f e       e@(ConcreteStore _) -> f e-      e@(AbstractStore) -> f e-      e@(SLoad a b c) -> f e <> (go a) <> (go b) <> (go c)-      e@(SStore a b c d) -> f e <> (go a) <> (go b) <> (go c) <> (go d)+      e@(AbstractStore _) -> f e+      e@(SLoad a b) -> f e <> (go a) <> (go b)+      e@(SStore a b c) -> f e <> (go a) <> (go b) <> (go c)        -- buffers @@ -260,28 +220,22 @@   POr a b -> POr (mapProp f a) (mapProp f b)   PImpl a b -> PImpl (mapProp f a) (mapProp f b) -mapTrace :: (forall a . Expr a -> Expr a) -> Trace -> Trace-mapTrace f (Trace x y z) = Trace x y (go z)-  where-    go :: TraceData -> TraceData-    go = \case-      EventTrace a b c -> EventTrace (f a) (f b) (fmap (mapExpr f) c)-      FrameTrace a -> FrameTrace (go' a)-      ErrorTrace a -> ErrorTrace a-      EntryTrace a -> EntryTrace a-      ReturnTrace a b -> ReturnTrace (f a) (go' b)--    go' :: FrameContext -> FrameContext-    go' = \case-      CreationContext a b c d -> CreationContext a (f b) c d-      CallContext a b c d e g h (i,j) k -> CallContext a b c d (f e) g (f h) (i,f j) k+mapProp' :: (Prop -> Prop) -> Prop -> Prop+mapProp' f = \case+  PBool b -> f $ PBool b+  PEq a b -> f $ PEq a b+  PLT a b -> f $ PLT a b+  PGT a b -> f $ PGT a b+  PLEq a b -> f $ PLEq a b+  PGEq a b -> f $ PGEq a b+  PNeg a -> f $ PNeg (mapProp' f a)+  PAnd a b -> f $ PAnd (mapProp' f a) (mapProp' f b)+  POr a b -> f $ POr (mapProp' f a) (mapProp' f b)+  PImpl a b -> f $ PImpl (mapProp' f a) (mapProp' f b) --- | Recursively applies a given function to every node in a given expr instance--- Recursion schemes do this & a lot more, but defining them over GADT's isn't worth the hassle mapExpr :: (forall a . Expr a -> Expr a) -> Expr b -> Expr b mapExpr f expr = runIdentity (mapExprM (Identity . f) expr) - mapExprM :: Monad m => (forall a . Expr a -> m (Expr a)) -> Expr b -> m (Expr b) mapExprM f expr = case expr of @@ -292,6 +246,18 @@   Var a -> f (Var a)   GVar s -> f (GVar s) +  -- addresses++  c@(C {}) -> mapEContractM f c++  -- addresses++  LitAddr a -> f (LitAddr a)+  SymAddr a -> f (SymAddr a)+  WAddr a -> do+    a' <- mapExprM f a+    f (WAddr a')+   -- bytes    IndexWord a b -> do@@ -348,19 +314,21 @@    Failure a b c -> do     a' <- mapM (mapPropM f) a-    b' <- mapTracesM f b-    f (Failure a' b' c)+    f (Failure a' b c)   Partial a b c -> do     a' <- mapM (mapPropM f) a-    b' <- mapTracesM f b-    f (Partial a' b' c)+    f (Partial a' b c)   Success a b c d -> do     a' <- mapM (mapPropM f) a-    b' <- mapTracesM f b     c' <- mapExprM f c-    d' <- mapExprM f d-    f (Success a' b' c' d')-+    d' <- do+      let x = Map.toList d+      x' <- forM x $ \(k,v) -> do+        k' <- f k+        v' <- mapEContractM f v+        pure (k',v')+      pure $ Map.fromList x'+    f (Success a' b c' d')   ITE a b c -> do     a' <- mapExprM f a     b' <- mapExprM f b@@ -513,25 +481,25 @@     a' <- mapExprM f a     f (BlockHash a') +  -- tx context++  TxValue -> f TxValue+   -- frame context -  Caller a -> f (Caller a)-  CallValue a -> f (CallValue a)-  Address a -> f (Address a)-  SelfBalance a b -> f (SelfBalance a b)   Gas a b -> f (Gas a b)-  Balance a b c -> do-    c' <- mapExprM f c-    f (Balance a b c')+  Balance a -> do+    a' <- mapExprM f a+    f (Balance a')    -- code    CodeSize a -> do     a' <- mapExprM f a     f (CodeSize a')-  ExtCodeHash a -> do+  CodeHash a -> do     a' <- mapExprM f a-    f (ExtCodeHash a')+    f (CodeHash a')    -- logs @@ -541,78 +509,19 @@     c' <- mapM (mapExprM f) c     f (LogEntry a' b' c') -  -- Contract Creation--  Create a b c d e g -> do-    a' <- mapExprM f a-    b' <- mapExprM f b-    c' <- mapExprM f c-    d' <- mapExprM f d-    e' <- mapM (mapExprM f) e-    g' <- mapExprM f g-    f (Create a' b' c' d' e' g')-  Create2 a b c d e g h -> do-    a' <- mapExprM f a-    b' <- mapExprM f b-    c' <- mapExprM f c-    d' <- mapExprM f d-    e' <- mapExprM f e-    g' <- mapM (mapExprM f) g-    h' <- mapExprM f h-    f (Create2 a' b' c' d' e' g' h')--  -- Calls--  Call a b c d e g h i j -> do-    a' <- mapExprM f a-    b' <- mapM (mapExprM f) b-    c' <- mapExprM f c-    d' <- mapExprM f d-    e' <- mapExprM f e-    g' <- mapExprM f g-    h' <- mapExprM f h-    i' <- mapM (mapExprM f) i-    j' <- mapExprM f j-    f (Call a' b' c' d' e' g' h' i' j')-  CallCode a b c d e g h i j -> do-    a' <- mapExprM f a-    b' <- mapExprM f b-    c' <- mapExprM f c-    d' <- mapExprM f d-    e' <- mapExprM f e-    g' <- mapExprM f g-    h' <- mapExprM f h-    i' <- mapM (mapExprM f) i-    j' <- mapExprM f j-    f (CallCode a' b' c' d' e' g' h' i' j')-  DelegeateCall a b c d e g h i j -> do-    a' <- mapExprM f a-    b' <- mapExprM f b-    c' <- mapExprM f c-    d' <- mapExprM f d-    e' <- mapExprM f e-    g' <- mapExprM f g-    h' <- mapExprM f h-    i' <- mapM (mapExprM f) i-    j' <- mapExprM f j-    f (DelegeateCall a' b' c' d' e' g' h' i' j')-   -- storage -  EmptyStore -> f EmptyStore-  ConcreteStore a -> f (ConcreteStore a)-  AbstractStore -> f AbstractStore-  SLoad a b c -> do+  ConcreteStore b -> f (ConcreteStore b)+  AbstractStore a -> f (AbstractStore a)+  SLoad a b -> do     a' <- mapExprM f a     b' <- mapExprM f b-    c' <- mapExprM f c-    f (SLoad a' b' c')-  SStore a b c d -> do+    f (SLoad a' b')+  SStore a b c -> do     a' <- mapExprM f a     b' <- mapExprM f b     c' <- mapExprM f c-    d' <- mapExprM f d-    f (SStore a' b' c' d')+    f (SStore a' b' c')    -- buffers @@ -651,7 +560,6 @@     a' <- mapExprM f a     f (BufLength a') - mapPropM :: Monad m => (forall a . Expr a -> m (Expr a)) -> Prop -> m Prop mapPropM f = \case   PBool b -> pure $ PBool b@@ -691,48 +599,38 @@     b' <- mapPropM f b     pure $ PImpl a' b' -mapTracesM :: forall m . Monad m => (forall a . Expr a -> m (Expr a)) -> Traces -> m Traces-mapTracesM f (Traces a b) = do-  a' <- mapM (mapM (mapTraceM f)) a-  pure $ Traces a' b -mapTraceM :: forall m . Monad m => (forall a . Expr a -> m (Expr a)) -> Trace -> m Trace-mapTraceM f (Trace x y z) = do-  z' <- go z-  pure $ Trace x y z'-  where-    go :: TraceData -> m TraceData-    go = \case-      EventTrace a b c -> do-        a' <- mapExprM f a-        b' <- mapExprM f b-        c' <- mapM (mapExprM f) c-        pure $ EventTrace a' b' c'-      FrameTrace a -> do-        a' <- go' a-        pure $ FrameTrace a'-      ReturnTrace a b -> do-        a' <- mapExprM f a-        b' <- go' b-        pure $ ReturnTrace a' b'-      a -> pure a+mapEContractM :: Monad m => (forall a . Expr a -> m (Expr a)) -> Expr EContract -> m (Expr EContract)+mapEContractM _ g@(GVar _) = pure g+mapEContractM f (C code storage balance nonce) = do+  code' <- mapCodeM f code+  storage' <- mapExprM f storage+  balance' <- mapExprM f balance+  pure $ C code' storage' balance' nonce -    go' :: FrameContext -> m FrameContext-    go' = \case-      CreationContext a b c d -> do-        b' <- mapExprM f b-        pure $ CreationContext a b' c d-      CallContext a b c d e g h (i,j) k -> do-        e' <- mapExprM f e-        h' <- mapExprM f h-        j' <- mapExprM f j-        pure $ CallContext a b c d e' g h' (i,j') k+mapContractM :: Monad m => (forall a . Expr a -> m (Expr a)) -> Contract -> m (Contract)+mapContractM f c = do+  code' <- mapCodeM f c.code+  storage' <- mapExprM f c.storage+  origStorage' <- mapExprM f c.origStorage+  balance' <- mapExprM f c.balance+  pure $ c { code = code', storage = storage', origStorage = origStorage', balance = balance' } +mapCodeM :: Monad m => (forall a . Expr a -> m (Expr a)) -> ContractCode -> m (ContractCode)+mapCodeM f = \case+  UnknownCode a -> fmap UnknownCode (f a)+  c@(RuntimeCode (ConcreteRuntimeCode _)) -> pure c+  RuntimeCode (SymbolicRuntimeCode c) -> do+    c' <- mapM (mapExprM f) c+    pure . RuntimeCode $ SymbolicRuntimeCode c'+  InitCode bs buf -> do+    buf' <- mapExprM f buf+    pure $ InitCode bs buf'+ -- | Generic operations over AST terms class TraversableTerm a where   mapTerm  :: (forall b. Expr b -> Expr b) -> a -> a   foldTerm :: forall c. Monoid c => (forall b. Expr b -> c) -> c -> a -> c-  instance TraversableTerm (Expr a) where   mapTerm = mapExpr
src/EVM/Types.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE UndecidableInstances #-} @@ -8,13 +9,15 @@  import GHC.Stack (HasCallStack, prettyCallStack, callStack) import Control.Arrow ((>>>))-import Control.Monad.State.Strict (State, mzero)+import Control.Monad.ST (ST)+import Control.Monad.State.Strict (StateT, mzero) import Crypto.Hash (hash, Keccak_256, Digest) import Data.Aeson import Data.Aeson qualified as JSON import Data.Aeson.Types qualified as JSON import Data.Bifunctor (first) import Data.Bits (Bits, FiniteBits, shiftR, shift, shiftL, (.&.), (.|.), toIntegralSized)+import Data.Binary qualified as Binary import Data.ByteArray qualified as BA import Data.Char import Data.List (foldl')@@ -41,10 +44,10 @@ import Data.Tree.Zipper qualified as Zipper import Data.Vector qualified as V import Data.Vector.Storable qualified as SV+import Data.Vector.Unboxed.Mutable (STVector) import Numeric (readHex, showHex) import Options.Generic import Optics.TH-import EVM.Hexdump (paddedShowHex) import EVM.FeeSchedule (FeeSchedule (..))  import Text.Regex.TDFA qualified as Regex@@ -59,6 +62,14 @@ mkUnpackedDoubleWord "Word512" ''Word256 "Int512" ''Int256 ''Word256   [''Typeable, ''Data, ''Generic] +++-- Conversions -------------------------------------------------------------------------------------+++-- We ignore hlint to supress the warnings about `fromIntegral` and friends here+#ifndef __HLINT__+ instance From Addr Integer where from = fromIntegral instance From Addr W256 where from = fromIntegral instance From Int256 Integer where from = fromIntegral@@ -68,7 +79,9 @@ instance From Word8 Word256 where from = fromIntegral instance From Word32 W256 where from = fromIntegral instance From Word32 Word256 where from = fromIntegral+instance From Word32 ByteString where from = toStrict . Binary.encode instance From Word64 W256 where from = fromIntegral+instance From W64 W256 where from = fromIntegral instance From Word256 Integer where from = fromIntegral instance From Word256 W256 where from = fromIntegral @@ -76,6 +89,7 @@ instance TryFrom Int Word256 where tryFrom = maybeTryFrom toIntegralSized instance TryFrom Int256 W256 where tryFrom = maybeTryFrom toIntegralSized instance TryFrom Integer W256 where tryFrom = maybeTryFrom toIntegralSized+instance TryFrom Integer Addr where tryFrom = maybeTryFrom toIntegralSized -- TODO: hevm relies on this behavior instance TryFrom W256 Addr where tryFrom = Right . fromIntegral instance TryFrom W256 FunctionSelector where tryFrom = maybeTryFrom toIntegralSized@@ -86,6 +100,7 @@ instance TryFrom W256 Word32 where tryFrom = maybeTryFrom toIntegralSized -- TODO: hevm relies on this behavior instance TryFrom W256 Word64 where tryFrom = Right . fromIntegral+instance TryFrom W256 W64 where tryFrom = Right . fromIntegral instance TryFrom Word160 Word8 where tryFrom = maybeTryFrom toIntegralSized instance TryFrom Word256 Int where tryFrom = maybeTryFrom toIntegralSized instance TryFrom Word256 Int256 where tryFrom = maybeTryFrom toIntegralSized@@ -93,19 +108,27 @@ instance TryFrom Word256 Word32 where tryFrom = maybeTryFrom toIntegralSized instance TryFrom Word512 W256 where tryFrom = maybeTryFrom toIntegralSized +truncateToAddr :: W256 -> Addr+truncateToAddr = fromIntegral++#endif++ -- Symbolic IR ------------------------------------------------------------------------------------- + -- phantom type tags for AST construction data EType   = Buf   | Storage   | Log   | EWord+  | EAddr+  | EContract   | Byte   | End   deriving (Typeable) - -- Variables refering to a global environment data GVar (a :: EType) where   BufVar :: Int -> GVar Buf@@ -168,8 +191,11 @@    -- identifiers +  -- | Literal words   Lit            :: {-# UNPACK #-} !W256 -> Expr EWord+  -- | Variables   Var            :: Text -> Expr EWord+  -- | variables introduced during the CSE pass   GVar           :: GVar a -> Expr a    -- bytes@@ -192,7 +218,7 @@    Partial        :: [Prop] -> Traces -> PartialExec -> Expr End   Failure        :: [Prop] -> Traces -> EvmError -> Expr End-  Success        :: [Prop] -> Traces -> Expr Buf -> Expr Storage -> Expr End+  Success        :: [Prop] -> Traces -> Expr Buf -> Map (Expr EAddr) (Expr EContract) -> Expr End   ITE            :: Expr EWord -> Expr End -> Expr End -> Expr End    -- integers@@ -249,25 +275,13 @@   ChainId        :: Expr EWord   BaseFee        :: Expr EWord -  -- frame context--  CallValue      :: Int                -- frame idx-                 -> Expr EWord--  Caller         :: Int                -- frame idx-                 -> Expr EWord+  -- tx context -  Address        :: Int                -- frame idx-                 -> Expr EWord+  TxValue        :: Expr EWord -  Balance        :: Int                -- frame idx-                 -> Int                -- PC (in case we're checking the current contract)-                 -> Expr EWord         -- address-                 -> Expr EWord+  -- frame context -  SelfBalance    :: Int                -- frame idx-                 -> Int                -- PC-                 -> Expr EWord+  Balance        :: Expr EAddr -> Expr EWord    Gas            :: Int                -- frame idx                  -> Int                -- PC@@ -275,10 +289,10 @@    -- code -  CodeSize       :: Expr EWord         -- address+  CodeSize       :: Expr EAddr         -- address                  -> Expr EWord         -- size -  ExtCodeHash    :: Expr EWord         -- address+  CodeHash       :: Expr EAddr         -- address                  -> Expr EWord         -- size    -- logs@@ -288,73 +302,37 @@                  -> [Expr EWord]       -- topics                  -> Expr Log -  -- Contract Creation -  Create         :: Expr EWord         -- value-                 -> Expr EWord         -- offset-                 -> Expr EWord         -- size-                 -> Expr Buf           -- memory-                 -> [Expr Log]          -- logs-                 -> Expr Storage       -- storage-                 -> Expr EWord         -- address--  Create2        :: Expr EWord         -- value-                 -> Expr EWord         -- offset-                 -> Expr EWord         -- size-                 -> Expr EWord         -- salt-                 -> Expr Buf           -- memory-                 -> [Expr Log]          -- logs-                 -> Expr Storage       -- storage-                 -> Expr EWord         -- address--  -- Calls+  -- Contract -  Call           :: Expr EWord         -- gas-                 -> Maybe (Expr EWord) -- target-                 -> Expr EWord         -- value-                 -> Expr EWord         -- args offset-                 -> Expr EWord         -- args size-                 -> Expr EWord         -- ret offset-                 -> Expr EWord         -- ret size-                 -> [Expr Log]          -- logs-                 -> Expr Storage       -- storage-                 -> Expr EWord         -- success+  -- A restricted view of a contract that does not include extraneous metadata+  -- from the full constructor defined in the VM state+  C ::+    { code    :: ContractCode+    , storage :: Expr Storage+    , balance :: Expr EWord+    , nonce   :: Maybe W64+    } -> Expr EContract -  CallCode       :: Expr EWord         -- gas-                 -> Expr EWord         -- address-                 -> Expr EWord         -- value-                 -> Expr EWord         -- args offset-                 -> Expr EWord         -- args size-                 -> Expr EWord         -- ret offset-                 -> Expr EWord         -- ret size-                 -> [Expr Log]         -- logs-                 -> Expr Storage       -- storage-                 -> Expr EWord         -- success+  -- addresses -  DelegeateCall  :: Expr EWord         -- gas-                 -> Expr EWord         -- address-                 -> Expr EWord         -- value-                 -> Expr EWord         -- args offset-                 -> Expr EWord         -- args size-                 -> Expr EWord         -- ret offset-                 -> Expr EWord         -- ret size-                 -> [Expr Log]         -- logs-                 -> Expr Storage       -- storage-                 -> Expr EWord         -- success+  -- Symbolic addresses are identified with an int. It is important that+  -- semantic equality is the same as syntactic equality here. Additionally all+  -- SAddr's in a given expression should be constrained to differ from any LitAddr's+  SymAddr        :: Text -> Expr EAddr+  LitAddr        :: Addr -> Expr EAddr+  WAddr          :: Expr EAddr -> Expr EWord    -- storage -  EmptyStore     :: Expr Storage-  ConcreteStore  :: Map W256 (Map W256 W256) -> Expr Storage-  AbstractStore  :: Expr Storage+  ConcreteStore  :: (Map W256 W256) -> Expr Storage+  AbstractStore  :: Expr EAddr -> Expr Storage -  SLoad          :: Expr EWord         -- address-                 -> Expr EWord         -- index+  SLoad          :: Expr EWord         -- key                  -> Expr Storage       -- storage                  -> Expr EWord         -- result -  SStore         :: Expr EWord         -- address-                 -> Expr EWord         -- index+  SStore         :: Expr EWord         -- key                  -> Expr EWord         -- value                  -> Expr Storage       -- old storage                  -> Expr Storage       -- new storae@@ -514,12 +492,17 @@   _ <= _ = False  +isPBool :: Prop -> Bool+isPBool (PBool _) = True+isPBool _ = False++ -- Errors ------------------------------------------------------------------------------------------   -- | Core EVM Error Types data EvmError-  = BalanceTooLow W256 W256+  = BalanceTooLow (Expr EWord) (Expr EWord)   | UnrecognizedOpcode Word8   | SelfDestruction   | StackUnderrun@@ -542,35 +525,36 @@  -- | Sometimes we can only partially execute a given program data PartialExec-  = UnexpectedSymbolicArg  { pc :: Int, msg  :: String, args  :: [SomeExpr] }-  | MaxIterationsReached  { pc :: Int, addr :: Addr }+  = UnexpectedSymbolicArg { pc :: Int, msg  :: String, args  :: [SomeExpr] }+  | MaxIterationsReached  { pc :: Int, addr :: Expr EAddr }+  | JumpIntoSymbolicCode  { pc :: Int, jumpDst :: Int }   deriving (Show, Eq, Ord)  -- | Effect types used by the vm implementation for side effects & control flow-data Effect-  = Query Query-  | Choose Choose-deriving instance Show Effect+data Effect s+  = Query (Query s)+  | Choose (Choose s)+deriving instance Show (Effect s)  -- | Queries halt execution until resolved through RPC calls or SMT queries-data Query where-  PleaseFetchContract :: Addr -> (Contract -> EVM ()) -> Query-  PleaseFetchSlot     :: Addr -> W256 -> (W256 -> EVM ()) -> Query-  PleaseAskSMT        :: Expr EWord -> [Prop] -> (BranchCondition -> EVM ()) -> Query-  PleaseDoFFI         :: [String] -> (ByteString -> EVM ()) -> Query+data Query s where+  PleaseFetchContract :: Addr -> BaseState -> (Contract -> EVM s ()) -> Query s+  PleaseFetchSlot     :: Addr -> W256 -> (W256 -> EVM s ()) -> Query s+  PleaseAskSMT        :: Expr EWord -> [Prop] -> (BranchCondition -> EVM s ()) -> Query s+  PleaseDoFFI         :: [String] -> (ByteString -> EVM s ()) -> Query s  -- | Execution could proceed down one of two branches-data Choose where-  PleaseChoosePath    :: Expr EWord -> (Bool -> EVM ()) -> Choose+data Choose s where+  PleaseChoosePath    :: Expr EWord -> (Bool -> EVM s ()) -> Choose s  -- | The possible return values of a SMT query data BranchCondition = Case Bool | Unknown   deriving Show -instance Show Query where+instance Show (Query s) where   showsPrec _ = \case-    PleaseFetchContract addr _ ->-      (("<EVM.Query: fetch contract " ++ show addr ++ ">") ++)+    PleaseFetchContract addr base _ ->+      (("<EVM.Query: fetch contract " ++ show addr ++ show base ++ ">") ++)     PleaseFetchSlot addr slot _ ->       (("<EVM.Query: fetch slot "         ++ show slot ++ " for "@@ -582,29 +566,29 @@     PleaseDoFFI cmd _ ->       (("<EVM.Query: do ffi: " ++ (show cmd)) ++) -instance Show Choose where+instance Show (Choose s) where   showsPrec _ = \case     PleaseChoosePath _ _ ->       (("<EVM.Choice: waiting for user to select path (0,1)") ++)  -- | The possible result states of a VM-data VMResult-  = VMFailure EvmError     -- ^ An operation failed-  | VMSuccess (Expr Buf)   -- ^ Reached STOP, RETURN, or end-of-code-  | HandleEffect Effect    -- ^ An effect must be handled for execution to continue-  | Unfinished PartialExec -- ^ Execution could not continue further+data VMResult s+  = VMFailure EvmError      -- ^ An operation failed+  | VMSuccess (Expr Buf)    -- ^ Reached STOP, RETURN, or end-of-code+  | HandleEffect (Effect s) -- ^ An effect must be handled for execution to continue+  | Unfinished PartialExec  -- ^ Execution could not continue further -deriving instance Show VMResult+deriving instance Show (VMResult s)   -- VM State ----------------------------------------------------------------------------------------   -- | The state of a stepwise EVM execution-data VM = VM-  { result         :: Maybe VMResult-  , state          :: FrameState-  , frames         :: [Frame]+data VM s = VM+  { result         :: Maybe (VMResult s)+  , state          :: FrameState s+  , frames         :: [Frame s]   , env            :: Env   , block          :: Block   , tx             :: TxState@@ -612,118 +596,132 @@   , traces         :: Zipper.TreePos Zipper.Empty Trace   , cache          :: Cache   , burned         :: {-# UNPACK #-} !Word64-  , iterations     :: Map CodeLocation (Int, [Expr EWord]) -- ^ how many times we've visited a loc, and what the contents of the stack were when we were there last+  , iterations     :: Map CodeLocation (Int, [Expr EWord])+  -- ^ how many times we've visited a loc, and what the contents of the stack were when we were there last   , constraints    :: [Prop]   , keccakEqs      :: [Prop]-  , allowFFI       :: Bool-  , overrideCaller :: Maybe Addr+  , config         :: RuntimeConfig   }   deriving (Show, Generic)  -- | Alias for the type of e.g. @exec1@.-type EVM a = State VM a+type EVM s a = StateT (VM s) (ST s) a +-- | The VM base state (i.e. should new contracts be created with abstract balance / storage?)+data BaseState+  = EmptyBase+  | AbstractBase+  deriving (Show)++-- | Configuration options that need to be consulted at runtime+data RuntimeConfig = RuntimeConfig+  { allowFFI :: Bool+  , overrideCaller :: Maybe (Expr EAddr)+  , baseState :: BaseState+  }+  deriving (Show)++abstRefineDefault :: AbstRefineConfig+abstRefineDefault = AbstRefineConfig False False++data AbstRefineConfig = AbstRefineConfig+  { arith :: Bool+  , mem   :: Bool+  }+  deriving (Show, Eq)+ -- | An entry in the VM's "call/create stack"-data Frame = Frame+data Frame s = Frame   { context :: FrameContext-  , state   :: FrameState+  , state   :: FrameState s   }   deriving (Show)  -- | Call/create info data FrameContext   = CreationContext-    { address         :: Addr+    { address         :: Expr EAddr     , codehash        :: Expr EWord-    , createreversion :: Map Addr Contract+    , createreversion :: Map (Expr EAddr) Contract     , substate        :: SubState     }   | CallContext-    { target        :: Addr-    , context       :: Addr+    { target        :: Expr EAddr+    , context       :: Expr EAddr     , offset        :: W256     , size          :: W256     , codehash      :: Expr EWord     , abi           :: Maybe W256     , calldata      :: Expr Buf-    , callreversion :: (Map Addr Contract, Expr Storage)+    , callreversion :: Map (Expr EAddr) Contract     , subState      :: SubState     }   deriving (Eq, Ord, Show, Generic)  -- | The "accrued substate" across a transaction data SubState = SubState-  { selfdestructs       :: [Addr]-  , touchedAccounts     :: [Addr]-  , accessedAddresses   :: Set Addr-  , accessedStorageKeys :: Set (Addr, W256)-  , refunds             :: [(Addr, Word64)]+  { selfdestructs       :: [Expr EAddr]+  , touchedAccounts     :: [Expr EAddr]+  , accessedAddresses   :: Set (Expr EAddr)+  , accessedStorageKeys :: Set (Expr EAddr, W256)+  , refunds             :: [(Expr EAddr, Word64)]   -- in principle we should include logs here, but do not for now   }   deriving (Eq, Ord, Show)  -- | The "registers" of the VM along with memory and data stack-data FrameState = FrameState-  { contract     :: Addr-  , codeContract :: Addr+data FrameState s = FrameState+  { contract     :: Expr EAddr+  , codeContract :: Expr EAddr   , code         :: ContractCode   , pc           :: {-# UNPACK #-} !Int   , stack        :: [Expr EWord]-  , memory       :: Expr Buf+  , memory       :: Memory s   , memorySize   :: Word64   , calldata     :: Expr Buf   , callvalue    :: Expr EWord-  , caller       :: Expr EWord+  , caller       :: Expr EAddr   , gas          :: {-# UNPACK #-} !Word64   , returndata   :: Expr Buf   , static       :: Bool   }   deriving (Show, Generic) +data Memory s+  = ConcreteMemory (MutableMemory s)+  | SymbolicMemory !(Expr Buf)++instance Show (Memory s) where+  show (ConcreteMemory _) = "<can't show mutable memory>"+  show (SymbolicMemory m) = show m++type MutableMemory s = STVector s Word8+ -- | The state that spans a whole transaction data TxState = TxState   { gasprice    :: W256   , gaslimit    :: Word64   , priorityFee :: W256-  , origin      :: Addr-  , toAddr      :: Addr+  , origin      :: Expr EAddr+  , toAddr      :: Expr EAddr   , value       :: Expr EWord   , substate    :: SubState   , isCreate    :: Bool-  , txReversion :: Map Addr Contract+  , txReversion :: Map (Expr EAddr) Contract   }   deriving (Show) --- | When doing symbolic execution, we have three different--- ways to model the storage of contracts. This determines--- not only the initial contract storage model but also how--- RPC or state fetched contracts will be modeled.-data StorageModel-  = ConcreteS    -- ^ Uses `Concrete` Storage. Reading / Writing from abstract-                 -- locations causes a runtime failure. Can be nicely combined with RPC.--  | SymbolicS    -- ^ Uses `Symbolic` Storage. Reading / Writing never reaches RPC,-                 -- but always done using an SMT array with no default value.--  | InitialS     -- ^ Uses `Symbolic` Storage. Reading / Writing never reaches RPC,-                 -- but always done using an SMT array with 0 as the default value.--  deriving (Read, Show)--instance ParseField StorageModel- -- | Various environmental data data Env = Env-  { contracts    :: Map Addr Contract-  , chainId      :: W256-  , storage      :: Expr Storage-  , origStorage  :: Map W256 (Map W256 W256)+  { contracts      :: Map (Expr EAddr) Contract+  , chainId        :: W256+  , freshAddresses :: Int   }   deriving (Show, Generic)  -- | Data about the block data Block = Block-  { coinbase    :: Addr+  { coinbase    :: Expr EAddr   , timestamp   :: Expr EWord   , number      :: W256   , prevRandao  :: W256@@ -733,61 +731,53 @@   , schedule    :: FeeSchedule Word64   } deriving (Show, Generic) --- | The state of a contract+-- | Full contract state data Contract = Contract-  { contractcode :: ContractCode-  , balance      :: W256-  , nonce        :: W256-  , codehash     :: Expr EWord-  , opIxMap      :: SV.Vector Int-  , codeOps      :: V.Vector (Int, Op)-  , external     :: Bool+  { code        :: ContractCode+  , storage     :: Expr Storage+  , origStorage :: Expr Storage+  , balance     :: Expr EWord+  , nonce       :: Maybe W64+  , codehash    :: Expr EWord+  , opIxMap     :: SV.Vector Int+  , codeOps     :: V.Vector (Int, Op)+  , external    :: Bool   }-  deriving (Eq, Ord, Show)+  deriving (Show, Eq, Ord)   -- Bytecode Representations ------------------------------------------------------------------------   -- | A unique id for a given pc-type CodeLocation = (Addr, Int)+type CodeLocation = (Expr EAddr, Int)  -- | The cache is data that can be persisted for efficiency: -- any expensive query that is constant at least within a block. data Cache = Cache-  { fetchedContracts :: Map Addr Contract-  , fetchedStorage :: Map W256 (Map W256 W256)-  , path :: Map (CodeLocation, Int) Bool+  { fetched :: Map Addr Contract+  , path    :: Map (CodeLocation, Int) Bool   } deriving (Show, Generic)  instance Semigroup Cache where   a <> b = Cache-    { fetchedContracts = Map.unionWith unifyCachedContract a.fetchedContracts b.fetchedContracts-    , fetchedStorage = Map.unionWith unifyCachedStorage a.fetchedStorage b.fetchedStorage+    { fetched = Map.unionWith unifyCachedContract a.fetched b.fetched     , path = mappend a.path b.path     }  instance Monoid Cache where-  mempty = Cache { fetchedContracts = mempty-                 , fetchedStorage = mempty+  mempty = Cache { fetched = mempty                  , path = mempty                  } -unifyCachedStorage :: Map W256 W256 -> Map W256 W256 -> Map W256 W256-unifyCachedStorage _ _ = undefined- -- only intended for use in Cache merges, where we expect -- everything to be Concrete unifyCachedContract :: Contract -> Contract -> Contract-unifyCachedContract _ _ = undefined-  {--unifyCachedContract a b = a & set storage merged-  where merged = case (view storage a, view storage b) of+unifyCachedContract a b = a { storage = merged }+  where merged = case (a.storage, b.storage) of                    (ConcreteStore sa, ConcreteStore sb) ->                      ConcreteStore (mappend sa sb)-                   _ ->-                     view storage a-   -}+                   _ -> a.storage   -- Bytecode Representations ------------------------------------------------------------------------@@ -809,9 +799,10 @@   hopefully we do not have to deal with dynamic immutable before we get a real data section... -} data ContractCode-  = InitCode ByteString (Expr Buf) -- ^ "Constructor" code, during contract creation-  | RuntimeCode RuntimeCode -- ^ "Instance" code, after contract creation-  deriving (Show, Ord)+  = UnknownCode (Expr EAddr)       -- ^ Fully abstract code, keyed on an address to give consistent results for e.g. extcodehash+  | InitCode ByteString (Expr Buf) -- ^ "Constructor" code, during contract creation+  | RuntimeCode RuntimeCode        -- ^ "Instance" code, after contract creation+  deriving (Show, Eq, Ord)  -- | We have two variants here to optimize the fully concrete case. -- ConcreteRuntimeCode just wraps a ByteString@@ -821,13 +812,6 @@   | SymbolicRuntimeCode (V.Vector (Expr Byte))   deriving (Show, Eq, Ord) --- runtime err when used for symbolic code-instance Eq ContractCode where-  InitCode a b  == InitCode c d  = a == c && b == d-  RuntimeCode x == RuntimeCode y = x == y-  _ == _ = False-- -- Execution Traces --------------------------------------------------------------------------------  @@ -849,7 +833,7 @@ -- | Wrapper type containing vm traces and the context needed to pretty print them properly data Traces = Traces   { traces :: Forest Trace-  , contracts :: Map Addr Contract+  , contracts :: Map (Expr EAddr) Contract   }   deriving (Eq, Ord, Show, Generic) @@ -865,18 +849,19 @@ -- | A specification for an initial VM state data VMOpts = VMOpts   { contract :: Contract+  , otherContracts :: [(Expr EAddr, Contract)]   , calldata :: (Expr Buf, [Prop])-  , initialStorage :: Expr Storage+  , baseState :: BaseState   , value :: Expr EWord   , priorityFee :: W256-  , address :: Addr-  , caller :: Expr EWord-  , origin :: Addr+  , address :: Expr EAddr+  , caller :: Expr EAddr+  , origin :: Expr EAddr   , gas :: Word64   , gaslimit :: Word64   , number :: W256   , timestamp :: Expr EWord-  , coinbase :: Addr+  , coinbase :: Expr EAddr   , prevRandao :: W256   , maxCodeSize :: W256   , blockGaslimit :: Word64@@ -885,7 +870,7 @@   , schedule :: FeeSchedule Word64   , chainId :: W256   , create :: Bool-  , txAccessList :: Map Addr [W256]+  , txAccessList :: Map (Expr EAddr) [W256]   , allowFFI :: Bool   } deriving Show @@ -1063,7 +1048,6 @@     ( Num, Integral, Real, Ord, Generic     , Bits , FiniteBits, Enum, Eq , Bounded     )-instance JSON.FromJSON W64  instance Read W64 where   readsPrec _ "0x" = [(0, "")]@@ -1075,6 +1059,14 @@ instance JSON.ToJSON W64 where   toJSON x = JSON.String  $ T.pack $ show x +instance FromJSON W64 where+  parseJSON v = do+    s <- T.unpack <$> parseJSON v+    case reads s of+      [(x, "")]  -> return x+      _          -> fail $ "invalid hex word (" ++ s ++ ")"++ word64Field :: JSON.Object -> Key -> JSON.Parser Word64 word64Field x f = ((readNull 0) . T.unpack)                   <$> (x .: f)@@ -1168,8 +1160,18 @@  maybeLitWord :: Expr EWord -> Maybe W256 maybeLitWord (Lit w) = Just w+maybeLitWord (WAddr (LitAddr w)) = Just (into w) maybeLitWord _ = Nothing +maybeLitAddr :: Expr EAddr -> Maybe Addr+maybeLitAddr (LitAddr a) = Just a+maybeLitAddr _ = Nothing++maybeConcreteStore :: Expr Storage -> Maybe (Map W256 W256)+maybeConcreteStore (ConcreteStore s) = Just s+maybeConcreteStore _ = Nothing++ word256 :: ByteString -> Word256 word256 xs | BS.length xs == 1 =   -- optimize one byte pushes@@ -1269,8 +1271,9 @@  -- Utils ------------------------------------------------------------------------------------------- +{- HLINT ignore internalError -} internalError:: HasCallStack => [Char] -> a-internalError m = error $ "Internal error: " ++ m ++ " -- " ++ (prettyCallStack callStack)+internalError m = error $ "Internal Error: " ++ m ++ " -- " ++ (prettyCallStack callStack)  concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b] concatMapM f xs = fmap concat (mapM f xs)@@ -1308,6 +1311,15 @@     Right s -> "\"" <> T.unpack s <> "\""     Left _ -> "❮utf8 decode failed❯: " <> (show $ ByteStringS bs) +-- |'paddedShowHex' displays a number in hexidecimal and pads the number+-- with 0 so that it has a minimum length of @w@.+paddedShowHex :: (Show a, Integral a) => Int -> a -> String+paddedShowHex w n = pad ++ str+    where+     str = showHex n ""+     pad = replicate (w - length str) '0'++ -- Optics ------------------------------------------------------------------------------------------  @@ -1323,3 +1335,4 @@ makeFieldLabelsNoPrefix ''Contract makeFieldLabelsNoPrefix ''Env makeFieldLabelsNoPrefix ''Block+makeFieldLabelsNoPrefix ''RuntimeConfig
src/EVM/UnitTest.hs view
@@ -3,99 +3,72 @@  module EVM.UnitTest where -import Prelude hiding (Word)- import EVM import EVM.ABI-import EVM.Concrete import EVM.SMT import EVM.Solvers import EVM.Dapp-import EVM.Debug (srcMapCodePos) import EVM.Exec-import EVM.Expr (litAddr, readStorage', simplify)+import EVM.Expr (readStorage') import EVM.Expr qualified as Expr-import EVM.Facts qualified as Facts-import EVM.Facts.Git qualified as Git-import EVM.FeeSchedule qualified as FeeSchedule+import EVM.FeeSchedule (feeSchedule) import EVM.Fetch qualified as Fetch import EVM.Format import EVM.Solidity-import EVM.SymExec (defaultVeriOpts, symCalldata, verify, isQed, extractCex, runExpr, subModel, VeriOpts(..))+import EVM.SymExec (defaultVeriOpts, symCalldata, verify, isQed, extractCex, runExpr, prettyCalldata, panicMsg, VeriOpts(..), flattenExpr) import EVM.Types import EVM.Transaction (initTx)-import EVM.RLP-import EVM.Stepper (Stepper, interpret)+import EVM.Stepper (Stepper) import EVM.Stepper qualified as Stepper -import Control.Monad.Operational qualified as Operational+import Control.Monad.ST (RealWorld, ST, stToIO) import Optics.Core hiding (elements) import Optics.State import Optics.State.Operators-import Optics.Zoom-import Control.Monad.Par.Class (spawn_)-import Control.Monad.Par.Class qualified as Par-import Control.Monad.Par.IO (runParIO) import Control.Monad.State.Strict hiding (state)-import Control.Monad.State.Strict qualified as State import Data.ByteString.Lazy qualified as BSLazy import Data.Binary.Get (runGet) import Data.ByteString (ByteString)-import Data.ByteString qualified as BS import Data.Decimal (DecimalRaw(..))-import Data.Either (isRight)+import Data.Either (isRight, rights, lefts) import Data.Foldable (toList) import Data.Map (Map) import Data.Map qualified as Map-import Data.Maybe (fromMaybe, catMaybes, fromJust, isJust, fromMaybe, mapMaybe, isNothing)-import Data.MultiSet (MultiSet)-import Data.MultiSet qualified as MultiSet-import Data.Set (Set)-import Data.Set qualified as Set+import Data.Maybe import Data.Text (isPrefixOf, stripSuffix, intercalate, Text, pack, unpack) import Data.Text qualified as Text import Data.Text.Encoding (encodeUtf8) import Data.Text.IO qualified as Text-import Data.Vector (Vector)-import Data.Vector qualified as Vector-import Data.Word (Word32, Word64)+import Data.Word (Word64) import GHC.Natural-import System.Environment (lookupEnv) import System.IO (hFlush, stdout)-import Test.QuickCheck hiding (verbose, Success, Failure)-import qualified Test.QuickCheck as QC import Witch (unsafeInto, into) -data UnitTestOptions = UnitTestOptions+data UnitTestOptions s = UnitTestOptions   { rpcInfo     :: Fetch.RpcInfo   , solvers     :: SolverGroup   , verbose     :: Maybe Int   , maxIter     :: Maybe Integer   , askSmtIters :: Integer   , smtDebug    :: Bool-  , maxDepth    :: Maybe Int   , smtTimeout  :: Maybe Natural   , solver      :: Maybe Text-  , covMatch    :: Maybe Text   , match       :: Text-  , fuzzRuns    :: Int-  , replay      :: Maybe (Text, BSLazy.ByteString)-  , vmModifier  :: VM -> VM   , dapp        :: DappInfo   , testParams  :: TestVMParams   , ffiAllowed  :: Bool   }  data TestVMParams = TestVMParams-  { address       :: Addr-  , caller        :: Addr-  , origin        :: Addr+  { address       :: Expr EAddr+  , caller        :: Expr EAddr+  , origin        :: Expr EAddr   , gasCreate     :: Word64   , gasCall       :: Word64   , baseFee       :: W256   , priorityFee   :: W256   , balanceCreate :: W256-  , coinbase      :: Addr+  , coinbase      :: Expr EAddr   , number        :: W256   , timestamp     :: W256   , gaslimit      :: Word64@@ -121,7 +94,7 @@   -- | Generate VeriOpts from UnitTestOptions-makeVeriOpts :: UnitTestOptions -> VeriOpts+makeVeriOpts :: UnitTestOptions s -> VeriOpts makeVeriOpts opts =    defaultVeriOpts { debug = opts.smtDebug                    , maxIter = opts.maxIter@@ -130,34 +103,20 @@                    }  -- | Top level CLI endpoint for hevm test-unitTest :: UnitTestOptions -> Contracts -> Maybe String -> IO Bool-unitTest opts (Contracts cs) cache' = do+unitTest :: UnitTestOptions RealWorld -> Contracts -> IO Bool+unitTest opts (Contracts cs) = do   let unitTests = findUnitTests opts.match $ Map.elems cs   results <- concatMapM (runUnitTestContract opts cs) unitTests-  let (passing, vms) = unzip results-  case cache' of-    Nothing ->-      pure ()-    Just path ->-      -- merge all of the post-vm caches and save into the state-      let-        evmcache = mconcat [vm.cache | vm <- vms]-      in-        liftIO $ Git.saveFacts (Git.RepoAt path) (Facts.cacheFacts evmcache)--  pure $ and passing-+  pure $ and results  -- | Assuming a constructor is loaded, this stepper will run the constructor -- to create the test contract, give it an initial balance, and run `setUp()'.-initializeUnitTest :: UnitTestOptions -> SolcContract -> Stepper ()+initializeUnitTest :: UnitTestOptions s -> SolcContract -> Stepper s () initializeUnitTest opts theContract = do    let addr = opts.testParams.address    Stepper.evm $ do-    -- Maybe modify the initial VM, e.g. to load library code-    modify opts.vmModifier     -- Make a trace entry for running the constructor     pushTrace (EntryTrace "constructor") @@ -166,7 +125,7 @@    Stepper.evm $ do     -- Give a balance to the test target-    #env % #contracts % ix addr % #balance %= (+ opts.testParams.balanceCreate)+    #env % #contracts % ix addr % #balance %= (`Expr.add` (Lit opts.testParams.balanceCreate))      -- call setUp(), if it exists, to initialize the test contract     let theAbi = theContract.abiMap@@ -183,244 +142,16 @@     Left e -> pushTrace (ErrorTrace e)     _ -> popTrace --- | Assuming a test contract is loaded and initialized, this stepper--- will run the specified test method and return whether it succeeded.-runUnitTest :: UnitTestOptions -> ABIMethod -> AbiValue -> Stepper Bool-runUnitTest a method args = do-  x <- execTestStepper a method args-  checkFailures a method x--execTestStepper :: UnitTestOptions -> ABIMethod -> AbiValue -> Stepper Bool-execTestStepper UnitTestOptions { .. } methodName' method = do-  -- Set up the call to the test method-  Stepper.evm $ do-    abiCall testParams (Left (methodName', method))-    pushTrace (EntryTrace methodName')-  -- Try running the test method-  Stepper.execFully >>= \case-     -- If we failed, put the error in the trace.-    Left e -> Stepper.evm (pushTrace (ErrorTrace e) >> popTrace) >> pure True-    _ -> pure False--exploreStep :: UnitTestOptions -> ByteString -> Stepper Bool-exploreStep UnitTestOptions{..} bs = do-  Stepper.evm $ do-    cs <- use (#env % #contracts)-    abiCall testParams (Right bs)-    let Method _ inputs sig _ _ = fromMaybe (internalError "unknown abi call") $ Map.lookup (unsafeInto $ word $ BS.take 4 bs) dapp.abiMap-        types = snd <$> inputs-    let ?context = DappContext dapp cs-    this <- fromMaybe (internalError "unknown target") <$> (use (#env % #contracts % at testParams.address))-    let name = maybe "" (contractNamePart . (.contractName)) $ lookupCode this.contractcode dapp-    pushTrace (EntryTrace (name <> "." <> sig <> "(" <> intercalate "," ((pack . show) <$> types) <> ")" <> showCall types (ConcreteBuf bs)))-  -- Try running the test method-  Stepper.execFully >>= \case-     -- If we failed, put the error in the trace.-    Left e -> Stepper.evm (pushTrace (ErrorTrace e) >> popTrace) >> pure True-    _ -> pure False--checkFailures :: UnitTestOptions -> ABIMethod -> Bool -> Stepper Bool-checkFailures UnitTestOptions { .. } method bailed = do-   -- Decide whether the test is supposed to fail or succeed-  let shouldFail = "testFail" `isPrefixOf` method-  if bailed then-    pure shouldFail-  else do-    -- Ask whether any assertions failed-    Stepper.evm $ do-      popTrace-      abiCall testParams $ Left ("failed()", emptyAbi)-    res <- Stepper.execFully-    case res of-      Right (ConcreteBuf r) ->-        let failed = case decodeAbiValue AbiBoolType (BSLazy.fromStrict r) of-              AbiBool f -> f-              _ -> internalError "fix me with better types"-        in pure (shouldFail == failed)-      c -> internalError $ "unexpected failure code: " <> show c---- | Randomly generates the calldata arguments and runs the test-fuzzTest :: UnitTestOptions -> Text -> [AbiType] -> VM -> Property-fuzzTest opts@UnitTestOptions{..} sig types vm = forAllShow (genAbiValue (AbiTupleType $ Vector.fromList types)) (show . ByteStringS . encodeAbiValue)-  $ \args -> ioProperty $-    EVM.Stepper.interpret (Fetch.oracle solvers rpcInfo) vm (runUnitTest opts sig args)--tick :: Text -> IO ()-tick x = Text.putStr x >> hFlush stdout---- | This is like an unresolved source mapping.-data OpLocation = OpLocation-  { srcContract :: Contract-  , srcOpIx :: Int-  } deriving (Show)--instance Eq OpLocation where-  (==) (OpLocation a b) (OpLocation a' b') = b == b' && a.contractcode == a'.contractcode--instance Ord OpLocation where-  compare (OpLocation a b) (OpLocation a' b') = compare (a.contractcode, b) (a'.contractcode, b')--srcMapForOpLocation :: DappInfo -> OpLocation -> Maybe SrcMap-srcMapForOpLocation dapp (OpLocation contr opIx) = srcMap dapp contr opIx--type CoverageState = (VM, MultiSet OpLocation)--currentOpLocation :: VM -> OpLocation-currentOpLocation vm =-  case currentContract vm of-    Nothing ->-      internalError "why no contract?"-    Just c ->-      OpLocation-        c-        (fromMaybe (internalError "op ix") (vmOpIx vm))--execWithCoverage :: StateT CoverageState IO VMResult-execWithCoverage = do _ <- runWithCoverage-                      fromJust <$> use (_1 % #result)--runWithCoverage :: StateT CoverageState IO VM-runWithCoverage = do-  -- This is just like `exec` except for every instruction evaluated,-  -- we also increment a counter indexed by the current code location.-  vm0 <- use _1-  case vm0.result of-    Nothing -> do-      vm1 <- zoom _1 (State.state (runState exec1) >> get)-      zoom _2 (modify (MultiSet.insert (currentOpLocation vm1)))-      runWithCoverage-    Just _ -> pure vm0---interpretWithCoverage-  :: UnitTestOptions-  -> Stepper a-  -> StateT CoverageState IO a-interpretWithCoverage opts@UnitTestOptions{..} =-  eval . Operational.view--  where-    eval-      :: Operational.ProgramView Stepper.Action a-      -> StateT CoverageState IO a--    eval (Operational.Return x) =-      pure x--    eval (action Operational.:>>= k) =-      case action of-        Stepper.Exec ->-          execWithCoverage >>= interpretWithCoverage opts . k-        Stepper.Run ->-          runWithCoverage >>= interpretWithCoverage opts . k-        Stepper.Wait (PleaseAskSMT (Lit c) _ continue) ->-          interpretWithCoverage opts (Stepper.evm (continue (Case (c > 0))) >>= k)-        Stepper.Wait q ->-          do m <- liftIO ((Fetch.oracle solvers rpcInfo) q)-             zoom _1 (State.state (runState m)) >> interpretWithCoverage opts (k ())-        Stepper.Ask _ ->-          internalError "cannot make choice in this interpreter"-        Stepper.IOAct q ->-          zoom _1 (StateT (runStateT q)) >>= interpretWithCoverage opts . k-        Stepper.EVM m ->-          zoom _1 (State.state (runState m)) >>= interpretWithCoverage opts . k--coverageReport-  :: DappInfo-  -> MultiSet SrcMap-  -> Map FilePath (Vector (Int, ByteString))-coverageReport dapp cov =-  let-    sources :: SourceCache-    sources = dapp.sources--    allPositions :: Set (FilePath, Int)-    allPositions =-      ( Set.fromList-      . mapMaybe (srcMapCodePos sources)-      . toList-      $ mconcat-        ( dapp.solcByName-        & Map.elems-        & map (\x -> x.runtimeSrcmap <> x.creationSrcmap)-        )-      )--    srcMapCov :: MultiSet (FilePath, Int)-    srcMapCov = MultiSet.mapMaybe (srcMapCodePos sources) cov--    linesByName :: Map FilePath (Vector ByteString)-    linesByName =-      Map.fromList $ zipWith-          (\(name, _) lines' -> (name, lines'))-          (Map.elems sources.files)-          (Map.elems sources.lines)--    f :: FilePath -> Vector ByteString -> Vector (Int, ByteString)-    f name =-      Vector.imap-        (\i bs ->-           let-             n =-               if Set.member (name, i + 1) allPositions-               then MultiSet.occur (name, i + 1) srcMapCov-               else -1-           in (n, bs))-  in-    Map.mapWithKey f linesByName--coverageForUnitTestContract-  :: UnitTestOptions-  -> Map Text SolcContract-  -> SourceCache-  -> (Text, [(Test, [AbiType])])-  -> IO (MultiSet SrcMap)-coverageForUnitTestContract-  opts@(UnitTestOptions {..}) contractMap _ (name, testNames) = do--  -- Look for the wanted contract by name from the Solidity info-  case Map.lookup name contractMap of-    Nothing ->-      -- Fail if there's no such contract-      internalError $ "Contract " ++ unpack name ++ " not found"--    Just theContract -> do-      -- Construct the initial VM and begin the contract's constructor-      let vm0 = initialUnitTestVm opts theContract-      (vm1, cov1) <--        execStateT-          (interpretWithCoverage opts-            (Stepper.enter name >> initializeUnitTest opts theContract))-          (vm0, mempty)--      -- Define the thread spawner for test cases-      let-        runOne' (test, _) = spawn_ . liftIO $ do-          (_, (_, cov)) <--            runStateT-              (interpretWithCoverage opts (runUnitTest opts (extractSig test) emptyAbi))-              (vm1, mempty)-          pure cov-      -- Run all the test cases in parallel and gather their coverages-      covs <--        runParIO (mapM runOne' testNames >>= mapM Par.get)--      -- Sum up all the coverage counts-      let cov2 = MultiSet.unions (cov1 : covs)--      pure (MultiSet.mapMaybe (srcMapForOpLocation dapp) cov2)- runUnitTestContract-  :: UnitTestOptions+  :: UnitTestOptions RealWorld   -> Map Text SolcContract-  -> (Text, [(Test, [AbiType])])-  -> IO [(Bool, VM)]+  -> (Text, [Sig])+  -> IO [Bool] runUnitTestContract   opts@(UnitTestOptions {..}) contractMap (name, testSigs) = do    -- Print a header-  liftIO $ putStrLn $ "Running " ++ show (length testSigs) ++ " tests for "-    ++ unpack name+  putStrLn $ "Running " ++ show (length testSigs) ++ " tests for " ++ unpack name    -- Look for the wanted contract by name from the Solidity info   case Map.lookup name contractMap of@@ -430,8 +161,8 @@      Just theContract -> do       -- Construct the initial VM and begin the contract's constructor-      let vm0 = initialUnitTestVm opts theContract-      vm1 <- liftIO $ EVM.Stepper.interpret (Fetch.oracle solvers rpcInfo) vm0 $ do+      vm0 <- stToIO $ initialUnitTestVm opts theContract+      vm1 <- Stepper.interpret (Fetch.oracle solvers rpcInfo) vm0 $ do         Stepper.enter name         initializeUnitTest opts theContract         Stepper.evm get@@ -440,320 +171,86 @@         Just (VMFailure _) -> liftIO $ do           Text.putStrLn "\x1b[31m[BAIL]\x1b[0m setUp() "           tick "\n"-          tick (Data.Text.pack $ show $ failOutput vm1 opts "setUp()")-          pure [(False, vm1)]+          tick $ failOutput vm1 opts "setUp()"+          pure [False]         Just (VMSuccess _) -> do           let -            runCache :: ([(Either Text Text, VM)], VM) -> (Test, [AbiType])-                        -> IO ([(Either Text Text, VM)], VM)-            runCache (results, vm) (test, types) = do-              (t, r, vm') <- runTest opts vm (test, types)-              liftIO $ Text.putStrLn t-              let vmCached = vm { cache = vm'.cache }-              pure (((r, vm'): results), vmCached)--          -- Run all the test cases and print their status updates,-          -- accumulating the vm cache throughout-          (details, _) <- foldM runCache ([], vm1) testSigs+          -- Run all the test cases and print their status+          details <- forM testSigs $ \s -> do+            (result, detail) <- symRun opts vm1 s+            Text.putStrLn result+            pure detail -          let running = [x | (Right x, _) <- details]-          let bailing = [x | (Left  x, _) <- details]+          let running = rights details+              bailing = lefts details -          liftIO $ do-            tick "\n"-            tick (Text.unlines (filter (not . Text.null) running))-            tick (Text.unlines bailing)+          tick "\n"+          tick (Text.unlines (filter (not . Text.null) running))+          tick (Text.unlines bailing) -          pure [(isRight r, vm) | (r, vm) <- details]+          pure $ fmap isRight details         _ -> internalError "setUp() did not end with a result"  -runTest :: UnitTestOptions -> VM -> (Test, [AbiType]) -> IO (Text, Either Text Text, VM)-runTest opts@UnitTestOptions{} vm (ConcreteTest testName, []) = liftIO $ runOne opts vm testName emptyAbi-runTest opts@UnitTestOptions{..} vm (ConcreteTest testName, types) = liftIO $ case replay of-  Nothing ->-    fuzzRun opts vm testName types-  Just (sig, callData) ->-    if sig == testName-    then runOne opts vm testName $-      decodeAbiValue (AbiTupleType (Vector.fromList types)) callData-    else fuzzRun opts vm testName types-runTest opts@UnitTestOptions{..} vm (InvariantTest testName, []) = liftIO $ case replay of-  Nothing -> exploreRun opts vm testName []-  Just (sig, cds) ->-    if sig == testName-    then exploreRun opts vm testName (decodeCalls cds)-    else exploreRun opts vm testName []-runTest _ _ (InvariantTest _, types) = internalError $ "invariant testing with arguments: " <> show types <> " is not implemented (yet!)"-runTest opts vm (SymbolicTest testName, types) = symRun opts vm testName types--type ExploreTx = (Addr, Addr, ByteString, W256)--decodeCalls :: BSLazy.ByteString -> [ExploreTx]-decodeCalls b = fromMaybe (internalError "could not decode replay data") $ do-  List v <- rlpdecode $ BSLazy.toStrict b-  pure $ unList <$> v-  where-    unList (List [BS caller', BS target, BS cd, BS ts]) =-      (unsafeInto (word caller'), unsafeInto (word target), cd, word ts)-    unList _ = internalError "fix me with better types"---- | Runs an invariant test, calls the invariant before execution begins-initialExplorationStepper :: UnitTestOptions -> ABIMethod -> [ExploreTx] -> [Addr] -> Int -> Stepper (Bool, RLP)-initialExplorationStepper opts'' testName replayData targets i = do-  let history = List []-  x <- runUnitTest opts'' testName emptyAbi-  if x-  then explorationStepper opts'' testName replayData targets history i-  else pure (False, history)--explorationStepper :: UnitTestOptions -> ABIMethod -> [ExploreTx] -> [Addr] -> RLP -> Int -> Stepper (Bool, RLP)-explorationStepper _ _ _ _ history 0  = pure (True, history)-explorationStepper opts@UnitTestOptions{..} testName replayData targets (List history) i = do- (caller', target, cd, timestamp') <--   case preview (ix (i - 1)) replayData of-     Just v -> pure v-     Nothing ->-      Stepper.evmIO $ do-       vm <- get-       let cs = vm.env.contracts-           noCode c = case c.contractcode of-             RuntimeCode (ConcreteRuntimeCode "") -> True-             RuntimeCode (SymbolicRuntimeCode c') -> null c'-             _ -> False-           mutable m = m.mutability `elem` [NonPayable, Payable]-           knownAbis :: Map Addr SolcContract-           knownAbis =-             -- exclude contracts without code-             Map.filter (not . BS.null . (.runtimeCode)) $-             -- exclude contracts without state changing functions-             Map.filter (not . null . Map.filter mutable . (.abiMap)) $-             -- exclude testing abis-             Map.filter (isNothing . preview (ix unitTestMarkerAbi) . (.abiMap)) $-             -- pick all contracts with known compiler artifacts-             fmap fromJust (Map.filter isJust $ Map.fromList [(addr, lookupCode c.contractcode dapp) | (addr, c)  <- Map.toList cs])-           selected = [(addr,-                        fromMaybe (internalError $ "no src found for: " <> show addr) $-                          lookupCode (fromMaybe (internalError $ "contract not found: " <> show addr) $-                            Map.lookup addr cs).contractcode dapp)-                       | addr  <- targets]-       -- go to IO and generate a random valid call to any known contract-       liftIO $ do-         -- select random contract-         (target, solcInfo) <- generate $ elements (if null targets then Map.toList knownAbis else selected)-         -- choose a random mutable method-         (_, (Method _ inputs sig _ _)) <- generate (elements $ Map.toList $ Map.filter mutable solcInfo.abiMap)-         let types = snd <$> inputs-         -- set the caller to a random address with 90% probability, 10% known EOA address-         let knownEOAs = Map.keys $ Map.filter noCode cs-         AbiAddress caller' <--           if null knownEOAs-           then generate $ genAbiValue AbiAddressType-           else generate $ frequency-             [ (90, genAbiValue AbiAddressType)-             , (10, AbiAddress <$> elements knownEOAs)-             ]-         -- make a call with random valid data to the function-         args <- generate $ genAbiValue (AbiTupleType $ Vector.fromList types)-         let cd = abiMethod (sig <> "(" <> intercalate "," ((pack . show) <$> types) <> ")") args-         -- increment timestamp with random amount-         timepassed <- into <$> generate (arbitrarySizedNatural :: Gen Word32)-         let ts = fromMaybe (internalError "symbolic timestamp not supported here") $ maybeLitWord vm.block.timestamp-         pure (caller', target, cd, into ts + timepassed)- let opts' = opts { testParams = testParams {address = target, caller = caller', timestamp = timestamp'}}-     thisCallRLP = List [BS $ word160Bytes caller', BS $ word160Bytes target, BS cd, BS $ word256Bytes timestamp']- -- set the timestamp- Stepper.evm $ assign (#block % #timestamp) (Lit timestamp')- -- perform the call- bailed <- exploreStep opts' cd- Stepper.evm popTrace- let newHistory = if bailed then List history else List (thisCallRLP:history)-     opts'' = opts {testParams = testParams {timestamp = timestamp'}}-     carryOn = explorationStepper opts'' testName replayData targets newHistory (i - 1)- -- if we didn't revert, run the test function- if bailed- then carryOn- else-   do x <- runUnitTest opts'' testName emptyAbi-      if x-      then carryOn-      else pure (False, List (thisCallRLP:history))-explorationStepper _ _ _ _ _ _  = internalError "malformed rlp"--getTargetContracts :: UnitTestOptions -> Stepper [Addr]-getTargetContracts UnitTestOptions{..} = do-  vm <- Stepper.evm get-  let contract' = fromJust $ currentContract vm-      theAbi = (fromJust $ lookupCode contract'.contractcode dapp).abiMap-      setUp  = abiKeccak (encodeUtf8 "targetContracts()")-  case Map.lookup setUp theAbi of-    Nothing -> pure []-    Just _ -> do-      Stepper.evm $ abiCall testParams (Left ("targetContracts()", emptyAbi))-      res <- Stepper.execFully-      case res of-        Right (ConcreteBuf r) ->-          let vs = case decodeAbiValue (AbiTupleType (Vector.fromList [AbiArrayDynamicType AbiAddressType])) (BSLazy.fromStrict r) of-                AbiTuple v -> v-                _ -> internalError "fix me with better types"-              targets = case Vector.toList vs of-                [AbiArrayDynamic AbiAddressType ts] ->-                  let unAbiAddress (AbiAddress a) = a-                      unAbiAddress _ = internalError "fix me with better types"-                  in unAbiAddress <$> Vector.toList ts-                _ -> internalError "fix me with better types"-          in pure targets-        _ -> internalError "internal error: unexpected failure code"--exploreRun :: UnitTestOptions -> VM -> ABIMethod -> [ExploreTx] -> IO (Text, Either Text Text, VM)-exploreRun opts@UnitTestOptions{..} initialVm testName replayTxs = do-  let oracle = Fetch.oracle solvers rpcInfo-  targets <- EVM.Stepper.interpret oracle initialVm (getTargetContracts opts)-  let depth = fromMaybe 20 maxDepth-  ((x, counterex), vm') <--    if null replayTxs then-      foldM (\a@((success, _),_) _ ->-               if success then-                 EVM.Stepper.interpret oracle initialVm $-                   (,) <$> initialExplorationStepper opts testName [] targets depth-                       <*> Stepper.evm get-               else pure a)-            ((True, (List [])), initialVm)  -- no canonical "post vm"-            [0..fuzzRuns]-    else EVM.Stepper.interpret oracle initialVm $-      (,) <$> initialExplorationStepper opts testName replayTxs targets (length replayTxs)-          <*> Stepper.evm get-  if x-  then pure ("\x1b[32m[PASS]\x1b[0m " <> testName <>  " (runs: " <> (pack $ show fuzzRuns) <>", depth: " <> pack (show depth) <> ")",-               Right (passOutput vm' opts testName), vm') -- no canonical "post vm"-  else let replayText = if null replayTxs-                        then "\nReplay data: '(" <> pack (show testName) <> "," <> pack (show (show (ByteStringS $ rlpencode counterex))) <> ")'"-                        else " (replayed)"-       in pure ("\x1b[31m[FAIL]\x1b[0m " <> testName <> replayText, Left  (failOutput vm' opts testName), vm')--execTest :: UnitTestOptions -> VM -> ABIMethod -> AbiValue -> IO (Bool, VM)-execTest opts@UnitTestOptions{..} vm testName args =-  EVM.Stepper.interpret (Fetch.oracle solvers rpcInfo) vm $ do-    (,) <$> execTestStepper opts testName args-        <*> Stepper.evm get---- | Define the thread spawner for normal test cases-runOne :: UnitTestOptions -> VM -> ABIMethod -> AbiValue -> IO (Text, Either Text Text, VM)-runOne opts@UnitTestOptions{..} vm testName args = do-  let argInfo = pack (if args == emptyAbi then "" else " with arguments: " <> show args)-  (bailed, vm') <- execTest opts vm testName args-  (success, vm'') <- EVM.Stepper.interpret (Fetch.oracle solvers rpcInfo) vm' $ do-    (,) <$> (checkFailures opts testName bailed)-        <*> Stepper.evm get-  if success-  then-     let gasSpent = testParams.gasCall - vm'.state.gas-         gasText = pack $ show (into gasSpent :: Integer)-     in-        pure-          ("\x1b[32m[PASS]\x1b[0m "-           <> testName <> argInfo <> " (gas: " <> gasText <> ")"-          , Right (passOutput vm'' opts testName)-          , vm''-          )-  else if bailed then-        pure-          ("\x1b[31m[BAIL]\x1b[0m "-           <> testName <> argInfo-          , Left (failOutput vm'' opts testName)-          , vm''-          )-      else-        pure-          ("\x1b[31m[FAIL]\x1b[0m "-           <> testName <> argInfo-          , Left (failOutput vm'' opts testName)-          , vm''-          )---- | Define the thread spawner for property based tests-fuzzRun :: UnitTestOptions -> VM -> Text -> [AbiType] -> IO (Text, Either Text Text, VM)-fuzzRun opts@UnitTestOptions{..} vm testName types = do-  let args = Args{ replay          = Nothing-                 , maxSuccess      = fuzzRuns-                 , maxDiscardRatio = 10-                 , maxSize         = 100-                 , chatty          = isJust verbose-                 , maxShrinks      = maxBound-                 }-  quickCheckWithResult args (fuzzTest opts testName types vm) >>= \case-    QC.Success numTests _ _ _ _ _ ->-      pure ("\x1b[32m[PASS]\x1b[0m "-             <> testName <> " (runs: " <> (pack $ show numTests) <> ")"-             -- this isn't the post vm we actually want, as we-             -- can't retrieve the correct vm from quickcheck-           , Right (passOutput vm opts testName)-           , vm-           )-    QC.Failure _ _ _ _ _ _ _ _ _ _ failCase _ _ ->-      let abiValue = decodeAbiValue (AbiTupleType (Vector.fromList types)) $ BSLazy.fromStrict $ hexText (pack $ concat failCase)-          ppOutput = pack $ show abiValue-      in do-        -- Run the failing test again to get a proper trace-        vm' <- EVM.Stepper.interpret (Fetch.oracle solvers rpcInfo) vm $-          runUnitTest opts testName abiValue >> Stepper.evm get-        pure ("\x1b[31m[FAIL]\x1b[0m "-               <> testName <> ". Counterexample: " <> ppOutput-               <> "\nRun:\n dapp test --replay '(\"" <> testName <> "\",\""-               <> (pack (concat failCase)) <> "\")'\nto test this case again, or \n dapp debug --replay '(\""-               <> testName <> "\",\"" <> (pack (concat failCase)) <> "\")'\nto debug it."-             , Left (failOutput vm' opts testName)-             , vm'-             )-    _ -> pure ("\x1b[31m[OOPS]\x1b[0m "-               <> testName-              , Left (failOutput vm opts testName)-              , vm-              )- -- | Define the thread spawner for symbolic tests-symRun :: UnitTestOptions -> VM -> Text -> [AbiType] -> IO (Text, Either Text Text, VM)-symRun opts@UnitTestOptions{..} vm testName types = do+symRun :: UnitTestOptions RealWorld -> VM RealWorld -> Sig -> IO (Text, Either Text Text)+symRun opts@UnitTestOptions{..} vm (Sig testName types) = do     let cd = symCalldata testName types [] (AbstractBuf "txdata")         shouldFail = "proveFail" `isPrefixOf` testName-        testContract = vm.state.contract+        testContract store = fromMaybe (internalError "test contract not found in state") (Map.lookup vm.state.contract store)      -- define postcondition depending on `shouldFail`     -- We directly encode the failure conditions from failed() in ds-test since this is easier to encode than a call into failed()     -- we need to read from slot 0 in the test contract and mask it with 0x10 to get the value of _failed     -- we don't need to do this when reading the failed from the cheatcode address since we don't do any packing there-    let failed store = (And (readStorage' (litAddr testContract) (Lit 0) store) (Lit 2) .== Lit 2)-                   .|| (readStorage' (litAddr cheatCode) (Lit 0x6661696c65640000000000000000000000000000000000000000000000000000) store .== Lit 1)+    let failed store = case Map.lookup cheatCode store of+          Just cheatContract -> (And (readStorage' (Lit 0) (testContract store).storage) (Lit 0x10) .== Lit 0x10)+                               .|| (readStorage' (Lit 0x6661696c65640000000000000000000000000000000000000000000000000000) cheatContract.storage .== Lit 1)+          Nothing -> And (readStorage' (Lit 0) (testContract store).storage) (Lit 2) .== Lit 2         postcondition = curry $ case shouldFail of           True -> \(_, post) -> case post of                                   Success _ _ _ store -> failed store                                   _ -> PBool True           False -> \(_, post) -> case post of                                    Success _ _ _ store -> PNeg (failed store)-                                   Failure _ _ _ -> PBool False+                                   Failure _ _ (Revert msg) -> case msg of+                                     ConcreteBuf b -> PBool $ b /= panicMsg 0x01+                                     b -> b ./= ConcreteBuf (panicMsg 0x01)+                                   Failure _ _ _ -> PBool True                                    Partial _ _ _ -> PBool True                                    _ -> internalError "Invalid leaf node" -    vm' <- EVM.Stepper.interpret (Fetch.oracle solvers rpcInfo) vm $+    vm' <- Stepper.interpret (Fetch.oracle solvers rpcInfo) vm $       Stepper.evm $ do         pushTrace (EntryTrace testName)         makeTxCall testParams cd         get      -- check postconditions against vm-    (_, results) <- verify solvers (makeVeriOpts opts) vm' (Just postcondition)+    (e, results) <- verify solvers (makeVeriOpts opts) vm' (Just postcondition)+    let allReverts = not . (any Expr.isSuccess) . flattenExpr $ e      -- display results     if all isQed results-    then do-      pure ("\x1b[32m[PASS]\x1b[0m " <> testName, Right "", vm)+    then if allReverts && (not shouldFail)+         then pure ("\x1b[31m[FAIL]\x1b[0m " <> testName, Left $ allBranchRev testName)+         else pure ("\x1b[32m[PASS]\x1b[0m " <> testName, Right "")     else do       let x = mapMaybe extractCex results       let y = symFailure opts testName (fst cd) types x-      pure ("\x1b[31m[FAIL]\x1b[0m " <> testName, Left y, vm)+      pure ("\x1b[31m[FAIL]\x1b[0m " <> testName, Left y) -symFailure :: UnitTestOptions -> Text -> Expr Buf -> [AbiType] -> [(Expr End, SMTCex)] -> Text+allBranchRev :: Text -> Text+allBranchRev testName = Text.unlines+  [ "Failure: " <> testName+  , ""+  , indentLines 2 $ Text.unlines+      [ "No reachable assertion violations, but all branches reverted"+      , "Prefix this testname with `proveFail` if this is expected"+      ]+  ]+symFailure :: UnitTestOptions RealWorld -> Text -> Expr Buf -> [AbiType] -> [(Expr End, SMTCex)] -> Text symFailure UnitTestOptions {..} testName cd types failures' =   mconcat     [ "Failure: "@@ -764,8 +261,8 @@     where       showRes = \case         Success _ _ _ _ -> if "proveFail" `isPrefixOf` testName-                       then "Successful execution"-                       else "Failed: DSTest Assertion Violation"+                           then "Successful execution"+                           else "Failed: DSTest Assertion Violation"         res ->           let ?context = DappContext { info = dapp, env = traceContext res}           in Text.pack $ prettyvmresult res@@ -783,23 +280,7 @@             _ -> ""         ] -prettyCalldata :: (?context :: DappContext) => SMTCex -> Expr Buf -> Text -> [AbiType] -> Text-prettyCalldata cex buf sig types = head (Text.splitOn "(" sig) <> showCalldata cex types buf--showCalldata :: (?context :: DappContext) => SMTCex -> [AbiType] -> Expr Buf -> Text-showCalldata cex tps buf = "(" <> intercalate "," (fmap showVal vals) <> ")"-  where-    argdata = Expr.drop 4 $ simplify $ subModel cex buf-    vals = case decodeBuf tps argdata of-             CAbi v -> v-             _ -> internalError $ "unable to abi decode function arguments:\n" <> (Text.unpack $ formatExpr argdata)--showVal :: AbiValue -> Text-showVal (AbiBytes _ bs) = formatBytes bs-showVal (AbiAddress addr) = Text.pack  . show $ addr-showVal v = Text.pack . show $ v--execSymTest :: UnitTestOptions -> ABIMethod -> (Expr Buf, [Prop]) -> Stepper (Expr End)+execSymTest :: UnitTestOptions RealWorld -> ABIMethod -> (Expr Buf, [Prop]) -> Stepper RealWorld (Expr End) execSymTest UnitTestOptions{ .. } method cd = do   -- Set up the call to the test method   Stepper.evm $ do@@ -808,7 +289,7 @@   -- Try running the test method   runExpr -checkSymFailures :: UnitTestOptions -> Stepper VM+checkSymFailures :: UnitTestOptions RealWorld -> Stepper RealWorld (VM RealWorld) checkSymFailures UnitTestOptions { .. } = do   -- Ask whether any assertions failed   Stepper.evm $ do@@ -821,7 +302,7 @@   let p = Text.replicate n " "   in Text.unlines (map (p <>) (Text.lines s)) -passOutput :: VM -> UnitTestOptions -> Text -> Text+passOutput :: VM s -> UnitTestOptions s -> Text -> Text passOutput vm UnitTestOptions { .. } testName =   let ?context = DappContext { info = dapp, env = vm.env.contracts }   in let v = fromMaybe 0 verbose@@ -836,8 +317,7 @@       ]     else "" --- TODO-failOutput :: VM -> UnitTestOptions -> Text -> Text+failOutput :: VM s -> UnitTestOptions s -> Text -> Text failOutput vm UnitTestOptions { .. } testName =   let ?context = DappContext { info = dapp, env = vm.env.contracts }   in mconcat@@ -922,37 +402,37 @@                   _ -> Nothing               _ -> Just "<symbolic decimal>" -abiCall :: TestVMParams -> Either (Text, AbiValue) ByteString -> EVM ()+abiCall :: TestVMParams -> Either (Text, AbiValue) ByteString -> EVM s () abiCall params args =   let cd = case args of         Left (sig, args') -> abiMethod sig args'         Right b -> b   in makeTxCall params (ConcreteBuf cd, []) -makeTxCall :: TestVMParams -> (Expr Buf, [Prop]) -> EVM ()+makeTxCall :: TestVMParams -> (Expr Buf, [Prop]) -> EVM s () makeTxCall params (cd, cdProps) = do   resetState   assign (#tx % #isCreate) False-  loadContract params.address+  execState (loadContract params.address) <$> get >>= put   assign (#state % #calldata) cd   #constraints %= (<> cdProps)-  assign (#state % #caller) (litAddr params.caller)+  assign (#state % #caller) params.caller   assign (#state % #gas) params.gasCall-  origin' <- fromMaybe (initialContract (RuntimeCode (ConcreteRuntimeCode ""))) <$> use (#env % #contracts % at params.origin)-  let originBal = origin'.balance-  when (originBal < params.gasprice * (into params.gasCall)) $ internalError "insufficient balance for gas cost"+  origin <- fromMaybe (initialContract (RuntimeCode (ConcreteRuntimeCode ""))) <$> use (#env % #contracts % at params.origin)+  let insufficientBal = maybe False (\b -> b < params.gasprice * (into params.gasCall)) (maybeLitWord origin.balance)+  when insufficientBal $ internalError "insufficient balance for gas cost"   vm <- get   put $ initTx vm -initialUnitTestVm :: UnitTestOptions -> SolcContract -> VM-initialUnitTestVm (UnitTestOptions {..}) theContract =-  let-    vm = makeVm $ VMOpts+initialUnitTestVm :: UnitTestOptions s -> SolcContract -> ST s (VM s)+initialUnitTestVm (UnitTestOptions {..}) theContract = do+  vm <- makeVm $ VMOpts            { contract = initialContract (InitCode theContract.creationCode mempty)+           , otherContracts = []            , calldata = mempty            , value = Lit 0            , address = testParams.address-           , caller = litAddr testParams.caller+           , caller = testParams.caller            , origin = testParams.origin            , gas = testParams.gasCreate            , gaslimit = testParams.gasCreate@@ -965,56 +445,53 @@            , priorityFee = testParams.priorityFee            , maxCodeSize = testParams.maxCodeSize            , prevRandao = testParams.prevrandao-           , schedule = FeeSchedule.berlin+           , schedule = feeSchedule            , chainId = testParams.chainId            , create = True-           , initialStorage = EmptyStore+           , baseState = EmptyBase            , txAccessList = mempty -- TODO: support unit test access lists???            , allowFFI = ffiAllowed            }-    creator =-      initialContract (RuntimeCode (ConcreteRuntimeCode ""))-        & set #nonce 1-        & set #balance testParams.balanceCreate-  in vm-    & set (#env % #contracts % at ethrunAddress) (Just creator)---getParametersFromEnvironmentVariables :: Maybe Text -> IO TestVMParams-getParametersFromEnvironmentVariables rpc = do-  block' <- maybe Fetch.Latest (Fetch.BlockNumber . read) <$> (lookupEnv "DAPP_TEST_NUMBER")+  let creator =+        initialContract (RuntimeCode (ConcreteRuntimeCode ""))+          & set #nonce (Just 1)+          & set #balance (Lit testParams.balanceCreate)+  pure $ vm & set (#env % #contracts % at (LitAddr ethrunAddress)) (Just creator) -  (miner,ts,blockNum,ran,limit,base) <--    case rpc of-      Nothing  -> pure (0,Lit 0,0,0,0,0)-      Just url -> Fetch.fetchBlockFrom block' url >>= \case-        Nothing -> internalError "Could not fetch block"-        Just Block{..} -> pure ( coinbase-                               , timestamp-                               , number-                               , prevRandao-                               , gaslimit-                               , baseFee-                               )-  let-    getWord s def = maybe def read <$> lookupEnv s-    getAddr s def = maybe def read <$> lookupEnv s-    ts' = fromMaybe (internalError "received unexpected symbolic timestamp via rpc") (maybeLitWord ts)+paramsFromRpc :: Fetch.RpcInfo -> IO TestVMParams+paramsFromRpc rpcinfo = do+  (miner,ts,blockNum,ran,limit,base) <- case rpcinfo of+    Nothing -> pure (SymAddr "miner", Lit 0, 0, 0, 0, 0)+    Just (block, url) -> Fetch.fetchBlockFrom block url >>= \case+      Nothing -> internalError "Could not fetch block"+      Just Block{..} -> pure ( coinbase+                             , timestamp+                             , number+                             , prevRandao+                             , gaslimit+                             , baseFee+                             )+  let ts' = fromMaybe (internalError "received unexpected symbolic timestamp via rpc") (maybeLitWord ts)+  pure $ TestVMParams+    -- TODO: make this symbolic! It needs some tweaking to the way that our+    -- symbolic interpreters work to allow us to symbolically exec constructor initialization+    { address = LitAddr 0xacab+    , caller = SymAddr "caller"+    , origin = SymAddr "origin"+    , gasCreate = defaultGasForCreating+    , gasCall = defaultGasForInvoking+    , baseFee = base+    , priorityFee = 0+    , balanceCreate = defaultBalanceForTestContract+    , coinbase = miner+    , number = blockNum+    , timestamp = ts'+    , gaslimit = limit+    , gasprice = 0+    , maxCodeSize = defaultMaxCodeSize+    , prevrandao = ran+    , chainId = 99+    } -  TestVMParams-    <$> getAddr "DAPP_TEST_ADDRESS" (createAddress ethrunAddress 1)-    <*> getAddr "DAPP_TEST_CALLER" ethrunAddress-    <*> getAddr "DAPP_TEST_ORIGIN" ethrunAddress-    <*> getWord "DAPP_TEST_GAS_CREATE" defaultGasForCreating-    <*> getWord "DAPP_TEST_GAS_CALL" defaultGasForInvoking-    <*> getWord "DAPP_TEST_BASEFEE" base-    <*> getWord "DAPP_TEST_PRIORITYFEE" 0-    <*> getWord "DAPP_TEST_BALANCE" defaultBalanceForTestContract-    <*> getAddr "DAPP_TEST_COINBASE" miner-    <*> getWord "DAPP_TEST_NUMBER" blockNum-    <*> getWord "DAPP_TEST_TIMESTAMP" ts'-    <*> getWord "DAPP_TEST_GAS_LIMIT" limit-    <*> getWord "DAPP_TEST_GAS_PRICE" 0-    <*> getWord "DAPP_TEST_MAXCODESIZE" defaultMaxCodeSize-    <*> getWord "DAPP_TEST_PREVRANDAO" ran-    <*> getWord "DAPP_TEST_CHAINID" 99+tick :: Text -> IO ()+tick x = Text.putStr x >> hFlush stdout
test/EVM/Test/BlockchainTests.hs view
@@ -2,20 +2,19 @@  import EVM (initialContract, makeVm) import EVM.Concrete qualified as EVM-import EVM.Dapp (emptyDapp)-import EVM.Expr (litAddr)-import EVM.FeeSchedule qualified+import EVM.FeeSchedule (feeSchedule) import EVM.Fetch qualified import EVM.Format (hexText) import EVM.Stepper qualified-import EVM.Solvers (withSolvers, Solver(Z3)) import EVM.Transaction-import EVM.TTY qualified as TTY import EVM.Types hiding (Block, Case)+import EVM.Test.Tracing (interpretWithTrace, VMTrace, compareTraces, EVMToolTraceOutput(..)) -import Control.Arrow ((***), (&&&)) import Optics.Core+import Control.Arrow ((***), (&&&)) import Control.Monad+import Control.Monad.ST (RealWorld, stToIO)+import Control.Monad.State.Strict import Data.Aeson ((.:), (.:?), FromJSON (..)) import Data.Aeson qualified as JSON import Data.Aeson.Types qualified as JSON@@ -30,15 +29,13 @@ import System.Environment (lookupEnv, getEnv) import System.FilePath.Find qualified as Find import System.FilePath.Posix (makeRelative, (</>))+import Witch (into, unsafeInto) import Witherable (Filterable, catMaybes)  import Test.Tasty import Test.Tasty.ExpectedFailure import Test.Tasty.HUnit-import Witch (into, unsafeInto) -type Storage = Map W256 W256- data Which = Pre | Post  data Block = Block@@ -54,14 +51,14 @@  data Case = Case   { vmOpts      :: VMOpts-  , checkContracts  :: Map Addr (Contract, Storage)-  , testExpectation :: Map Addr (Contract, Storage)+  , checkContracts  :: Map Addr Contract+  , testExpectation :: Map Addr Contract   } deriving Show  data BlockchainCase = BlockchainCase   { blocks  :: [Block]-  , pre     :: Map Addr (Contract, Storage)-  , post    :: Map Addr (Contract, Storage)+  , pre     :: Map Addr Contract+  , post    :: Map Addr Contract   , network :: String   } deriving Show @@ -89,7 +86,7 @@   parsed <- parseBCSuite <$> LazyByteString.readFile file   case parsed of    Left "No cases to check." -> pure [] -- error "no-cases ok"-   Left _err -> pure [] -- error err+   Left _err -> pure [] -- error _err    Right allTests -> pure $      (\(name, x) -> testCase' name $ runVMTest True (name, x)) <$> Map.toList allTests   where@@ -131,41 +128,55 @@  runVMTest :: Bool -> (String, Case) -> IO () runVMTest diffmode (_name, x) = do-  let vm0 = vmForCase x+  vm0 <- vmForCase x   result <- EVM.Stepper.interpret (EVM.Fetch.zero 0 (Just 0)) vm0 EVM.Stepper.runFully   maybeReason <- checkExpectation diffmode x result   forM_ maybeReason assertFailure --- | Example usage:--- $ cabal new-repl ethereum-tests--- ghci> debugVMTest "BlockchainTests/GeneralStateTests/VMTests/vmArithmeticTest/twoOps.json" "twoOps_d0g0v0_London"-debugVMTest :: String -> String -> IO ()-debugVMTest file test = do+-- | Run a vm test and output a geth style per opcode trace+traceVMTest :: String -> String -> IO [VMTrace]+traceVMTest file test = do   repo <- getEnv "HEVM_ETHEREUM_TESTS_REPO"   Right allTests <- parseBCSuite <$> LazyByteString.readFile (repo </> file)   let x = case filter (\(name, _) -> name == test) $ Map.toList allTests of         [(_, x')] -> x'         _ -> internalError "test not found"-  let vm0 = vmForCase x-  result <- withSolvers Z3 0 Nothing $ \solvers ->-    TTY.runFromVM solvers Nothing Nothing emptyDapp vm0-  void $ checkExpectation True x result+  vm0 <- vmForCase x+  (_, (_, ts)) <- runStateT (interpretWithTrace (EVM.Fetch.zero 0 (Just 0)) EVM.Stepper.runFully) (vm0, [])+  pure ts +-- | Read a geth trace from disk+readTrace :: FilePath -> IO (Either String EVMToolTraceOutput)+readTrace = JSON.eitherDecodeFileStrict++-- | given a path to a test file, a test case from within that file, and a trace from geth from running that test, compare the traces and show where we differ+-- This would need a few tweaks to geth to make this really usable (i.e. evm statetest show allow running a single test from within the test file).+traceVsGeth :: String -> String -> FilePath -> IO ()+traceVsGeth file test gethTrace = do+  hevm <- traceVMTest file test+  EVMToolTraceOutput ts _ <- fromJust <$> (JSON.decodeFileStrict gethTrace :: IO (Maybe EVMToolTraceOutput))+  _ <- compareTraces hevm ts+  pure ()+ splitEithers :: (Filterable f) => f (Either a b) -> (f a, f b) splitEithers =   (catMaybes *** catMaybes)   . (fmap fst &&& fmap snd)   . (fmap (preview _Left &&& preview _Right)) -checkStateFail :: Bool -> Case -> VM -> (Bool, Bool, Bool, Bool) -> IO String+fromConcrete :: Expr Storage -> Map W256 W256+fromConcrete (ConcreteStore s) = s+fromConcrete s = internalError $ "unexpected abstract store: " <> show s++checkStateFail :: Bool -> Case -> VM RealWorld -> (Bool, Bool, Bool, Bool) -> IO String checkStateFail diff x vm (okMoney, okNonce, okData, okCode) = do   let-    printContracts :: Map Addr (Contract, Storage) -> IO ()-    printContracts cs = putStrLn $ Map.foldrWithKey (\k (c, s) acc ->+    printContracts :: Map Addr Contract -> IO ()+    printContracts cs = putStrLn $ Map.foldrWithKey (\k c acc ->       acc ++ show k ++ " : "-                   ++ (show . toInteger  $ (view #nonce c)) ++ " "-                   ++ (show . toInteger  $ (view #balance c)) ++ " "-                   ++ (printStorage s)+                   ++ (show $ fromJust $ c.nonce) ++ " "+                   ++ (show $ fromJust $ maybeLitWord c.balance) ++ " "+                   ++ (show $ fromConcrete $ c.storage)         ++ "\n") "" cs      reason = map fst (filter (not . snd)@@ -177,8 +188,7 @@         ])     check = x.checkContracts     expected = x.testExpectation-    actual = Map.map (,mempty) $ view (#env % #contracts) vm -- . to (fmap (clearZeroStorage.clearOrigStorage))) vm-    printStorage = show -- TODO: fixme+    actual = fmap (clearZeroStorage . clearOrigStorage) $ forceConcreteAddrs vm.env.contracts    when diff $ do     putStr (unwords reason)@@ -190,7 +200,7 @@     printContracts actual   pure (unwords reason) -checkExpectation :: Bool -> Case -> VM -> IO (Maybe String)+checkExpectation :: Bool -> Case -> VM RealWorld -> IO (Maybe String) checkExpectation diff x vm = do   let expectation = x.testExpectation       (okState, b2, b3, b4, b5) = checkExpectedContracts vm expectation@@ -200,67 +210,69 @@     Just <$> checkStateFail diff x vm (b2, b3, b4, b5)  -- quotient account state by nullness-(~=) :: Map Addr (Contract, Storage) -> Map Addr (Contract, Storage) -> Bool+(~=) :: Map Addr Contract -> Map Addr Contract -> Bool (~=) cs1 cs2 =     let nullAccount = EVM.initialContract (RuntimeCode (ConcreteRuntimeCode ""))-        padNewAccounts cs ks = Map.union cs $ Map.fromList [(k, (nullAccount, mempty)) | k <- ks]+        padNewAccounts cs ks = Map.union cs $ Map.fromList [(k, nullAccount) | k <- ks]         padded_cs1 = padNewAccounts cs1 (Map.keys cs2)         padded_cs2 = padNewAccounts cs2 (Map.keys cs1)     in and $ zipWith (===) (Map.elems padded_cs1) (Map.elems padded_cs2) -(===) :: (Contract, Storage) -> (Contract, Storage) -> Bool-(c1, s1) === (c2, s2) =+(===) :: Contract -> Contract -> Bool+c1 === c2 =   codeEqual && storageEqual && (c1 ^. #balance == c2 ^. #balance) && (c1 ^. #nonce ==  c2 ^. #nonce)   where-    storageEqual = s1 == s2-    codeEqual = case (c1 ^. #contractcode, c2 ^. #contractcode) of+    storageEqual = c1.storage == c2.storage+    codeEqual = case (c1 ^. #code, c2 ^. #code) of       (RuntimeCode a', RuntimeCode b') -> a' == b'       _ -> internalError "unexpected code" -checkExpectedContracts :: VM -> Map Addr (Contract, Storage) -> (Bool, Bool, Bool, Bool, Bool)+checkExpectedContracts :: VM RealWorld -> Map Addr Contract -> (Bool, Bool, Bool, Bool, Bool) checkExpectedContracts vm expected =-  let cs = zipWithStorages $ vm ^. #env % #contracts -- . to (fmap (clearZeroStorage.clearOrigStorage))-      expectedCs = clearStorage <$> expected-  in ( (expectedCs ~= cs)-     , (clearBalance <$> expectedCs) ~= (clearBalance <$> cs)-     , (clearNonce   <$> expectedCs) ~= (clearNonce   <$> cs)-     , (clearStorage <$> expectedCs) ~= (clearStorage <$> cs)-     , (clearCode    <$> expectedCs) ~= (clearCode    <$> cs)+  let cs = fmap (clearZeroStorage . clearOrigStorage) $ forceConcreteAddrs vm.env.contracts+  in ( (expected ~= cs)+     , (clearBalance <$> expected) ~= (clearBalance <$> cs)+     , (clearNonce   <$> expected) ~= (clearNonce   <$> cs)+     , (clearStorage <$> expected) ~= (clearStorage <$> cs)+     , (clearCode    <$> expected) ~= (clearCode    <$> cs)      )-  where-  zipWithStorages = Map.mapWithKey (\addr c -> (c, lookupStorage addr))-  lookupStorage _ =-    case vm ^. #env % #storage of-      ConcreteStore _ -> mempty -- clearZeroStorage $ fromMaybe mempty $ Map.lookup (num addr) s-      EmptyStore -> mempty-      AbstractStore -> mempty -- error "AbstractStore, should this be handled?"-      SStore {} -> mempty -- error "SStore, should this be handled?"-      GVar _ -> internalError "unexpected global variable" -clearStorage :: (Contract, Storage) -> (Contract, Storage)-clearStorage (c, _) = (c, mempty)+clearOrigStorage :: Contract -> Contract+clearOrigStorage = set #origStorage (ConcreteStore mempty) -clearBalance :: (Contract, Storage) -> (Contract, Storage)-clearBalance (c, s) = (set #balance 0 c, s)+clearZeroStorage :: Contract -> Contract+clearZeroStorage c = case c.storage of+  ConcreteStore m -> let store = Map.filter (/= 0) m+                     in set #storage (ConcreteStore store) c+  _ -> internalError "Internal Error: unexpected abstract store" -clearNonce :: (Contract, Storage) -> (Contract, Storage)-clearNonce (c, s) = (set #nonce 0 c, s)+clearStorage :: Contract -> Contract+clearStorage c = c { storage = clear c.storage }+  where+    clear :: Expr Storage -> Expr Storage+    clear (ConcreteStore _) = ConcreteStore mempty+    clear _ = internalError "Internal Error: unexpected abstract store" -clearCode :: (Contract, Storage) -> (Contract, Storage)-clearCode (c, s) = (set #contractcode (RuntimeCode (ConcreteRuntimeCode "")) c, s)+clearBalance :: Contract -> Contract+clearBalance c = set #balance (Lit 0) c -newtype ContractWithStorage = ContractWithStorage (Contract, Storage)+clearNonce :: Contract -> Contract+clearNonce c = set #nonce (Just 0) c -instance FromJSON ContractWithStorage where+clearCode :: Contract -> Contract+clearCode c = set #code (RuntimeCode (ConcreteRuntimeCode "")) c++instance FromJSON Contract where   parseJSON (JSON.Object v) = do     code <- (RuntimeCode . ConcreteRuntimeCode <$> (hexText <$> v .: "code"))-    storage' <- v .: "storage"-    balance' <- v .: "balance"-    nonce'   <- v .: "nonce"-    let c = EVM.initialContract code-              & #balance .~ balance'-              & #nonce   .~ nonce'-    return $ ContractWithStorage (c, storage')+    storage <- v .: "storage"+    balance <- v .: "balance"+    nonce   <- v .: "nonce"+    pure $ EVM.initialContract code+             & #balance .~ (Lit balance)+             & #nonce   ?~ nonce+             & #storage .~ (ConcreteStore storage)+             & #origStorage .~ (ConcreteStore storage)    parseJSON invalid =     JSON.typeMismatch "Contract" invalid@@ -285,17 +297,15 @@     baseFee    <- fmap read <$> v' .:? "baseFeePerGas"     timestamp  <- wordField v' "timestamp"     mixHash    <- wordField v' "mixHash"-    return $ Block coinbase difficulty mixHash gasLimit (fromMaybe 0 baseFee) number timestamp txs+    pure $ Block coinbase difficulty mixHash gasLimit (fromMaybe 0 baseFee) number timestamp txs   parseJSON invalid =     JSON.typeMismatch "Block" invalid -parseContracts :: Which -> JSON.Object -> JSON.Parser (Map Addr (Contract, Storage))-parseContracts w v =-  (Map.map unwrap) <$> (v .: which >>= parseJSON)+parseContracts :: Which -> JSON.Object -> JSON.Parser (Map Addr Contract)+parseContracts w v = v .: which >>= parseJSON   where which = case w of           Pre  -> "pre"           Post -> "postState"-        unwrap (ContractWithStorage x) = x  parseBCSuite :: Lazy.ByteString -> Either String (Map String Case) parseBCSuite x = case (JSON.eitherDecode' x) :: Either String (Map String BlockchainCase) of@@ -343,7 +353,7 @@ maxCodeSize = 24576  fromBlockchainCase' :: Block -> Transaction-                       -> Map Addr (Contract, Storage) -> Map Addr (Contract, Storage)+                       -> Map Addr Contract -> Map Addr Contract                        -> Either BlockchainError Case fromBlockchainCase' block tx preState postState =   let isCreate = isNothing tx.toAddr in@@ -352,40 +362,40 @@       (_, Nothing) -> Left (if isCreate then FailedCreate else InvalidTx)       (Just origin, Just checkState) -> Right $ Case         (VMOpts-         { contract      = EVM.initialContract theCode-         , calldata      = (cd, [])-         , value         = Lit tx.value-         , address       = toAddr-         , caller        = litAddr origin-         , initialStorage = EmptyStore-         , origin        = origin-         , gas           = tx.gasLimit - txGasCost feeSchedule tx-         , baseFee       = block.baseFee-         , priorityFee   = priorityFee tx block.baseFee-         , gaslimit      = tx.gasLimit-         , number        = block.number-         , timestamp     = Lit block.timestamp-         , coinbase      = block.coinbase-         , prevRandao    = block.mixHash-         , maxCodeSize   = maxCodeSize-         , blockGaslimit = block.gasLimit-         , gasprice      = effectiveGasPrice-         , schedule      = feeSchedule-         , chainId       = 1-         , create        = isCreate-         , txAccessList  = txAccessMap tx-         , allowFFI      = False+         { contract       = EVM.initialContract theCode+         , otherContracts = []+         , calldata       = (cd, [])+         , value          = Lit tx.value+         , address        = toAddr+         , caller         = LitAddr origin+         , baseState      = EmptyBase+         , origin         = LitAddr origin+         , gas            = tx.gasLimit - txGasCost feeSchedule tx+         , baseFee        = block.baseFee+         , priorityFee    = priorityFee tx block.baseFee+         , gaslimit       = tx.gasLimit+         , number         = block.number+         , timestamp      = Lit block.timestamp+         , coinbase       = LitAddr block.coinbase+         , prevRandao     = block.mixHash+         , maxCodeSize    = maxCodeSize+         , blockGaslimit  = block.gasLimit+         , gasprice       = effectiveGasPrice+         , schedule       = feeSchedule+         , chainId        = 1+         , create         = isCreate+         , txAccessList   = Map.mapKeys LitAddr (txAccessMap tx)+         , allowFFI       = False          })         checkState         postState           where-            toAddr = fromMaybe (EVM.createAddress origin senderNonce) tx.toAddr-            senderNonce = view (accountAt origin % #nonce) (Map.map fst preState)-            feeSchedule = EVM.FeeSchedule.berlin-            toCode = Map.lookup toAddr preState+            toAddr = maybe (EVM.createAddress origin (fromJust senderNonce)) LitAddr (tx.toAddr)+            senderNonce = view (accountAt (LitAddr origin) % #nonce) (Map.mapKeys LitAddr preState)+            toCode = Map.lookup toAddr (Map.mapKeys LitAddr preState)             theCode = if isCreate                       then InitCode tx.txdata mempty-                      else maybe (RuntimeCode (ConcreteRuntimeCode "")) (view #contractcode . fst) toCode+                      else maybe (RuntimeCode (ConcreteRuntimeCode "")) (view #code) toCode             effectiveGasPrice = effectiveprice tx block.baseFee             cd = if isCreate                  then mempty@@ -412,47 +422,45 @@      EIP1559Transaction -> fromJust tx.maxFeePerGas      _ -> fromJust tx.gasPrice -validateTx :: Transaction -> Block -> Map Addr (Contract, Storage) -> Maybe ()+validateTx :: Transaction -> Block -> Map Addr Contract -> Maybe () validateTx tx block cs = do-  let cs' = Map.map fst cs   origin        <- sender tx-  originBalance <- (view #balance) <$> view (at origin) cs'-  originNonce   <- (view #nonce)   <$> view (at origin) cs'+  (Lit originBalance) <- (view #balance) <$> view (at origin) cs+  originNonce   <- (view #nonce)   <$> view (at origin) cs   let gasDeposit = (effectiveprice tx block.baseFee) * (into tx.gasLimit)   if gasDeposit + tx.value <= originBalance-    && tx.nonce == originNonce && block.baseFee <= maxBaseFee tx+    && (Just (unsafeInto tx.nonce) == originNonce) && block.baseFee <= maxBaseFee tx   then Just ()   else Nothing -checkTx :: Transaction -> Block -> Map Addr (Contract, Storage) -> Maybe (Map Addr (Contract, Storage))+checkTx :: Transaction -> Block -> Map Addr Contract -> Maybe (Map Addr Contract) checkTx tx block prestate = do   origin <- sender tx   validateTx tx block prestate-  let isCreate   = isNothing tx.toAddr-      senderNonce = view (accountAt origin % #nonce) (Map.map fst prestate)-      toAddr      = fromMaybe (EVM.createAddress origin senderNonce) tx.toAddr-      prevCode    = view (accountAt toAddr % #contractcode) (Map.map fst prestate)-      prevNonce   = view (accountAt toAddr % #nonce) (Map.map fst prestate)+  let isCreate    = isNothing tx.toAddr+      cs          = Map.mapKeys LitAddr prestate+      senderNonce = view (accountAt (LitAddr origin) % #nonce) cs+      toAddr      = maybe (EVM.createAddress origin (fromJust senderNonce)) LitAddr (tx.toAddr)+      prevCode    = view (accountAt toAddr % #code) cs+      prevNonce   = view (accountAt toAddr % #nonce) cs        nonEmptyAccount = case prevCode of                         RuntimeCode (ConcreteRuntimeCode b) -> not (BS.null b)                         _ -> True-      badNonce = prevNonce /= 0+      badNonce = prevNonce /= Just 0       initCodeSizeExceeded = BS.length tx.txdata > (unsafeInto maxCodeSize * 2)   if isCreate && (badNonce || nonEmptyAccount || initCodeSizeExceeded)   then mzero   else-    return prestate+    pure prestate -vmForCase :: Case -> VM-vmForCase x =-  let-    a = x.checkContracts-    cs = Map.map fst a-    st = Map.mapKeys into $ Map.map snd a-    vm = makeVm x.vmOpts-      & set (#env % #contracts) cs-      & set (#env % #storage) (ConcreteStore st)-      & set (#env % #origStorage) st-  in-    initTx vm+vmForCase :: Case -> IO (VM RealWorld)+vmForCase x = do+  vm <- stToIO $ makeVm x.vmOpts+    <&> set (#env % #contracts) (Map.mapKeys LitAddr x.checkContracts)+  pure $ initTx vm++forceConcreteAddrs :: Map (Expr EAddr) Contract -> Map Addr Contract+forceConcreteAddrs cs = Map.mapKeys+      (fromMaybe (internalError "Internal Error: unexpected symbolic address") . maybeLitAddr)+      cs
test/EVM/Test/Tracing.hs view
@@ -13,7 +13,8 @@  import Control.Monad (when) import Control.Monad.Operational qualified as Operational-import Control.Monad.State.Strict (StateT(..), liftIO, runState)+import Control.Monad.ST (RealWorld, ST, stToIO)+import Control.Monad.State.Strict (StateT(..), liftIO) import Control.Monad.State.Strict qualified as State import Data.Aeson ((.:), (.:?)) import Data.Aeson qualified as JSON@@ -36,22 +37,21 @@ import Test.QuickCheck.Instances.Natural() import Test.QuickCheck.Instances.ByteString() import Test.Tasty (testGroup, TestTree)-import Test.Tasty.HUnit (assertEqual)+import Test.Tasty.HUnit (assertEqual, testCase) import Test.Tasty.QuickCheck hiding (Failure, Success)-import Witch (into)+import Witch (into, unsafeInto)  import Optics.Core hiding (pre) import Optics.State-import Optics.Zoom  import EVM (makeVm, initialContract, exec1) import EVM.Assembler (assemble) import EVM.Expr qualified as Expr+import EVM.Concrete qualified as Concrete import EVM.Exec (ethrunAddress) import EVM.Fetch qualified as Fetch import EVM.Format (bsToHex, formatBinary)-import EVM.Concrete (createAddress)-import EVM.FeeSchedule qualified as FeeSchedule+import EVM.FeeSchedule import EVM.Op (intToOpName) import EVM.Sign (deriveAddr) import EVM.Solvers@@ -65,10 +65,10 @@   VMTrace   { tracePc      :: Int   , traceOp      :: Int-  , traceStack   :: [W256]-  , traceMemSize :: Word64+  , traceGas     :: Data.Word.Word64+  , traceMemSize :: Data.Word.Word64   , traceDepth   :: Int-  , traceGas     :: Word64+  , traceStack   :: [W256]   , traceError   :: Maybe String   } deriving (Generic, Show) @@ -96,6 +96,7 @@     , opName :: String     , stack :: [W256]     , error :: Maybe String+    , gasCost :: Maybe W256     } deriving (Generic, Show)  instance JSON.FromJSON EVMToolTrace where@@ -109,6 +110,7 @@     <*> v .: "opName"     <*> v .: "stack"     <*> v .:? "error"+    <*> v .:? "gasCost"  mkBlockHash:: Int -> Expr 'EWord mkBlockHash x = (into x :: Integer) & show & Char8.pack & EVM.Types.keccak' & Lit@@ -120,6 +122,8 @@   EVMToolOutput     { output :: ByteStringS     , gasUsed :: W256+    , time :: Maybe Integer+    , error :: Maybe String     } deriving (Generic, Show)  instance JSON.FromJSON EVMToolOutput@@ -140,14 +144,14 @@   , gasLimit    :: Data.Word.Word64   , baseFee     :: W256   , maxCodeSize :: W256-  , schedule    :: FeeSchedule.FeeSchedule Data.Word.Word64+  , schedule    :: FeeSchedule Data.Word.Word64   , blockHashes :: Map.Map Int W256   } deriving (Show, Generic)  instance JSON.ToJSON EVMToolEnv where   toJSON b = JSON.object [ ("currentCoinBase"  , (JSON.toJSON $ b.coinbase))                          , ("currentDifficulty", (JSON.toJSON $ b.prevRandao))-                         , ("currentGasLimit"  , (JSON.toJSON ("0x" ++ showHex (toInteger $ b.gasLimit) "")))+                         , ("currentGasLimit"  , (JSON.toJSON ("0x" ++ showHex (into @Integer b.gasLimit) "")))                          , ("currentNumber"    , (JSON.toJSON $ b.number))                          , ("currentTimestamp" , (JSON.toJSON tstamp))                          , ("currentBaseFee"   , (JSON.toJSON $ b.baseFee))@@ -167,7 +171,7 @@                              , gasLimit   = 0xffffffffffffffff                              , baseFee    = 0                              , maxCodeSize= 0xffffffff-                             , schedule   = FeeSchedule.berlin+                             , schedule   = feeSchedule                              , blockHashes = mempty                              } @@ -263,7 +267,7 @@                              }     txn = EVM.Transaction.Transaction       { txdata     = txData-      , gasLimit = fromIntegral gaslimitExec+      , gasLimit = unsafeInto gaslimitExec       , gasPrice = Just 1       , nonce    = 172       , toAddr   = Just 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192@@ -281,23 +285,20 @@                         , timestamp   =  Lit 0x3e8                         , number      =  0x0                         , prevRandao  =  0x0-                        , gasLimit    =  fromIntegral gaslimitExec+                        , gasLimit    =  unsafeInto gaslimitExec                         , baseFee     =  0x0                         , maxCodeSize =  0xfffff-                        , schedule    =  FeeSchedule.berlin+                        , schedule    =  feeSchedule                         , blockHashes =  blockHashesDefault                         }     sk = 0xDC38EE117CAE37750EB1ECC5CFD3DE8E85963B481B93E732C5D0CB66EE6B0C9D-    fromAddress :: Addr     fromAddress = fromJust $ deriveAddr sk-    toAddress :: Addr     toAddress = 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192  getHEVMRet :: OpContract -> ByteString -> Int -> IO (Either (EvmError, [VMTrace]) (Expr 'End, [VMTrace], VMTraceResult)) getHEVMRet contr txData gaslimitExec = do   let (txn, evmEnv, contrAlloc, fromAddress, toAddress, _) = evmSetup contr txData gaslimitExec-  hevmRun <- runCodeWithTrace Nothing evmEnv contrAlloc txn (fromAddress, toAddress)-  return hevmRun+  runCodeWithTrace Nothing evmEnv contrAlloc txn (LitAddr fromAddress) (LitAddr toAddress)  getEVMToolRet :: OpContract -> ByteString -> Int -> IO (Maybe EVMToolResult) getEVMToolRet contr txData gaslimitExec = do@@ -325,8 +326,7 @@     putStrLn $ "evmtool exited with code " <> show exitCode     putStrLn $ "evmtool stderr output:" <> show evmtoolStderr     putStrLn $ "evmtool stdout output:" <> show evmtoolStdout-  evmtoolResult <- JSON.decodeFileStrict "result.json" :: IO (Maybe EVMToolResult)-  return evmtoolResult+  JSON.decodeFileStrict "result.json" :: IO (Maybe EVMToolResult)  -- Compares traces of evmtool (from go-ethereum) and HEVM compareTraces :: [VMTrace] -> [EVMToolTrace] -> IO (Bool)@@ -341,21 +341,19 @@           bPc = b.pc           aStack = a.traceStack           bStack = b.stack-          aGas = fromIntegral a.traceGas+          aGas = into a.traceGas           bGas = b.gas-      if aGas /= bGas then do-                          putStrLn "GAS doesn't match:"-                          putStrLn $ "HEVM's gas   : " <> (show aGas)-                          putStrLn $ "evmtool's gas: " <> (show bGas)-                          else-                          -- putStrLn $ "Gas match   : " <> (show aGas)-                          return ()-      if aOp /= bOp || aPc /= bPc then-                          putStrLn $ "HEVM: " <> (intToOpName aOp) <> " (pc " <> (show aPc) <> ") --- evmtool " <> (intToOpName bOp) <> " (pc " <> (show bPc) <> ")"-                          else-                          -- putStrLn $ "trace element match. " <> (intToOpName aOp) <> " pc: " <> (show aPc)-                          return ()+      -- putStrLn $ "hevm: " <> intToOpName aOp <> " pc: " <> show aPc <> " gas: " <> show aGas <> " stack: " <> show aStack+      -- putStrLn $ "geth: " <> intToOpName bOp <> " pc: " <> show bPc <> " gas: " <> show bGas <> " stack: " <> show bStack +      when (aGas /= bGas) $ do+        putStrLn "GAS doesn't match:"+        putStrLn $ "HEVM's gas   : " <> (show aGas)+        putStrLn $ "evmtool's gas: " <> (show bGas)+        putStrLn $ "executing opcode: " <> (intToOpName aOp)+      when (aOp /= bOp || aPc /= bPc) $ do+        putStrLn $ "HEVM: " <> (intToOpName aOp) <> " (pc " <> (show aPc) <> ") --- evmtool " <> (intToOpName bOp) <> " (pc " <> (show bPc) <> ")"+       when (isJust b.error) $ do         putStrLn $ "Error by evmtool: " <> (show b.error)         putStrLn $ "Error by HEVM   : " <> (show a.traceError)@@ -403,25 +401,25 @@ deleteTraceOutputFiles :: Maybe EVMToolResult -> IO () deleteTraceOutputFiles evmtoolResult =   case evmtoolResult of-    Nothing -> return ()+    Nothing -> pure ()     Just res -> do       let traceFileName = getTraceFileName res       System.Directory.removeFile traceFileName       System.Directory.removeFile (traceFileName ++ ".json")  -- Create symbolic VM from concrete VM-symbolify :: VM -> VM+symbolify :: VM s -> VM s symbolify vm = vm { state = vm.state { calldata = AbstractBuf "calldata" } }  -- | Takes a runtime code and calls it with the provided calldata --   Uses evmtool's alloc and transaction to set up the VM correctly-runCodeWithTrace :: Fetch.RpcInfo -> EVMToolEnv -> EVMToolAlloc -> EVM.Transaction.Transaction -> (Addr, Addr) -> IO (Either (EvmError, [VMTrace]) ((Expr 'End, [VMTrace], VMTraceResult)))-runCodeWithTrace rpcinfo evmEnv alloc txn (fromAddr, toAddress) = withSolvers Z3 0 Nothing $ \solvers -> do-  let origVM = vmForRuntimeCode code' calldata' evmEnv alloc txn (fromAddr, toAddress)-      calldata' = ConcreteBuf txn.txdata+runCodeWithTrace :: Fetch.RpcInfo -> EVMToolEnv -> EVMToolAlloc -> EVM.Transaction.Transaction -> Expr EAddr -> Expr EAddr -> IO (Either (EvmError, [VMTrace]) ((Expr 'End, [VMTrace], VMTraceResult)))+runCodeWithTrace rpcinfo evmEnv alloc txn fromAddr toAddress = withSolvers Z3 0 Nothing $ \solvers -> do+  let calldata' = ConcreteBuf txn.txdata       code' = alloc.code-      buildExpr :: SolverGroup -> VM -> IO (Expr End)+      buildExpr :: SolverGroup -> VM RealWorld -> IO (Expr End)       buildExpr s vm = interpret (Fetch.oracle s Nothing) Nothing 1 Naive vm runExpr+  origVM <- stToIO $ vmForRuntimeCode code' calldata' evmEnv alloc txn fromAddr toAddress    expr <- buildExpr solvers $ symbolify origVM   (res, (vm, trace)) <- runStateT (interpretWithTrace (Fetch.oracle solvers rpcinfo) Stepper.execFully) (origVM, [])@@ -429,24 +427,24 @@     Left x -> pure $ Left (x, trace)     Right _ -> pure $ Right (expr, trace, vmres vm) -vmForRuntimeCode :: ByteString -> Expr Buf -> EVMToolEnv -> EVMToolAlloc -> EVM.Transaction.Transaction -> (Addr, Addr) -> VM-vmForRuntimeCode runtimecode calldata' evmToolEnv alloc txn (fromAddr, toAddress) =-  let contr = initialContract (RuntimeCode (ConcreteRuntimeCode runtimecode))-      contrWithBal = (contr :: Contract) { balance = alloc.balance }-  in-  (makeVm $ VMOpts-    { contract = contrWithBal+vmForRuntimeCode :: ByteString -> Expr Buf -> EVMToolEnv -> EVMToolAlloc -> EVM.Transaction.Transaction -> Expr EAddr -> Expr EAddr -> ST s (VM s)+vmForRuntimeCode runtimecode calldata' evmToolEnv alloc txn fromAddr toAddress =+  let contract = initialContract (RuntimeCode (ConcreteRuntimeCode runtimecode))+                 & set #balance (Lit alloc.balance)+  in (makeVm $ VMOpts+    { contract = contract+    , otherContracts = []     , calldata = (calldata', [])     , value = Lit txn.value-    , initialStorage = EmptyStore+    , baseState = EmptyBase     , address =  toAddress-    , caller = Expr.litAddr fromAddr+    , caller = fromAddr     , origin = fromAddr-    , coinbase = evmToolEnv.coinbase+    , coinbase = LitAddr evmToolEnv.coinbase     , number = evmToolEnv.number     , timestamp = evmToolEnv.timestamp     , gasprice = fromJust txn.gasPrice-    , gas = txn.gasLimit - fromIntegral (EVM.Transaction.txGasCost evmToolEnv.schedule txn)+    , gas = txn.gasLimit - (EVM.Transaction.txGasCost evmToolEnv.schedule txn)     , gaslimit = txn.gasLimit     , blockGaslimit = evmToolEnv.gasLimit     , prevRandao = evmToolEnv.prevRandao@@ -458,19 +456,26 @@     , create = False     , txAccessList = mempty     , allowFFI = False-    }) & set (#env % #contracts % at ethrunAddress)+    }) <&> set (#env % #contracts % at (LitAddr ethrunAddress))              (Just (initialContract (RuntimeCode (ConcreteRuntimeCode BS.empty))))-       & set (#state % #calldata) calldata'+       <&> set (#state % #calldata) calldata'  runCode :: Fetch.RpcInfo -> ByteString -> Expr Buf -> IO (Maybe (Expr Buf)) runCode rpcinfo code' calldata' = withSolvers Z3 0 Nothing $ \solvers -> do-  let origVM = vmForRuntimeCode code' calldata' emptyEvmToolEnv emptyEVMToolAlloc EVM.Transaction.emptyTransaction (ethrunAddress, createAddress ethrunAddress 1)+  origVM <- stToIO $ vmForRuntimeCode+              code'+              calldata'+              emptyEvmToolEnv+              emptyEVMToolAlloc+              EVM.Transaction.emptyTransaction+              (LitAddr ethrunAddress)+              (Concrete.createAddress ethrunAddress 1)   res <- Stepper.interpret (Fetch.oracle solvers rpcinfo) origVM Stepper.execFully   pure $ case res of     Left _ -> Nothing     Right b -> Just b -vmtrace :: VM -> VMTrace+vmtrace :: VM s -> VMTrace vmtrace vm =   let     memsize = vm.state.memorySize@@ -485,7 +490,7 @@              , traceError = readoutError vm.result              }   where-    readoutError :: Maybe VMResult -> Maybe String+    readoutError :: Maybe (VMResult s) -> Maybe String     readoutError (Just (VMFailure e)) = case e of       -- NOTE: error text made to closely match go-ethereum's errors.go file       OutOfGas {}             -> Just "out of gas"@@ -508,7 +513,7 @@       err                     -> Just $ "HEVM error: " <> show err     readoutError _ = Nothing -vmres :: VM -> VMTraceResult+vmres :: VM s -> VMTraceResult vmres vm =   let     gasUsed' = vm.tx.gaslimit - vm.state.gas@@ -523,22 +528,23 @@      , gasUsed = gasUsed'      } -type TraceState = (VM, [VMTrace])+type TraceState s = (VM s, [VMTrace]) -execWithTrace :: StateT TraceState IO VMResult+execWithTrace :: StateT (TraceState RealWorld) IO (VMResult RealWorld) execWithTrace = do   _ <- runWithTrace   fromJust <$> use (_1 % #result) -runWithTrace :: StateT TraceState IO VM+runWithTrace :: StateT (TraceState RealWorld) IO (VM RealWorld) runWithTrace = do   -- This is just like `exec` except for every instruction evaluated,   -- we also increment a counter indexed by the current code location.   vm0 <- use _1   case vm0.result of     Nothing -> do-      State.modify (\(a, b) -> (a, b ++ [vmtrace vm0]))-      zoom _1 (State.state (runState exec1))+      State.modify' (\(a, b) -> (a, b ++ [vmtrace vm0]))+      vm' <- liftIO $ stToIO $ State.execStateT exec1 vm0+      assign _1 vm'       runWithTrace     Just (VMFailure _) -> do       -- Update error text for last trace element@@ -550,16 +556,16 @@     Just _ -> pure vm0  interpretWithTrace-  :: Fetch.Fetcher-  -> Stepper.Stepper a-  -> StateT TraceState IO a+  :: Fetch.Fetcher RealWorld+  -> Stepper.Stepper RealWorld a+  -> StateT (TraceState RealWorld) IO a interpretWithTrace fetcher =   eval . Operational.view    where     eval-      :: Operational.ProgramView Stepper.Action a-      -> StateT TraceState IO a+      :: Operational.ProgramView (Stepper.Action RealWorld) a+      -> StateT (TraceState RealWorld) IO a      eval (Operational.Return x) =       pure x@@ -568,22 +574,26 @@       case action of         Stepper.Exec ->           execWithTrace >>= interpretWithTrace fetcher . k-        Stepper.Run ->-          runWithTrace >>= interpretWithTrace fetcher . k         Stepper.Wait q -> case q of           PleaseAskSMT (Lit x) _ continue ->             interpretWithTrace fetcher (Stepper.evm (continue (Case (x > 0))) >>= k)           _ -> do             m <- liftIO (fetcher q)-            zoom _1 (State.state (runState m)) >> interpretWithTrace fetcher (k ())+            vm <- use _1+            vm' <- liftIO $ stToIO $ State.execStateT m vm+            assign _1 vm'+            interpretWithTrace fetcher (k ())         Stepper.Ask _ ->           internalError "cannot make choice in this interpreter"         Stepper.IOAct q ->-          zoom _1 (StateT (runStateT q)) >>= interpretWithTrace fetcher . k-        Stepper.EVM m ->-          zoom _1 (State.state (runState m)) >>= interpretWithTrace fetcher . k+          liftIO q >>= interpretWithTrace fetcher . k+        Stepper.EVM m -> do+          vm <- use _1+          (r, vm') <- liftIO $ stToIO $ State.runStateT m vm+          assign _1 vm'+          interpretWithTrace fetcher (k r) -data OpContract = OpContract [Op]+newtype OpContract = OpContract [Op] instance Show OpContract where   show (OpContract a) = "OpContract " ++ (show a) @@ -646,11 +656,11 @@       OpJumpi -> do         let filtDests = (filter (> pos) jumpDests)         rndPos <- randItem filtDests-        fixup ax (pos+34) (ret++[(OpPush (Lit (fromInteger (fromIntegral rndPos)))), (OpJumpi)])+        fixup ax (pos+34) (ret++[(OpPush (Lit (unsafeInto rndPos))), (OpJumpi)])       OpJump -> do         let filtDests = (filter (> pos) jumpDests)         rndPos <- randItem filtDests-        fixup ax (pos+34) (ret++[(OpPush (Lit (fromInteger (fromIntegral rndPos)))), (OpJump)])+        fixup ax (pos+34) (ret++[(OpPush (Lit (unsafeInto rndPos))), (OpJump)])       myop@(OpPush _) -> fixup ax (pos+33) (ret++[myop])       myop -> fixup ax (pos+1) (ret++[myop])   fmap OpContract ops2@@ -661,37 +671,40 @@     onePush :: Gen Op     onePush  = do       p <- chooseInt (1, 10)-      pure $ OpPush (Lit (fromIntegral p))+      pure $ OpPush (Lit (unsafeInto p))  genContract :: Int -> Gen [Op] genContract n = do     y <- chooseInt (3, 6)     pushes <- genPush y     normalOps <- vectorOf (3*n+40) genOne+    large :: Bool <- chooseAny+    extra <- if large then vectorOf (100) genOne+                      else pure []     addReturn <- chooseInt (0, 10)-    let contr = pushes ++ normalOps+    let contr = pushes ++ normalOps ++ extra     if addReturn < 10 then pure $ contr++[OpPush (Lit 0x40), OpPush (Lit 0x0), OpReturn]                       else pure contr   where     genOne :: Gen Op     genOne = frequency [       -- math ops-      (200, frequency [-          (1, pure OpAdd)-        , (1, pure OpMul)+      (20, frequency [+          (2, pure OpAdd)+        , (2, pure OpMul)         , (1, pure OpSub)-        , (1, pure OpDiv)+        , (2, pure OpDiv)         , (1, pure OpSdiv)-        , (1, pure OpMod)+        , (2, pure OpMod)         , (1, pure OpSmod)         , (1, pure OpAddmod)-        , (1, pure OpMulmod)+        , (2, pure OpMulmod)         , (1, pure OpExp)         , (1, pure OpSignextend)-        , (1, pure OpLt)-        , (1, pure OpGt)-        , (1, pure OpSlt)-        , (1, pure OpSgt)+        , (2, pure OpLt)+        , (2, pure OpGt)+        , (2, pure OpSlt)+        , (2, pure OpSgt)         , (1, pure OpSha3)       ])       -- Comparison & binary ops@@ -708,8 +721,8 @@         , (1, pure OpSar)       ])       -- calldata-      , (800, pure OpCalldataload)-      , (200, pure OpCalldatacopy)+      , (200, pure OpCalldataload)+      , (800, pure OpCalldatacopy)       -- Get some info       , (100, frequency [           (10, pure OpAddress)@@ -742,8 +755,8 @@       -- memory manip       , (1200, frequency [           (50, pure OpMload)-        , (50, pure OpMstore)-        , (1, pure OpMstore8)+        , (1, pure OpMstore)+        , (300, pure OpMstore8)       ])       -- storage manip       , (100, frequency [@@ -751,7 +764,7 @@         , (1, pure OpSstore)       ])       -- Jumping around-      , (20, frequency [+      , (50, frequency [             (1, pure OpJump)           , (10, pure OpJumpi)       ])@@ -770,16 +783,16 @@           (1, pure OpPop)         , (400, do             -- x <- arbitrary-            large <- chooseInt (0, 100)+            large <- chooseInt (0, 2000)             x <- if large == 0 then chooseBoundedIntegral (0::W256, (2::W256)^(256::W256)-1)                                else chooseBoundedIntegral (0, 10)-            pure $ OpPush (Lit (fromIntegral x)))+            pure $ OpPush (Lit x))         , (10, do             x <- chooseInt (1, 10)-            pure $ OpDup (fromIntegral x))+            pure $ OpDup (unsafeInto x))         , (10, do             x <- chooseInt (1, 10)-            pure $ OpSwap (fromIntegral x))+            pure $ OpSwap (unsafeInto x))       ])]       -- End states       -- , (1, frequency [@@ -795,12 +808,13 @@ randItem :: [a] -> IO a randItem = generate . Test.QuickCheck.elements -getOp :: VM -> Word8+getOp :: VM s -> Word8 getOp vm =   let pcpos  = vm ^. #state % #pc       code' = vm ^. #state % #code       xs = case code' of-        InitCode _ _ -> internalError "InitCode instead of RuntimeCode"+        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@@ -808,68 +822,128 @@  tests :: TestTree tests = testGroup "contract-quickcheck-run"-    [ testProperty "random-contract-concrete-call" $ \(contr :: OpContract) -> ioProperty $ do-        txDataRaw <- generate $ sized $ \n -> vectorOf (10*n+5) $ chooseInt (0,255)-        gaslimitExec <- generate $ chooseInt (40000, 0xffff)+    [ testProperty "random-contract-concrete-call" $ \(contr :: OpContract, GasLimitInt gasLimit, TxDataRaw txDataRaw) -> ioProperty $ do         let txData = BS.pack $ toEnum <$> txDataRaw         -- TODO: By removing external calls, we fuzz less         --       It should work also when we external calls. Removing for now.         contrFixed <- fixContractJumps $ removeExtcalls contr-        evmtoolResult <- getEVMToolRet contrFixed txData gaslimitExec-        hevmRun <- getHEVMRet contrFixed txData gaslimitExec-        (Just evmtoolTraceOutput) <- getTraceOutput evmtoolResult-        case hevmRun of-          (Right (expr, hevmTrace, hevmTraceResult)) -> do-            let-              concretize :: Expr a -> Expr Buf -> Expr a-              concretize a c = mapExpr go a-                where-                  go :: Expr a -> Expr a-                  go = \case-                             AbstractBuf "calldata" -> c-                             y -> y-              concretizedExpr = concretize expr (ConcreteBuf txData)-              simplConcExpr = Expr.simplify concretizedExpr-              getReturnVal :: Expr End -> Maybe ByteString-              getReturnVal (Success _ _ (ConcreteBuf bs) _) = Just bs-              getReturnVal _ = Nothing-              simplConcrExprRetval = getReturnVal simplConcExpr-            traceOK <- compareTraces hevmTrace (evmtoolTraceOutput.trace)-            -- putStrLn $ "HEVM trace   : " <> show hevmTrace-            -- putStrLn $ "evmtool trace: " <> show (evmtoolTraceOutput.toTrace)-            assertEqual "Traces and gas must match" traceOK True-            let resultOK = evmtoolTraceOutput.output.output == hevmTraceResult.out-            if resultOK then do-              putStrLn $ "HEVM & evmtool's outputs match: '" <> (bsToHex $ bssToBs evmtoolTraceOutput.output.output) <> "'"-              if isNothing simplConcrExprRetval || (fromJust simplConcrExprRetval) == (bssToBs hevmTraceResult.out)-                 then do-                   putStr "OK, symbolic interpretation -> concrete calldata -> Expr.simplify gives the same answer."-                   if isNothing simplConcrExprRetval then putStrLn ", but it was a Nothing, so not strong equivalence"-                                                     else putStrLn ""-                 else do-                   putStrLn $ "concretized expr           : " <> (show concretizedExpr)-                   putStrLn $ "simplified concretized expr: " <> (show simplConcExpr)-                   putStrLn $ "return value computed      : " <> (show simplConcrExprRetval)-                   putStrLn $ "evmtool's return value     : " <> (show hevmTraceResult.out)-                   assertEqual "Simplified, concretized expression must match evmtool's output." True False-            else do-              putStrLn $ "Name of trace file: " <> (getTraceFileName $ fromJust evmtoolResult)-              putStrLn $ "HEVM result  :" <> (show hevmTraceResult)-              T.putStrLn $ "HEVM result: " <> (formatBinary $ bssToBs hevmTraceResult.out)-              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-          Left (evmerr, hevmTrace) -> do-            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-        System.Directory.removeFile "txs.json"-        System.Directory.removeFile "alloc-out.json"-        System.Directory.removeFile "alloc.json"-        System.Directory.removeFile "result.json"-        System.Directory.removeFile "env.json"-        deleteTraceOutputFiles evmtoolResult+        checkTraceAndOutputs contrFixed gasLimit txData+      , testCase "calldata-wraparound" $ do+        let contract = OpContract $ concat+              [ [OpPush (Lit 0xff),OpPush (Lit 31),OpMstore8] -- value, offs+              , [OpPush (Lit 0x3),OpPush (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff),OpPush (Lit 0x0),OpCalldatacopy] -- size, offs, destOffs+              , [OpPush (Lit 0x20),OpPush (Lit 0),OpReturn] -- datasize, offs+              ]+        checkTraceAndOutputs contract 40000 (BS.pack [1, 2, 3, 4, 5])+      , testCase "calldata-wraparound2" $ do+        let contract = OpContract $ concat+              [ [OpPush (Lit 0xff),OpPush (Lit 0),OpMstore8] -- value, offs+              , [OpPush (Lit 0x10),OpPush (Lit 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff),OpPush (Lit 0x0),OpCalldatacopy] -- size, offs, destOffs+              , [OpPush (Lit 0x20),OpPush (Lit 0),OpReturn] -- datasize, offs+              ]+        checkTraceAndOutputs contract 40000 (BS.pack [1, 2, 3, 4, 5])+      , testCase "calldata-overwrite-with-0-if-oversized" $ do+        -- supposed to copy 1...6 and then 0s, overwriting the 0xff with 0+        let contract = OpContract $ concat+              [ [OpPush (Lit 0xff),OpPush (Lit 1),OpMstore8] -- value, offs+              , [OpPush (Lit 10),OpPush (Lit 0),OpPush (Lit 0), OpCalldatacopy] -- size, offs, destOffs+              , [OpPush (Lit 10),OpPush (Lit 0x0),OpReturn] -- datasize, offset+              ]+        checkTraceAndOutputs contract 40000 (BS.pack [1, 2, 3, 4, 5, 6])+      , testCase "calldata-overwrite-correct-size" $ do+        let contract = OpContract $ concat+              [ [OpPush (Lit 0xff),OpPush (Lit 8),OpMstore8] -- value, offs+              , [OpPush (Lit 10),OpPush (Lit 0),OpPush (Lit 0), OpCalldatacopy] -- size, offs, destOffs+              , [OpPush (Lit 10),OpPush (Lit 0x0),OpReturn] -- datasize, offset+              ]+        checkTraceAndOutputs contract 40000 (BS.pack [1, 2, 3, 4, 5, 6])+      , testCase "calldata-offset-copy" $ do+        let contract = OpContract $ concat+              [ [OpPush (Lit 0xff),OpPush (Lit 8),OpMstore8] -- value, offs+              , [OpPush (Lit 0xff),OpPush (Lit 1),OpMstore8] -- value, offs+              , [OpPush (Lit 10),OpPush (Lit 4),OpPush (Lit 0), OpCalldatacopy] -- size, offs, destOffs+              , [OpPush (Lit 10),OpPush (Lit 0x0),OpReturn] -- datasize, offset+              ]+        checkTraceAndOutputs contract 40000 (BS.pack [1, 2, 3, 4, 5, 6])     ]++checkTraceAndOutputs :: OpContract -> Int -> ByteString -> IO ()+checkTraceAndOutputs contract gasLimit txData = do+  evmtoolResult <- getEVMToolRet contract txData gasLimit+  hevmRun <- getHEVMRet contract txData gasLimit+  (Just evmtoolTraceOutput) <- getTraceOutput evmtoolResult+  case hevmRun of+    (Right (expr, hevmTrace, hevmTraceResult)) -> do+      let+        concretize :: Expr a -> Expr Buf -> Expr a+        concretize a c = mapExpr go a+          where+            go :: Expr a -> Expr a+            go = \case+                       AbstractBuf "calldata" -> c+                       y -> y+        concretizedExpr = concretize expr (ConcreteBuf txData)+        simplConcExpr = Expr.simplify concretizedExpr+        getReturnVal :: Expr End -> Maybe ByteString+        getReturnVal (Success _ _ (ConcreteBuf bs) _) = Just bs+        getReturnVal _ = Nothing+        simplConcrExprRetval = getReturnVal simplConcExpr+      traceOK <- compareTraces hevmTrace (evmtoolTraceOutput.trace)+      -- putStrLn $ "HEVM trace   : " <> show hevmTrace+      -- putStrLn $ "evmtool trace: " <> show (evmtoolTraceOutput.trace)+      assertEqual "Traces and gas must match" traceOK True+      let resultOK = evmtoolTraceOutput.output.output == hevmTraceResult.out+      if resultOK then do+        putStrLn $ "HEVM & evmtool's outputs match: '" <> (bsToHex $ bssToBs evmtoolTraceOutput.output.output) <> "'"+        if isNothing simplConcrExprRetval || (fromJust simplConcrExprRetval) == (bssToBs hevmTraceResult.out)+           then do+             putStr "OK, symbolic interpretation -> concrete calldata -> Expr.simplify gives the same answer."+             if isNothing 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)+             putStrLn $ "simplified concretized expr      : " <> (show simplConcExpr)+             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 (fromJust simplConcrExprRetval))+             assertEqual "Simplified, concretized expression must match evmtool's output." True False+      else do+        putStrLn $ "Name of trace file: " <> (getTraceFileName $ fromJust evmtoolResult)+        putStrLn $ "HEVM result  :" <> (show hevmTraceResult)+        T.putStrLn $ "HEVM result: " <> (formatBinary $ bssToBs hevmTraceResult.out)+        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+    Left (evmerr, hevmTrace) -> do+      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+  System.Directory.removeFile "txs.json"+  System.Directory.removeFile "alloc-out.json"+  System.Directory.removeFile "alloc.json"+  System.Directory.removeFile "result.json"+  System.Directory.removeFile "env.json"+  deleteTraceOutputFiles evmtoolResult++-- GasLimitInt+newtype GasLimitInt = GasLimitInt (Int)+  deriving (Show, Eq)++instance Arbitrary GasLimitInt where+  arbitrary = do+    let mkLimit = chooseInt (50000, 0xfffff)+    fmap GasLimitInt mkLimit++-- GenTxDataRaw+newtype TxDataRaw = TxDataRaw ([Int])+  deriving (Show, Eq)++instance Arbitrary TxDataRaw where+  arbitrary = do+    let+      txDataRaw = sized $ \n -> vectorOf (10*n+5) $ chooseInt (0,255)+    fmap TxDataRaw txDataRaw
test/EVM/Test/Utils.hs view
@@ -7,6 +7,7 @@ import Data.Text qualified as T import Data.Text.IO qualified as T import GHC.IO.Handle (hClose)+import GHC.Natural import Paths_hevm qualified as Paths import System.Directory import System.IO.Temp@@ -17,37 +18,27 @@ import EVM.Fetch (RpcInfo) import EVM.Solidity import EVM.Solvers-import EVM.TTY qualified as TTY import EVM.UnitTest+import Control.Monad.ST (RealWorld) -runSolidityTestCustom :: FilePath -> Text -> Maybe Integer -> Bool -> RpcInfo -> ProjectType -> IO Bool-runSolidityTestCustom testFile match maxIter ffiAllowed rpcinfo projectType = do+runSolidityTestCustom :: FilePath -> Text -> Maybe Natural -> Maybe Integer -> Bool -> RpcInfo -> ProjectType -> IO Bool+runSolidityTestCustom testFile match timeout maxIter ffiAllowed rpcinfo projectType = do   withSystemTempDirectory "dapp-test" $ \root -> do     compile projectType root testFile >>= \case       Left e -> error e       Right bo@(BuildOutput contracts _) -> do-        withSolvers Z3 1 Nothing $ \solvers -> do+        withSolvers Z3 1 timeout $ \solvers -> do           opts <- testOpts solvers root (Just bo) match maxIter ffiAllowed rpcinfo-          unitTest opts contracts Nothing+          unitTest opts contracts  runSolidityTest :: FilePath -> Text -> IO Bool-runSolidityTest testFile match = runSolidityTestCustom testFile match Nothing True Nothing Foundry--debugSolidityTest :: FilePath -> RpcInfo -> IO ()-debugSolidityTest testFile rpcinfo = do-  withSystemTempDirectory "dapp-test" $ \root -> do-    compile DappTools root testFile >>= \case-      Left e -> error e-      Right bo -> do-        withSolvers Z3 1 Nothing $ \solvers -> do-          opts <- testOpts solvers root (Just bo) ".*" Nothing True rpcinfo-          TTY.main opts root (Just bo)+runSolidityTest testFile match = runSolidityTestCustom testFile match Nothing Nothing True Nothing Foundry -testOpts :: SolverGroup -> FilePath -> Maybe BuildOutput -> Text -> Maybe Integer -> Bool -> RpcInfo -> IO UnitTestOptions+testOpts :: SolverGroup -> FilePath -> Maybe BuildOutput -> Text -> Maybe Integer -> Bool -> RpcInfo -> IO (UnitTestOptions RealWorld) testOpts solvers root buildOutput match maxIter allowFFI rpcinfo = do   let srcInfo = maybe emptyDapp (dappInfo root) buildOutput -  params <- getParametersFromEnvironmentVariables Nothing+  params <- paramsFromRpc rpcinfo    pure UnitTestOptions     { solvers = solvers@@ -57,13 +48,8 @@     , smtDebug = False     , smtTimeout = Nothing     , solver = Nothing-    , covMatch = Nothing     , verbose = Just 1     , match = match-    , maxDepth = Nothing-    , fuzzRuns = 100-    , replay = Nothing-    , vmModifier = id     , testParams = params     , dapp = srcInfo     , ffiAllowed = allowFFI
− test/contracts/fail/cheatCodes.sol
@@ -1,39 +0,0 @@-import "ds-test/test.sol";--interface Hevm {-    function warp(uint256) external;-    function roll(uint256) external;-    function load(address,bytes32) external returns (bytes32);-    function store(address,bytes32,bytes32) external;-    function sign(uint256,bytes32) external returns (uint8,bytes32,bytes32);-    function addr(uint256) external returns (address);-    function ffi(string[] calldata) external returns (bytes memory);-    function prank(address) external;-}--contract Payable {-    function hi() public payable {}-}--contract TestFailCheatCodes is DSTest {-    Hevm hevm = Hevm(HEVM_ADDRESS);--    function testBadFFI() public {-        string[] memory inputs = new string[](2);-        inputs[0] = "echo";-        inputs[1] = "acab";--        // should revert if --ffi hasn't been passed to hevm...-        hevm.ffi(inputs);-    }--    function test_prank_underflow() public {-        address from = address(0x1312);-        uint amt = 10;--        Payable target = new Payable();--        hevm.prank(from);-        target.hi{value : amt}();-    }-}
− test/contracts/fail/dsProveFail.sol
@@ -1,66 +0,0 @@-import "ds-test/test.sol";-import "tokens/erc20.sol";--contract SolidityTest is DSTest {-    ERC20 token;--    function setUp() public {-        token = new ERC20("TOKEN", "TKN", 18);-    }--    function prove_trivial() public {-        assert(false);-    }--    function prove_add(uint x, uint y) public {-        unchecked {-            assertTrue(x + y >= x);-        }-    }--    //function proveFail_shouldFail(address usr) public {-        //usr.call("");-    //}--    function prove_smtTimeout(uint x, uint y, uint z) public {-        if ((x * y / z) * (x / y) / (x * y) == (x * x * x * y * z / x * z * y)) {-            assertTrue(false);-        } else {-            assertTrue(true);-        }-    }--    function prove_multi(uint x) public {-        if (x == 3) {-            assertTrue(false);-        } else if (x == 9) {-            assertTrue(false);-        } else if (x == 1023423194871904872390487213) {-            assertTrue(false);-        } else {-            assertTrue(true);-        }-    }--    function prove_mul(uint136 x, uint128 y) public {-        x * y;-    }--    function prove_distributivity(uint120 x, uint120 y, uint120 z) public {-        assertEq(x + (y * z), (x + y) * (x + z));-    }--    function prove_transfer(uint supply, address usr, uint amt) public {-        token.mint(address(this), supply);--        uint prebal = token.balanceOf(usr);-        token.transfer(usr, amt);-        uint postbal = token.balanceOf(usr);--        uint expected = usr == address(this)-                        ? 0    // self transfer is a noop-                        : amt; // otherwise `amt` has been transfered to `usr`-        assertEq(expected, postbal - prebal);-    }-}-
− test/contracts/fail/invariantFail.sol
@@ -1,50 +0,0 @@-import "ds-test/test.sol";--contract Testdapp {-    uint public x;-    function f() public {-        x++;-    }-    function g(uint y) public {-        if (y % 2 == 0) x*=2;-    }-}---contract TestdappTest is DSTest {-    Testdapp testdapp;--    function setUp() public {-        testdapp = new Testdapp();-    }--    function invariantFirst() public {-        assertLt(testdapp.x(), 100);-    }-}--contract InvariantCount is DSTest {-    BrokenAtStart count;-    address[] targetContracts_;--    function targetContracts() public returns (address[] memory) {-      return targetContracts_;-    }-    function setUp() public {-        count = new BrokenAtStart();-        targetContracts_.push(address(count));-    }--    // this can only fail if we call the invariant method before calling any other method in the target contracts-    function invariantCount() public {-        assertGt(count.count(), 0);-    }-}--contract BrokenAtStart {-    uint public count;--    function inc() public {-        count++;-    }-}
− test/contracts/fail/trivial.sol
@@ -1,9 +0,0 @@-import {DSTest} from "ds-test/test.sol";--// should run and pass-contract Trivial is DSTest {-    function testFalse() public {-        assertTrue(false);-    }-}-
− test/contracts/lib/erc20.sol
@@ -1,201 +0,0 @@-// modified from solmate erc20-// https://github.com/transmissions11/solmate/blob/c2594bf4635ad773a8f4763e20b7e79582e41535/src/tokens/ERC20.sol--/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.-/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)-/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)-/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.-contract ERC20 {--    // --- events -----    event Transfer(address indexed from, address indexed to, uint256 amount);-    event Approval(address indexed owner, address indexed spender, uint256 amount);--    // --- metadata -----    string public name;-    string public symbol;-    uint8 public immutable decimals;--    // --- erc20 data -----    uint256 public totalSupply;-    mapping(address => uint256) public balanceOf;-    mapping(address => mapping(address => uint256)) public allowance;--    // --- eip2612 data -----    uint256 internal immutable INITIAL_CHAIN_ID;-    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;-    mapping(address => uint256) public nonces;--    // --- admin -----    address public owner;-    modifier auth { require(owner == msg.sender, "not-authorized"); _; }--    // --- init -----    constructor(-        string memory _name,-        string memory _symbol,-        uint8 _decimals-    ) {-        name = _name;-        symbol = _symbol;-        decimals = _decimals;--        INITIAL_CHAIN_ID = block.chainid;-        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();--        owner = msg.sender;-    }--    // --- erc20 logic -----    function approve(address spender, uint256 amount) public returns (bool) {-        allowance[msg.sender][spender] = amount;--        emit Approval(msg.sender, spender, amount);--        return true;-    }--    function transfer(address to, uint256 amount) public returns (bool) {-        balanceOf[msg.sender] -= amount;--        // Cannot overflow because the sum of all user-        // balances can't exceed the max uint256 value.-        unchecked {-            balanceOf[to] += amount;-        }--        emit Transfer(msg.sender, to, amount);--        return true;-    }--    function transferFrom(-        address from,-        address to,-        uint256 amount-    ) public returns (bool) {-        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.--        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;--        balanceOf[from] -= amount;--        // Cannot overflow because the sum of all user-        // balances can't exceed the max uint256 value.-        unchecked {-            balanceOf[to] += amount;-        }--        emit Transfer(from, to, amount);--        return true;-    }--    // --- mint / burn logic -----    function mint(address to, uint256 amount) public auth {-        _mint(to, amount);-    }--    function burn(address from, uint256 amount) public auth {-        _burn(from, amount);-    }--    // --- eip2612 logic -----    function permit(-        address owner,-        address spender,-        uint256 value,-        uint256 deadline,-        uint8 v,-        bytes32 r,-        bytes32 s-    ) public {-        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");--        // Unchecked because the only math done is incrementing-        // the owner's nonce which cannot realistically overflow.-        unchecked {-            address recoveredAddress = ecrecover(-                keccak256(-                    abi.encodePacked(-                        "\x19\x01",-                        DOMAIN_SEPARATOR(),-                        keccak256(-                            abi.encode(-                                keccak256(-                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"-                                ),-                                owner,-                                spender,-                                value,-                                nonces[owner]++,-                                deadline-                            )-                        )-                    )-                ),-                v,-                r,-                s-            );--            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");--            allowance[recoveredAddress][spender] = value;-        }--        emit Approval(owner, spender, value);-    }--    function DOMAIN_SEPARATOR() public view returns (bytes32) {-        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();-    }--    function computeDomainSeparator() internal view returns (bytes32) {-        return-            keccak256(-                abi.encode(-                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),-                    keccak256(bytes(name)),-                    keccak256("1"),-                    block.chainid,-                    address(this)-                )-            );-    }--    // --- internal mint / burn logic -----    function _mint(address to, uint256 amount) internal {-        totalSupply += amount;--        // Cannot overflow because the sum of all user-        // balances can't exceed the max uint256 value.-        unchecked {-            balanceOf[to] += amount;-        }--        emit Transfer(address(0), to, amount);-    }--    function _burn(address from, uint256 amount) internal {-        balanceOf[from] -= amount;--        // Cannot underflow because a user's balance-        // will never be larger than the total supply.-        unchecked {-            totalSupply -= amount;-        }--        emit Transfer(from, address(0), amount);-    }-}
− test/contracts/lib/test.sol
@@ -1,469 +0,0 @@-// SPDX-License-Identifier: GPL-3.0-or-later--// This program is free software: you can redistribute it and/or modify-// it under the terms of the GNU General Public License as published by-// the Free Software Foundation, either version 3 of the License, or-// (at your option) any later version.--// This program is distributed in the hope that it will be useful,-// but WITHOUT ANY WARRANTY; without even the implied warranty of-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-// GNU General Public License for more details.--// You should have received a copy of the GNU General Public License-// along with this program.  If not, see <http://www.gnu.org/licenses/>.--pragma solidity >=0.5.0;--contract DSTest {-    event log                    (string);-    event logs                   (bytes);--    event log_address            (address);-    event log_bytes32            (bytes32);-    event log_int                (int);-    event log_uint               (uint);-    event log_bytes              (bytes);-    event log_string             (string);--    event log_named_address      (string key, address val);-    event log_named_bytes32      (string key, bytes32 val);-    event log_named_decimal_int  (string key, int val, uint decimals);-    event log_named_decimal_uint (string key, uint val, uint decimals);-    event log_named_int          (string key, int val);-    event log_named_uint         (string key, uint val);-    event log_named_bytes        (string key, bytes val);-    event log_named_string       (string key, string val);--    bool public IS_TEST = true;-    bool private _failed;--    address constant HEVM_ADDRESS =-        address(bytes20(uint160(uint256(keccak256('hevm cheat code')))));--    modifier mayRevert() { _; }-    modifier testopts(string memory) { _; }--    function failed() public returns (bool) {-        if (_failed) {-            return _failed;-        } else {-            bool globalFailed = false;-            if (hasHEVMContext()) {-                (, bytes memory retdata) = HEVM_ADDRESS.call(-                    abi.encodePacked(-                        bytes4(keccak256("load(address,bytes32)")),-                        abi.encode(HEVM_ADDRESS, bytes32("failed"))-                    )-                );-                globalFailed = abi.decode(retdata, (bool));-            }-            return globalFailed;-        }-    } --    function fail() internal {-        if (hasHEVMContext()) {-            (bool status, ) = HEVM_ADDRESS.call(-                abi.encodePacked(-                    bytes4(keccak256("store(address,bytes32,bytes32)")),-                    abi.encode(HEVM_ADDRESS, bytes32("failed"), bytes32(uint256(0x01)))-                )-            );-            status; // Silence compiler warnings-        }-        _failed = true;-    }--    function hasHEVMContext() internal view returns (bool) {-        uint256 hevmCodeSize = 0;-        assembly {-            hevmCodeSize := extcodesize(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)-        }-        return hevmCodeSize > 0;-    }--    modifier logs_gas() {-        uint startGas = gasleft();-        _;-        uint endGas = gasleft();-        emit log_named_uint("gas", startGas - endGas);-    }--    function assertTrue(bool condition) internal {-        if (!condition) {-            emit log("Error: Assertion Failed");-            fail();-        }-    }--    function assertTrue(bool condition, string memory err) internal {-        if (!condition) {-            emit log_named_string("Error", err);-            assertTrue(condition);-        }-    }--    function assertEq(address a, address b) internal {-        if (a != b) {-            emit log("Error: a == b not satisfied [address]");-            emit log_named_address("  Expected", b);-            emit log_named_address("    Actual", a);-            fail();-        }-    }-    function assertEq(address a, address b, string memory err) internal {-        if (a != b) {-            emit log_named_string ("Error", err);-            assertEq(a, b);-        }-    }--    function assertEq(bytes32 a, bytes32 b) internal {-        if (a != b) {-            emit log("Error: a == b not satisfied [bytes32]");-            emit log_named_bytes32("  Expected", b);-            emit log_named_bytes32("    Actual", a);-            fail();-        }-    }-    function assertEq(bytes32 a, bytes32 b, string memory err) internal {-        if (a != b) {-            emit log_named_string ("Error", err);-            assertEq(a, b);-        }-    }-    function assertEq32(bytes32 a, bytes32 b) internal {-        assertEq(a, b);-    }-    function assertEq32(bytes32 a, bytes32 b, string memory err) internal {-        assertEq(a, b, err);-    }--    function assertEq(int a, int b) internal {-        if (a != b) {-            emit log("Error: a == b not satisfied [int]");-            emit log_named_int("  Expected", b);-            emit log_named_int("    Actual", a);-            fail();-        }-    }-    function assertEq(int a, int b, string memory err) internal {-        if (a != b) {-            emit log_named_string("Error", err);-            assertEq(a, b);-        }-    }-    function assertEq(uint a, uint b) internal {-        if (a != b) {-            emit log("Error: a == b not satisfied [uint]");-            emit log_named_uint("  Expected", b);-            emit log_named_uint("    Actual", a);-            fail();-        }-    }-    function assertEq(uint a, uint b, string memory err) internal {-        if (a != b) {-            emit log_named_string("Error", err);-            assertEq(a, b);-        }-    }-    function assertEqDecimal(int a, int b, uint decimals) internal {-        if (a != b) {-            emit log("Error: a == b not satisfied [decimal int]");-            emit log_named_decimal_int("  Expected", b, decimals);-            emit log_named_decimal_int("    Actual", a, decimals);-            fail();-        }-    }-    function assertEqDecimal(int a, int b, uint decimals, string memory err) internal {-        if (a != b) {-            emit log_named_string("Error", err);-            assertEqDecimal(a, b, decimals);-        }-    }-    function assertEqDecimal(uint a, uint b, uint decimals) internal {-        if (a != b) {-            emit log("Error: a == b not satisfied [decimal uint]");-            emit log_named_decimal_uint("  Expected", b, decimals);-            emit log_named_decimal_uint("    Actual", a, decimals);-            fail();-        }-    }-    function assertEqDecimal(uint a, uint b, uint decimals, string memory err) internal {-        if (a != b) {-            emit log_named_string("Error", err);-            assertEqDecimal(a, b, decimals);-        }-    }--    function assertGt(uint a, uint b) internal {-        if (a <= b) {-            emit log("Error: a > b not satisfied [uint]");-            emit log_named_uint("  Value a", a);-            emit log_named_uint("  Value b", b);-            fail();-        }-    }-    function assertGt(uint a, uint b, string memory err) internal {-        if (a <= b) {-            emit log_named_string("Error", err);-            assertGt(a, b);-        }-    }-    function assertGt(int a, int b) internal {-        if (a <= b) {-            emit log("Error: a > b not satisfied [int]");-            emit log_named_int("  Value a", a);-            emit log_named_int("  Value b", b);-            fail();-        }-    }-    function assertGt(int a, int b, string memory err) internal {-        if (a <= b) {-            emit log_named_string("Error", err);-            assertGt(a, b);-        }-    }-    function assertGtDecimal(int a, int b, uint decimals) internal {-        if (a <= b) {-            emit log("Error: a > b not satisfied [decimal int]");-            emit log_named_decimal_int("  Value a", a, decimals);-            emit log_named_decimal_int("  Value b", b, decimals);-            fail();-        }-    }-    function assertGtDecimal(int a, int b, uint decimals, string memory err) internal {-        if (a <= b) {-            emit log_named_string("Error", err);-            assertGtDecimal(a, b, decimals);-        }-    }-    function assertGtDecimal(uint a, uint b, uint decimals) internal {-        if (a <= b) {-            emit log("Error: a > b not satisfied [decimal uint]");-            emit log_named_decimal_uint("  Value a", a, decimals);-            emit log_named_decimal_uint("  Value b", b, decimals);-            fail();-        }-    }-    function assertGtDecimal(uint a, uint b, uint decimals, string memory err) internal {-        if (a <= b) {-            emit log_named_string("Error", err);-            assertGtDecimal(a, b, decimals);-        }-    }--    function assertGe(uint a, uint b) internal {-        if (a < b) {-            emit log("Error: a >= b not satisfied [uint]");-            emit log_named_uint("  Value a", a);-            emit log_named_uint("  Value b", b);-            fail();-        }-    }-    function assertGe(uint a, uint b, string memory err) internal {-        if (a < b) {-            emit log_named_string("Error", err);-            assertGe(a, b);-        }-    }-    function assertGe(int a, int b) internal {-        if (a < b) {-            emit log("Error: a >= b not satisfied [int]");-            emit log_named_int("  Value a", a);-            emit log_named_int("  Value b", b);-            fail();-        }-    }-    function assertGe(int a, int b, string memory err) internal {-        if (a < b) {-            emit log_named_string("Error", err);-            assertGe(a, b);-        }-    }-    function assertGeDecimal(int a, int b, uint decimals) internal {-        if (a < b) {-            emit log("Error: a >= b not satisfied [decimal int]");-            emit log_named_decimal_int("  Value a", a, decimals);-            emit log_named_decimal_int("  Value b", b, decimals);-            fail();-        }-    }-    function assertGeDecimal(int a, int b, uint decimals, string memory err) internal {-        if (a < b) {-            emit log_named_string("Error", err);-            assertGeDecimal(a, b, decimals);-        }-    }-    function assertGeDecimal(uint a, uint b, uint decimals) internal {-        if (a < b) {-            emit log("Error: a >= b not satisfied [decimal uint]");-            emit log_named_decimal_uint("  Value a", a, decimals);-            emit log_named_decimal_uint("  Value b", b, decimals);-            fail();-        }-    }-    function assertGeDecimal(uint a, uint b, uint decimals, string memory err) internal {-        if (a < b) {-            emit log_named_string("Error", err);-            assertGeDecimal(a, b, decimals);-        }-    }--    function assertLt(uint a, uint b) internal {-        if (a >= b) {-            emit log("Error: a < b not satisfied [uint]");-            emit log_named_uint("  Value a", a);-            emit log_named_uint("  Value b", b);-            fail();-        }-    }-    function assertLt(uint a, uint b, string memory err) internal {-        if (a >= b) {-            emit log_named_string("Error", err);-            assertLt(a, b);-        }-    }-    function assertLt(int a, int b) internal {-        if (a >= b) {-            emit log("Error: a < b not satisfied [int]");-            emit log_named_int("  Value a", a);-            emit log_named_int("  Value b", b);-            fail();-        }-    }-    function assertLt(int a, int b, string memory err) internal {-        if (a >= b) {-            emit log_named_string("Error", err);-            assertLt(a, b);-        }-    }-    function assertLtDecimal(int a, int b, uint decimals) internal {-        if (a >= b) {-            emit log("Error: a < b not satisfied [decimal int]");-            emit log_named_decimal_int("  Value a", a, decimals);-            emit log_named_decimal_int("  Value b", b, decimals);-            fail();-        }-    }-    function assertLtDecimal(int a, int b, uint decimals, string memory err) internal {-        if (a >= b) {-            emit log_named_string("Error", err);-            assertLtDecimal(a, b, decimals);-        }-    }-    function assertLtDecimal(uint a, uint b, uint decimals) internal {-        if (a >= b) {-            emit log("Error: a < b not satisfied [decimal uint]");-            emit log_named_decimal_uint("  Value a", a, decimals);-            emit log_named_decimal_uint("  Value b", b, decimals);-            fail();-        }-    }-    function assertLtDecimal(uint a, uint b, uint decimals, string memory err) internal {-        if (a >= b) {-            emit log_named_string("Error", err);-            assertLtDecimal(a, b, decimals);-        }-    }--    function assertLe(uint a, uint b) internal {-        if (a > b) {-            emit log("Error: a <= b not satisfied [uint]");-            emit log_named_uint("  Value a", a);-            emit log_named_uint("  Value b", b);-            fail();-        }-    }-    function assertLe(uint a, uint b, string memory err) internal {-        if (a > b) {-            emit log_named_string("Error", err);-            assertLe(a, b);-        }-    }-    function assertLe(int a, int b) internal {-        if (a > b) {-            emit log("Error: a <= b not satisfied [int]");-            emit log_named_int("  Value a", a);-            emit log_named_int("  Value b", b);-            fail();-        }-    }-    function assertLe(int a, int b, string memory err) internal {-        if (a > b) {-            emit log_named_string("Error", err);-            assertLe(a, b);-        }-    }-    function assertLeDecimal(int a, int b, uint decimals) internal {-        if (a > b) {-            emit log("Error: a <= b not satisfied [decimal int]");-            emit log_named_decimal_int("  Value a", a, decimals);-            emit log_named_decimal_int("  Value b", b, decimals);-            fail();-        }-    }-    function assertLeDecimal(int a, int b, uint decimals, string memory err) internal {-        if (a > b) {-            emit log_named_string("Error", err);-            assertLeDecimal(a, b, decimals);-        }-    }-    function assertLeDecimal(uint a, uint b, uint decimals) internal {-        if (a > b) {-            emit log("Error: a <= b not satisfied [decimal uint]");-            emit log_named_decimal_uint("  Value a", a, decimals);-            emit log_named_decimal_uint("  Value b", b, decimals);-            fail();-        }-    }-    function assertLeDecimal(uint a, uint b, uint decimals, string memory err) internal {-        if (a > b) {-            emit log_named_string("Error", err);-            assertGeDecimal(a, b, decimals);-        }-    }--    function assertEq(string memory a, string memory b) internal {-        if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) {-            emit log("Error: a == b not satisfied [string]");-            emit log_named_string("  Value a", a);-            emit log_named_string("  Value b", b);-            fail();-        }-    }-    function assertEq(string memory a, string memory b, string memory err) internal {-        if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) {-            emit log_named_string("Error", err);-            assertEq(a, b);-        }-    }--    function checkEq0(bytes memory a, bytes memory b) internal pure returns (bool ok) {-        ok = true;-        if (a.length == b.length) {-            for (uint i = 0; i < a.length; i++) {-                if (a[i] != b[i]) {-                    ok = false;-                }-            }-        } else {-            ok = false;-        }-    }-    function assertEq0(bytes memory a, bytes memory b) internal {-        if (!checkEq0(a, b)) {-            emit log("Error: a == b not satisfied [bytes]");-            emit log_named_bytes("  Expected", a);-            emit log_named_bytes("    Actual", b);-            fail();-        }-    }-    function assertEq0(bytes memory a, bytes memory b, string memory err) internal {-        if (!checkEq0(a, b)) {-            emit log_named_string("Error", err);-            assertEq0(a, b);-        }-    }-}
− test/contracts/pass/abstract.sol
@@ -1,15 +0,0 @@-import {DSTest} from "ds-test/test.sol";--// should not be run (no code)-abstract contract MyTest is DSTest {-    function testAbstract() public {-        assertTrue(true);-    }-}--// should run and pass-contract TestMy is MyTest {-    function testTrue() public {-        assertTrue(true);-    }-}
− test/contracts/pass/cheatCodes.sol
@@ -1,125 +0,0 @@-pragma experimental ABIEncoderV2;--import "ds-test/test.sol";--interface Hevm {-    function warp(uint256) external;-    function roll(uint256) external;-    function load(address,bytes32) external returns (bytes32);-    function store(address,bytes32,bytes32) external;-    function sign(uint256,bytes32) external returns (uint8,bytes32,bytes32);-    function addr(uint256) external returns (address);-    function ffi(string[] calldata) external returns (bytes memory);-    function prank(address) external;-}--contract HasStorage {-    uint slot0 = 10;-}--contract Prankster {-    function prankme() public returns (address) {-        return msg.sender;-    }-}--contract Payable {-    function hi() public payable {}-}--contract CheatCodes is DSTest {-    address store = address(new HasStorage());-    Hevm hevm = Hevm(HEVM_ADDRESS);--    function test_warp_concrete(uint128 jump) public {-        uint pre = block.timestamp;-        hevm.warp(block.timestamp + jump);-        assertEq(block.timestamp, pre + jump);-    }--    function prove_warp_symbolic(uint128 jump) public {-        test_warp_concrete(jump);-    }--    function test_roll_concrete(uint64 jump) public {-        uint pre = block.number;-        hevm.roll(block.number + jump);-        assertEq(block.number, pre + jump);-    }--    function test_store_load_concrete(uint x) public {-        uint ten = uint(hevm.load(store, bytes32(0)));-        assertEq(ten, 10);--        hevm.store(store, bytes32(0), bytes32(x));-        uint val = uint(hevm.load(store, bytes32(0)));-        assertEq(val, x);-    }--    function prove_store_load_symbolic(uint x) public {-        test_store_load_concrete(x);-    }--    function test_sign_addr_digest(uint sk, bytes32 digest) public {-        if (sk == 0) return; // invalid key--        (uint8 v, bytes32 r, bytes32 s) = hevm.sign(sk, digest);-        address expected = hevm.addr(sk);-        address actual = ecrecover(digest, v, r, s);--        assertEq(actual, expected);-    }--    function test_sign_addr_message(uint sk, bytes memory message) public {-        test_sign_addr_digest(sk, keccak256(message));-    }--    function testFail_sign_addr(uint sk, bytes32 digest) public {-        uint badKey = sk + 1;--        (uint8 v, bytes32 r, bytes32 s) = hevm.sign(badKey, digest);-        address expected = hevm.addr(sk);-        address actual = ecrecover(digest, v, r, s);--        assertEq(actual, expected);-    }--    function testFail_addr_zero_sk() public {-        hevm.addr(0);-    }--    function testFFI() public {-        string[] memory inputs = new string[](3);-        inputs[0] = "echo";-        inputs[1] = "-n";-        inputs[2] = "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000046163616200000000000000000000000000000000000000000000000000000000";--        (string memory output) = abi.decode(hevm.ffi(inputs), (string));-        assertEq(output, "acab");-    }--    function test_prank() public {-        Prankster prankster = new Prankster();-        assertEq(prankster.prankme(), address(this));-        hevm.prank(address(0xdeadbeef));-        assertEq(prankster.prankme(), address(0xdeadbeef));-        assertEq(prankster.prankme(), address(this));-    }--    function test_prank_val() public {-        address from = address(0x1312);-        uint amt = 10;--        Payable target = new Payable();-        from.call{value: amt}("");--        uint preBal = from.balance;--        hevm.prank(from);-        target.hi{value : amt}();--        uint postBal = from.balance;--        assertEq(preBal - postBal, amt);-    }-}
− test/contracts/pass/constantinople.sol
@@ -1,331 +0,0 @@-import "ds-test/test.sol";--contract DeadCode{-    function dummy() external returns (uint256) {}-}--contract ConstantinopleTests is DSTest {-    DeadCode notmuch;-    function setUp() public {-      notmuch = new DeadCode();-    }--    // this 5 byte-long initcode simply returns nothing-    // PUSH1  00     PUSH1  00     RETURN-    // 60     00     60     00     f3-    bytes32 zerocode        = 0x60006000f3000000000000000000000000000000000000000000000000000000;-    // this 13 byte-long initcode simply returns 0xdeadbeef:-    // PUSH4  de     ad     be     ef     PUSH1  00     MSTORE PUSH1  32     PUSH1  00     RETURN-    // 63     de     ad     be     ef     60     00     52     60     20     60     00     f3-    bytes32 deadcode        = 0x63deadbeef60005260206000f300000000000000000000000000000000000000;-    // this 25 byte-long initcode returns deadcode (but without the padding)-    // PUSH1  0d     PUSH1  0c     PUSH1  00     CODECO PUSH1  0d     PUSH1  00     RETURN deadcode-    // 60     0d     60     0c     60     00     39     60     0d     60     00     f3-    bytes32 deploysdeadcode = 0x600d600c600039600d6000f363deadbeef60005260206000f300000000000000;--    // EXTCODEHASH of non-existent account is 0-    function test_extcodehash_1() public {-        uint256 h;-        assembly {-            h := extcodehash(0x10)-        }-        assertEq(h, 0);-    }-    // EXTCODEHASH of account with no code is keccak256("")-    function test_extcodehash_2() public {-        address a;-        uint256 h;-        assembly {-            let top := mload(0x40)-            mstore(top, sload(zerocode.slot))-            a := create(0, top, 5)-            h := extcodehash(a)-        }-        assertEq(h, 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470);-    }-    // EXTCODEHASH of account with code 0xdeadbeef is keccak256(0xdeadbeef)-    function test_extcodehash_3() public {-        address a;-        uint256 h;--        assembly {-          let top := mload(0x40)-          mstore(top, sload(deadcode.slot))-          a := create(0, top, 13)-          h := extcodehash(a)-        }--        uint256 expected_h;-        assembly {-            let top := mload(0x40)-            mstore(top, 0xdeadbeef)-            expected_h := keccak256(top, 32)-        }-        assertEq(h, expected_h);-    }-    // EXTCODEHASH of the notmuch contract should be same as-    // doing EXTCODECOPY and then keccak256-    function test_extcodehash_4() public {-        address a = address(notmuch);-        uint256 h;--        assembly {-            h := extcodehash(a)-        }--        uint256 expected_h;-        assembly {-            let top := mload(0x40)-            let size := extcodesize(a)-            extcodecopy(a, top, 0, size)-            expected_h := keccak256(top, size)-        }-        assertEq(h, expected_h);-    }--    // address of account created by CREATE2 is-    // keccak256(0xff + address + salt + keccak256(init_code))[12:]-    function test_create2_1() public {-        address a;-        uint256 salt = 0xfacefeed;-        assembly {-          let top := mload(0x40)-          mstore(top, sload(deadcode.slot))-          a := create2(0, top, 13, salt)-        }--        address expected_a;--        assembly {-          let top := mload(0x40)-          mstore(top, sload(deadcode.slot))-          let inithash := keccak256(top, 13)-          mstore(sub(top, 11), address())-          mstore8(top, 0xff)-          mstore(add(top, 21), salt)-          mstore(add(top, 53), inithash)-          expected_a := and(keccak256(top, 85), 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff)-        }--        assertEq(a, expected_a);-    }-    // calling a CREATE2 contract works as expected-    function test_create2_2() public {-        address a;-        uint256 salt = 0xfacefeed;-        assembly {-          let top := mload(0x40)-          mstore(top, sload(deploysdeadcode.slot))-          a := create2(0, top, 25, salt)-        }--        assertEq(DeadCode(a).dummy(), 0xdeadbeef);-    }-    // TODO: test some SELFDESTRUCT properties of CREATE2-    // TODO: test EXTCODEHASH on self-destructed contract-    // TODO: test EXTCODEHASH on precompiled contracts--    // SHL, SHR and SAR tests taken from-    // https://github.com/ethereum/EIPs/blob/fde32dfd6b24bac7bfabf6c1ebe3f5a603d5ff4c/EIPS/eip-145.md-    function test_shl() public {-        uint z;--        assembly {-            z := shl(0x00, 0x0000000000000000000000000000000000000000000000000000000000000001)-        }-        assertEq(z, 0x0000000000000000000000000000000000000000000000000000000000000001);--        assembly {-            z := shl(0x01, 0x0000000000000000000000000000000000000000000000000000000000000001)-        }-        assertEq(z, 0x0000000000000000000000000000000000000000000000000000000000000002);--        assembly {-            z := shl(0xff, 0x0000000000000000000000000000000000000000000000000000000000000001)-        }-        assertEq(z, 0x8000000000000000000000000000000000000000000000000000000000000000);--        assembly {-            z := shl(0x0100, 0x0000000000000000000000000000000000000000000000000000000000000001)-        }-        assertEq(z, 0x0000000000000000000000000000000000000000000000000000000000000000);--        assembly {-            z := shl(0x0101, 0x0000000000000000000000000000000000000000000000000000000000000001)-        }-        assertEq(z, 0x0000000000000000000000000000000000000000000000000000000000000000);--        assembly {-            z := shl(0x00, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)-        }-        assertEq(z, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);--        assembly {-            z := shl(0x01, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)-        }-        assertEq(z, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe);--        assembly {-            z := shl(0xff, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)-        }-        assertEq(z, 0x8000000000000000000000000000000000000000000000000000000000000000);--        assembly {-            z := shl(0x0100, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)-        }-        assertEq(z, 0x0000000000000000000000000000000000000000000000000000000000000000);--        assembly {-            z := shl(0x01, 0x0000000000000000000000000000000000000000000000000000000000000000)-        }-        assertEq(z, 0x0000000000000000000000000000000000000000000000000000000000000000);--        assembly {-            z := shl(0x01, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)-        }-        assertEq(z, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe);-    }--    function test_shr() public {-        uint z;--        assembly {-            z := shr(0x00, 0x0000000000000000000000000000000000000000000000000000000000000001)-        }-        assertEq(z, 0x0000000000000000000000000000000000000000000000000000000000000001);--        assembly {-            z := shr(0x01, 0x0000000000000000000000000000000000000000000000000000000000000001)-        }-        assertEq(z, 0x0000000000000000000000000000000000000000000000000000000000000000);--        assembly {-            z := shr(0x01, 0x8000000000000000000000000000000000000000000000000000000000000000)-        }-        assertEq(z, 0x4000000000000000000000000000000000000000000000000000000000000000);--        assembly {-            z := shr(0xff, 0x8000000000000000000000000000000000000000000000000000000000000000)-        }-        assertEq(z, 0x0000000000000000000000000000000000000000000000000000000000000001);--        assembly {-            z := shr(0x0100, 0x8000000000000000000000000000000000000000000000000000000000000000)-        }-        assertEq(z, 0x0000000000000000000000000000000000000000000000000000000000000000);--        assembly {-            z := shr(0x0101, 0x8000000000000000000000000000000000000000000000000000000000000000)-        }-        assertEq(z, 0x0000000000000000000000000000000000000000000000000000000000000000);--        assembly {-            z := shr(0x00, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)-        }-        assertEq(z, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);--        assembly {-            z := shr(0x01, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)-        }-        assertEq(z, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);--        assembly {-            z := shr(0xff, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)-        }-        assertEq(z, 0x0000000000000000000000000000000000000000000000000000000000000001);--        assembly {-            z := shr(0x0100, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)-        }-        assertEq(z, 0x0000000000000000000000000000000000000000000000000000000000000000);--        assembly {-            z := shr(0x01, 0x0000000000000000000000000000000000000000000000000000000000000000)-        }-        assertEq(z, 0x0000000000000000000000000000000000000000000000000000000000000000);-    }--    function test_sar() public {-        uint z;--        assembly {-            z := sar(0x00, 0x0000000000000000000000000000000000000000000000000000000000000001)-        }-        assertEq(bytes32(z), bytes32(0x0000000000000000000000000000000000000000000000000000000000000001));--        assembly {-            z := sar(0x01, 0x0000000000000000000000000000000000000000000000000000000000000001)-        }-        assertEq(bytes32(z), bytes32(0x0000000000000000000000000000000000000000000000000000000000000000));--        assembly {-            z := sar(0x01, 0x8000000000000000000000000000000000000000000000000000000000000000)-        }-        assertEq(bytes32(z), bytes32(0xc000000000000000000000000000000000000000000000000000000000000000));--        assembly {-            z := sar(0xff, 0x8000000000000000000000000000000000000000000000000000000000000000)-        }-        assertEq(bytes32(z), bytes32(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff));--        assembly {-            z := sar(0x0100, 0x8000000000000000000000000000000000000000000000000000000000000000)-        }-        assertEq(bytes32(z), bytes32(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff));--        assembly {-            z := sar(0x0101, 0x8000000000000000000000000000000000000000000000000000000000000000)-        }-        assertEq(bytes32(z), bytes32(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff));--        assembly {-            z := sar(0x00, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)-        }-        assertEq(bytes32(z), bytes32(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff));--        assembly {-            z := sar(0x01, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)-        }-        assertEq(bytes32(z), bytes32(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff));--        assembly {-            z := sar(0xff, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)-        }-        assertEq(bytes32(z), bytes32(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff));--        assembly {-            z := sar(0x0100, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)-        }-        assertEq(bytes32(z), bytes32(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff));--        assembly {-            z := sar(0x01, 0x0000000000000000000000000000000000000000000000000000000000000000)-        }-        assertEq(bytes32(z), bytes32(0x0000000000000000000000000000000000000000000000000000000000000000));--        assembly {-            z := sar(0xfe, 0x4000000000000000000000000000000000000000000000000000000000000000)-        }-        assertEq(bytes32(z), bytes32(0x0000000000000000000000000000000000000000000000000000000000000001));--        assembly {-            z := sar(0xf8, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)-        }-        assertEq(bytes32(z), bytes32(0x000000000000000000000000000000000000000000000000000000000000007f));--        assembly {-            z := sar(0xfe, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)-        }-        assertEq(bytes32(z), bytes32(0x0000000000000000000000000000000000000000000000000000000000000001));--        assembly {-            z := sar(0xff, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)-        }-        assertEq(bytes32(z), bytes32(0x0000000000000000000000000000000000000000000000000000000000000000));--        assembly {-            z := sar(0x0100, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)-        }-        assertEq(bytes32(z), bytes32(0x0000000000000000000000000000000000000000000000000000000000000000));-    }-}
− test/contracts/pass/dsProvePass.sol
@@ -1,83 +0,0 @@-import "ds-test/test.sol";-import "tokens/erc20.sol";--contract ConstructorArg {-    address immutable public a;-    constructor(address _a) public {-        a = _a;-    }-}--contract SolidityTest is DSTest {-    ERC20 token;--    function setUp() public {-        token = new ERC20("Token", "TKN", 18);-    }--    function prove_trivial() public {-        assertTrue(true);-    }--    function prove_easy(uint v) public {-        if (v != 100) return;-        assertEq(v, 100);-    }--    function prove_add(uint x, uint y) public {-        unchecked {-            if (x + y < x) return; // no overflow-            assertTrue(x + y >= x);-        }-    }--    function prove_balance(address usr, uint amt) public {-        assertEq(0, token.balanceOf(usr));-        token.mint(usr, amt);-        assertEq(amt, token.balanceOf(usr));-    }--    function prove_supply(uint supply) public {-        token.mint(address(this), supply);-        uint actual = token.totalSupply();-        assertEq(supply, actual);-    }--    function prove_constructorArgs(address b) public {-        ConstructorArg c = new ConstructorArg(b);-        assertEq(b, c.a());-    }--    function proveFail_revertSmoke() public {-        require(false);-    }--    function proveFail_assertSmoke() public {-        assertTrue(false);-    }--    // Takes too long, disabling here, moving to benchmarks-    // function prove_transfer(uint supply, address usr, uint amt) public {-    //     if (amt > supply) return; // no underflow-    //-    //     token.mint(address(this), supply);-    //-    //     uint prebal = token.balanceOf(usr);-    //     token.transfer(usr, amt);-    //     uint postbal = token.balanceOf(usr);-    //-    //     uint expected = usr == address(this)-    //                     ? 0    // self transfer is a noop-    //                     : amt; // otherwise `amt` has been transfered to `usr`-    //     assertEq(expected, postbal - prebal);-    // }--    function prove_burn(uint supply, uint amt) public {-        if (amt > supply) return; // no undeflow--        token.mint(address(this), supply);-        token.burn(address(this), amt);--        assertEq(supply - amt, token.totalSupply());-    }-}
− test/contracts/pass/invariants.sol
@@ -1,41 +0,0 @@-import "ds-test/test.sol";-import "tokens/erc20.sol";--contract InvariantTest is DSTest {-    ERC20 token;-    User user;-    address[] targetContracts_;--    function targetContracts() public returns (address[] memory) {-      return targetContracts_;-    }--    function setUp() public {-        token = new ERC20("TOKEN", "TKN", 18);-        user = new User(token);-        token.mint(address(user), type(uint).max);-        targetContracts_.push(address(user));-    }--    function invariantTestThisBal() public {-        assertLe(token.balanceOf(address(user)), type(uint).max);-    }-    function invariantTotSupply() public {-        assertEq(token.totalSupply(), type(uint).max);-    }-}--contract User {-  ERC20 token;-  constructor(ERC20 token_) public {-    token = token_;-  }--  function doTransfer(address to, uint amount) public {-    token.transfer(to, amount);-  }--  function doSelfTransfer(uint amount) public {-    token.transfer(address(this), amount);-  }-}
− test/contracts/pass/libraries.sol
@@ -1,18 +0,0 @@-import "ds-test/test.sol";--library A {-    function f(uint128 x) public returns (uint256) {-        return uint(x) * 2;-    }-}--contract B is DSTest {-    using A for uint128;-    function test_f(uint128 x) public {-        assertEq(uint(x) * 2, x.f());-    }--    function testFail_f() public {-        assertEq(1, uint128(1).f());-    }-}
− test/contracts/pass/loops.sol
@@ -1,12 +0,0 @@-import "ds-test/test.sol";--contract Loops is DSTest {--    function prove_loop(uint n) public {-        uint counter = 0;-        for (uint i = 0; i < n; i++) {-            counter++;-        }-        assertTrue(counter < 100);-    }-}
− test/contracts/pass/rpc.sol
@@ -1,16 +0,0 @@-import {DSTest} from "ds-test/test.sol";-import {ERC20} from "tokens/erc20.sol";--contract C is DSTest {-    // BAL: https://etherscan.io/address/0xba100000625a3754423978a60c9317c58a424e3D#code-    ERC20 bal = ERC20(0xba100000625a3754423978a60c9317c58a424e3D);--    function test_trivial() public {-        uint ub = bal.balanceOf(0xBA12222222228d8Ba445958a75a0704d566BF2C8);-        assertEq(ub, 27099516537379438397130892);-    }--    function prove_trivial() public {-        test_trivial();-    }-}
− test/contracts/pass/trivial.sol
@@ -1,9 +0,0 @@-import {DSTest} from "ds-test/test.sol";--// should run and pass-contract Trivial is DSTest {-    function testTrue() public {-        assertTrue(true);-    }-}-
test/rpc.hs view
@@ -10,6 +10,7 @@ import Data.Text (Text) import Data.Vector qualified as V +import Optics.Core import EVM (makeVm) import EVM.ABI import EVM.Fetch@@ -20,6 +21,7 @@ import EVM.Test.Utils import EVM.Solidity (ProjectType(..)) import EVM.Types hiding (BlockNumber)+import Control.Monad.ST (stToIO, RealWorld)  main :: IO () main = defaultMain tests@@ -37,7 +39,7 @@                                                    , prevRandao                                                    ) -        assertEqual "coinbase" (Addr 0xea674fdde714fd979de3edf0f56aa9716b898ec8) cb+        assertEqual "coinbase" (LitAddr 0xea674fdde714fd979de3edf0f56aa9716b898ec8) cb         assertEqual "number" (BlockNumber numb) block         assertEqual "basefee" 38572377838 basefee         assertEqual "prevRan" 11049842297455506 prevRan@@ -51,7 +53,7 @@                                                    , prevRandao                                                    ) -        assertEqual "coinbase" (Addr 0x690b9a9e9aa1c9db991c7721a92d351db4fac990) cb+        assertEqual "coinbase" (LitAddr 0x690b9a9e9aa1c9db991c7721a92d351db4fac990) cb         assertEqual "number" (BlockNumber numb) block         assertEqual "basefee" 22163046690 basefee         assertEqual "prevRan" 0x2267531ab030ed32fd5f2ef51f81427332d0becbd74fe7f4cd5684ddf4b287e0 prevRan@@ -60,7 +62,7 @@     -- execute against remote state from a ds-test harness     [ testCase "dapp-test" $ do         let testFile = "test/contracts/pass/rpc.sol"-        runSolidityTestCustom testFile ".*" Nothing False testRpcInfo Foundry >>= assertEqual "test result" True+        runSolidityTestCustom testFile ".*" Nothing Nothing False testRpcInfo Foundry >>= assertEqual "test result" True      -- concretely exec "transfer" on WETH9 using remote rpc     -- https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code@@ -73,11 +75,11 @@         postVm <- withSolvers Z3 1 Nothing $ \solvers ->           Stepper.interpret (oracle solvers (Just (BlockNumber blockNum, testRpc))) vm Stepper.runFully         let-          postStore = case postVm.env.storage of+          wethStore = (fromJust $ Map.lookup (LitAddr 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) postVm.env.contracts).storage+          wethStore' = case wethStore of             ConcreteStore s -> s-            _ -> internalError "ConcreteStore expected"-          wethStore = fromJust $ Map.lookup 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 postStore-          receiverBal = fromJust $ Map.lookup (keccak' (word256Bytes 0xdead <> word256Bytes 0x3)) wethStore+            _ -> internalError "Expecting concrete store"+          receiverBal = fromJust $ Map.lookup (keccak' (word256Bytes 0xdead <> word256Bytes 0x3)) wethStore'           msg = case postVm.result of             Just (VMSuccess m) -> m             _ -> internalError "VMSuccess expected"@@ -100,49 +102,50 @@   ]  -- call into WETH9 from 0xf04a... (a large holder)-weth9VM :: W256 -> (Expr Buf, [Prop]) -> IO VM+weth9VM :: W256 -> (Expr Buf, [Prop]) -> IO (VM RealWorld) weth9VM blockNum calldata' = do   let-    caller' = Lit 0xf04a5cc80b1e94c69b48f5ee68a08cd2f09a7c3e+    caller' = LitAddr 0xf04a5cc80b1e94c69b48f5ee68a08cd2f09a7c3e     weth9 = Addr 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2     callvalue' = Lit 0   vmFromRpc blockNum calldata' callvalue' caller' weth9 -vmFromRpc :: W256 -> (Expr Buf, [Prop]) -> Expr EWord -> Expr EWord -> Addr -> IO VM-vmFromRpc blockNum calldata' callvalue' caller' address' = do-  ctrct <- fetchContractFrom (BlockNumber blockNum) testRpc address' >>= \case-        Nothing -> internalError $ "contract not found: " <> show address'+vmFromRpc :: W256 -> (Expr Buf, [Prop]) -> Expr EWord -> Expr EAddr -> Addr -> IO (VM RealWorld)+vmFromRpc blockNum calldata callvalue caller address = do+  ctrct <- fetchContractFrom (BlockNumber blockNum) testRpc address >>= \case+        Nothing -> internalError $ "contract not found: " <> show address         Just contract' -> return contract'    blk <- fetchBlockFrom (BlockNumber blockNum) testRpc >>= \case     Nothing -> internalError "could not fetch block"     Just b -> pure b -  pure $ makeVm $ VMOpts-    { contract      = ctrct-    , calldata      = calldata'-    , value         = callvalue'-    , address       = address'-    , caller        = caller'-    , origin        = 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-    , initialStorage = EmptyStore-    , txAccessList  = mempty-    , allowFFI      = False-    }+  stToIO $ (makeVm $ VMOpts+    { contract       = 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+    }) <&> set (#cache % #fetched % at address) (Just ctrct)  testRpc :: Text testRpc = "https://eth-mainnet.alchemyapi.io/v2/vpeKFsEF6PHifHzdtcwXSDbhV3ym5Ro4"
test/test.hs view
@@ -7,2766 +7,3816 @@  import GHC.TypeLits import Data.Proxy-import Control.Monad.State.Strict-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.List qualified as List-import Data.Map.Strict qualified as Map-import Data.Maybe-import Data.String.Here-import Data.Text (Text)-import Data.Text qualified as T-import Data.Time (diffUTCTime, getCurrentTime)-import Data.Typeable-import Data.Vector qualified as Vector-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 (into, unsafeInto)--import Optics.Core hiding (pre, re)-import Optics.State-import Optics.Operators.Unsafe--import EVM hiding (choose)-import EVM.ABI-import EVM.Concrete (createAddress)-import EVM.Exec-import EVM.Expr qualified as Expr-import EVM.Fetch qualified as Fetch-import EVM.Format (hexText)-import EVM.Patricia qualified as Patricia-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.Tracing qualified as Tracing-import EVM.Test.Utils-import EVM.Traversals-import EVM.Types--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"-  [ Tracing.tests-  , testGroup "StorageTests"-    [ testCase "read-from-sstore" $ assertEqual ""-        (Lit 0xab)-        (Expr.readStorage' (Lit 0x0) (Lit 0x0) (SStore (Lit 0x0) (Lit 0x0) (Lit 0xab) AbstractStore))-    , testCase "read-from-concrete" $ assertEqual ""-        (Lit 0xab)-        (Expr.readStorage' (Lit 0x0) (Lit 0x0) (ConcreteStore $ Map.fromList [(0x0, Map.fromList [(0x0, 0xab)])]))-    , testCase "read-past-abstract-writes-to-different-address" $ assertEqual ""-        (Lit 0xab)-        (Expr.readStorage' (Lit 0x0) (Lit 0x0) (SStore (Lit 0x1) (Var "a") (Var "b") (ConcreteStore $ Map.fromList [(0x0, Map.fromList [(0x0, 0xab)])])))-    , testCase "abstract-slots-block-reads-for-same-address" $ assertEqual ""-        (SLoad (Lit 0x0) (Lit 0x0) (SStore (Lit 0x0) (Var "b") (Var "c") (ConcreteStore $ Map.fromList [(0x0, Map.fromList [(0x0, 0xab)])])))-        (Expr.readStorage' (Lit 0x0) (Lit 0x0)-          (SStore (Lit 0x1) (Var "1312") (Var "acab") (SStore (Lit 0x0) (Var "b") (Var "c") (ConcreteStore $ Map.fromList [(0x0, Map.fromList [(0x0, 0xab)])]))))-    , testCase "abstract-addrs-block-reads" $ assertEqual ""-        (SLoad (Lit 0x0) (Lit 0x0) (SStore (Var "1312") (Lit 0x0) (Lit 0x0) (ConcreteStore $ Map.fromList [(0x0, Map.fromList [(0x0, 0xab)])])))-        (Expr.readStorage' (Lit 0x0) (Lit 0x0)-          (SStore (Lit 0xacab) (Lit 0xdead) (Lit 0x0) (SStore (Var "1312") (Lit 0x0) (Lit 0x0) (ConcreteStore $ Map.fromList [(0x0, Map.fromList [(0x0, 0xab)])]))))--    , testCase "accessStorage uses fetchedStorage" $ do-        let dummyContract =-              (initialContract (RuntimeCode (ConcreteRuntimeCode mempty)))-                { external = True }-            vm = vmForEthrunCreation ""-            -- perform the initial access-            vm1 = execState (EVM.accessStorage 0 (Lit 0) (pure . pure ())) vm-            -- it should fetch the contract first-            vm2 = case vm1.result of-                    Just (HandleEffect (Query (PleaseFetchContract _addr continue))) ->-                      execState (continue dummyContract) vm1-                    _ -> internalError "unexpected result"-            -- then it should fetch the slow-            vm3 = case vm2.result of-                    Just (HandleEffect (Query (PleaseFetchSlot _addr _slot continue))) ->-                      execState (continue 1337) vm2-                    _ -> internalError "unexpected result"-            -- perform the same access as for vm1-            vm4 = execState (EVM.accessStorage 0 (Lit 0) (pure . pure ())) vm3--        -- there won't be query now as accessStorage uses fetch cache-        assertBool (show vm4.result) (isNothing vm4.result)-    ]-  , testGroup "SimplifierUnitTests"-    -- common overflow cases that the simplifier was getting wrong-    [ testCase "writeWord-overflow" $ do-        let e = ReadByte (Lit 0x0) (WriteWord (Lit 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd) (Lit 0x0) (ConcreteBuf "\255\255\255\255"))-        b <- checkEquiv e (Expr.simplify e)-        assertBool "Simplifier failed" b-    , testCase "CopySlice-overflow" $ do-        let e = ReadWord (Lit 0x0) (CopySlice (Lit 0x0) (Lit 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc) (Lit 0x6) (ConcreteBuf "\255\255\255\255\255\255") (ConcreteBuf ""))-        b <- checkEquiv e (Expr.simplify e)-        assertBool "Simplifier failed" b-    , testCase "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-        assertBool "must be equivalent" 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 (min n 50)) $ testGroup "SimplifierTests"-    [ testProperty  "buffer-simplification" $ \(expr :: Expr Buf) -> ioProperty $ do-        let simplified = Expr.simplify expr-        checkEquiv expr simplified-    , testProperty "store-simplification" $ \(expr :: Expr Storage) -> ioProperty $ do-        let simplified = Expr.simplify expr-        checkEquiv expr simplified-    , testProperty "byte-simplification" $ \(expr :: Expr Byte) -> ioProperty $ do-        let simplified = Expr.simplify expr-        checkEquiv expr simplified-    -- https://github.com/ethereum/hevm/issues/311-    , ignoreTest $ testProperty "word-simplification" $ \(ZeroDepthWord expr) -> ioProperty $ do-        let simplified = Expr.simplify expr-        checkEquiv expr simplified-    , testProperty "readStorage-equivalance" $ \(store, addr, slot) -> ioProperty $ do-        let simplified = Expr.readStorage' addr slot store-            full = SLoad addr slot store-        checkEquiv simplified full-    , testProperty "writeStorage-equivalance" $ \(val, GenWriteStorageExpr (addr, slot, store)) -> ioProperty $ do-        let simplified = Expr.writeStorage addr slot val store-            full = SStore addr slot val store-        checkEquiv simplified full-    , testProperty "readWord-equivalance" $ \(buf, idx) -> ioProperty $ do-        let simplified = Expr.readWord idx buf-            full = ReadWord idx buf-        checkEquiv simplified full-    , testProperty "writeWord-equivalance" $ \(idx, val, WriteWordBuf buf) -> ioProperty $ do-        let simplified = Expr.writeWord idx val buf-            full = WriteWord idx val buf-        checkEquiv simplified full-    , testProperty "arith-simplification" $ \(_ :: Int) -> ioProperty $ do-        expr <- generate . sized $ genWordArith 15-        let simplified = Expr.simplify expr-        checkEquiv expr simplified-    , testProperty "readByte-equivalance" $ \(buf, idx) -> ioProperty $ do-        let simplified = Expr.readByte idx buf-            full = ReadByte idx buf-        checkEquiv simplified full-    -- 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-        checkEquiv simplified full-    , 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-        checkEquiv simplified full-    , testProperty "indexWord-equivalence" $ \(src, LitWord @50 idx) -> ioProperty $ do-        let simplified = Expr.indexWord idx src-            full = IndexWord idx src-        checkEquiv simplified full-    , 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-        checkEquiv simplified full-    , 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 -> 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 <- generate $ fixLength buf-        case Expr.toList input of-          Nothing -> do-            putStrLn "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-    ]-  , testGroup "MemoryTests"-    [ 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-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 "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 "encodeConcreteStore-overwrite" $-      let-        w :: Int -> W256-        w x = W256 $ EVM.Types.word256 $ BS.pack [fromIntegral x]-      in-      assertEqual ""-        (EVM.SMT.encodeConcreteStore $-          Map.fromList [(w 1, (Map.fromList [(w 2, w 99), (w 2, w 100)]))])-        "(sstore (_ bv1 256) (_ bv2 256) (_ bv100 256) emptyStore)"-    , 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)-    ]-  , testGroup "ABI"-    [ testProperty "Put/get inverse" $ \x ->-        case runGetOrFail (getAbi (abiValueType x)) (runPut (putAbi x)) of-          Right ("", _, x') -> x' == x-          _ -> False-    ]-  , testGroup "Solidity-Expressions"-    [ testCase "Trivial" $-        SolidityCall "x = 3;" []-          ===> AbiUInt 256 3--    , testCase "Arithmetic" $ do-        SolidityCall "x = a + 1;"-          [AbiUInt 256 1] ===> AbiUInt 256 2-        SolidityCall "unchecked { x = a - 1; }"-          [AbiUInt 8 0] ===> AbiUInt 8 255--    , testCase "keccak256()" $-        SolidityCall "x = uint(keccak256(abi.encodePacked(a)));"-          [AbiString ""] ===> AbiUInt 256 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470--    , testProperty "abi encoding vs. solidity" $ withMaxSuccess 20 $ forAll (arbitrary >>= genAbiValue) $-      \y -> ioProperty $ do-          Just encoded <- runStatements [i| x = abi.encode(a);|]-            [y] AbiBytesDynamicType-          let solidityEncoded = case decodeAbiValue (AbiTupleType $ Vector.fromList [AbiBytesDynamicType]) (BS.fromStrict encoded) of-                AbiTuple (Vector.toList -> [e]) -> e-                _ -> internalError "AbiTuple expected"-          let hevmEncoded = encodeAbiValue (AbiTuple $ Vector.fromList [y])-          assertEqual "abi encoding mismatch" solidityEncoded (AbiBytesDynamic hevmEncoded)--    , testProperty "abi encoding vs. solidity (2 args)" $ withMaxSuccess 20 $ forAll (arbitrary >>= bothM genAbiValue) $-      \(x', y') -> ioProperty $ do-          Just encoded <- runStatements [i| x = abi.encode(a, b);|]-            [x', y'] AbiBytesDynamicType-          let solidityEncoded = case decodeAbiValue (AbiTupleType $ Vector.fromList [AbiBytesDynamicType]) (BS.fromStrict encoded) of-                AbiTuple (Vector.toList -> [e]) -> e-                _ -> internalError "AbiTuple expected"-          let hevmEncoded = encodeAbiValue (AbiTuple $ Vector.fromList [x',y'])-          assertEqual "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:-    , testProperty "abi encoding vs. solidity (function pointer)" $ withMaxSuccess 20 $ forAll (genAbiValue AbiFunctionType) $-      \y -> ioProperty $ do-          Just encoded <- runFunction [i|-              function foo(function() external a) public pure returns (bytes memory x) {-                x = abi.encode(a);-              }-            |] (abiMethod "foo(function)" (AbiTuple (Vector.singleton y)))-          let solidityEncoded = case decodeAbiValue (AbiTupleType $ Vector.fromList [AbiBytesDynamicType]) (BS.fromStrict encoded) of-                AbiTuple (Vector.toList -> [e]) -> e-                _ -> internalError "AbiTuple expected"-          let hevmEncoded = encodeAbiValue (AbiTuple $ Vector.fromList [y])-          assertEqual "abi encoding mismatch" solidityEncoded (AbiBytesDynamic hevmEncoded)-    ]--  , testGroup "Precompiled contracts"-      [ testGroup "Example (reverse)"-          [ testCase "success" $-              assertEqual "example contract reverses"-                (execute 0xdeadbeef "foobar" 6) (Just "raboof")-          , testCase "failure" $-              assertEqual "example contract fails on length mismatch"-                (execute 0xdeadbeef "foobar" 5) Nothing-          ]--      , testGroup "ECRECOVER"-          [ testCase "success" $ do-              let-                r = hex "c84e55cee2032ea541a32bf6749e10c8b9344c92061724c4e751600f886f4732"-                s = hex "1542b6457e91098682138856165381453b3d0acae2470286fd8c8a09914b1b5d"-                v = hex "000000000000000000000000000000000000000000000000000000000000001c"-                h = hex "513954cf30af6638cb8f626bd3f8c39183c26784ce826084d9d267868a18fb31"-                a = hex "0000000000000000000000002d5e56d45c63150d937f2182538a0f18510cb11f"-              assertEqual "successful recovery"-                (Just a)-                (execute 1 (h <> v <> r <> s) 32)-          , testCase "fail on made up values" $ do-              let-                r = hex "c84e55cee2032ea541a32bf6749e10c8b9344c92061724c4e751600f886f4731"-                s = hex "1542b6457e91098682138856165381453b3d0acae2470286fd8c8a09914b1b5d"-                v = hex "000000000000000000000000000000000000000000000000000000000000001c"-                h = hex "513954cf30af6638cb8f626bd3f8c39183c26784ce826084d9d267868a18fb31"-              assertEqual "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 "Unresolved link detection"-    [ testCase "holes detected" $ do-        let code' = "608060405234801561001057600080fd5b5060405161040f38038061040f83398181016040528101906100329190610172565b73__$f3cbc3eb14e5bd0705af404abcf6f741ec$__63ab5c1ffe826040518263ffffffff1660e01b81526004016100699190610217565b60206040518083038186803b15801561008157600080fd5b505af4158015610095573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100b99190610145565b50506103c2565b60006100d36100ce84610271565b61024c565b9050828152602081018484840111156100ef576100ee610362565b5b6100fa8482856102ca565b509392505050565b600081519050610111816103ab565b92915050565b600082601f83011261012c5761012b61035d565b5b815161013c8482602086016100c0565b91505092915050565b60006020828403121561015b5761015a61036c565b5b600061016984828501610102565b91505092915050565b6000602082840312156101885761018761036c565b5b600082015167ffffffffffffffff8111156101a6576101a5610367565b5b6101b284828501610117565b91505092915050565b60006101c6826102a2565b6101d081856102ad565b93506101e08185602086016102ca565b6101e981610371565b840191505092915050565b60006102016003836102ad565b915061020c82610382565b602082019050919050565b6000604082019050818103600083015261023181846101bb565b90508181036020830152610244816101f4565b905092915050565b6000610256610267565b905061026282826102fd565b919050565b6000604051905090565b600067ffffffffffffffff82111561028c5761028b61032e565b5b61029582610371565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b60008115159050919050565b60005b838110156102e85780820151818401526020810190506102cd565b838111156102f7576000848401525b50505050565b61030682610371565b810181811067ffffffffffffffff821117156103255761032461032e565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f6261720000000000000000000000000000000000000000000000000000000000600082015250565b6103b4816102be565b81146103bf57600080fd5b50565b603f806103d06000396000f3fe6080604052600080fdfea26469706673582212207d03b26e43dc3d116b0021ddc9817bde3762a3b14315351f11fc4be384fd14a664736f6c63430008060033"-        assertBool "linker hole not detected" (containsLinkerHole code'),-      testCase "no false positives" $ do-        let code' = "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806317bf8bac1461003b578063acffee6b1461005d575b600080fd5b610043610067565b604051808215151515815260200191505060405180910390f35b610065610073565b005b60008060015414905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8a8fd6d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156100da57600080fd5b505afa1580156100ee573d6000803e3d6000fd5b505050506040513d602081101561010457600080fd5b810190808051906020019092919050505060018190555056fea265627a7a723158205d775f914dcb471365a430b5f5b2cfe819e615cbbb5b2f1ccc7da1fd802e43c364736f6c634300050b0032"-        assertBool "false positive" (not . containsLinkerHole $ code')-    ]--  , testGroup "metadata stripper"-    [ testCase "it strips the metadata for solc => 0.6" $ do-        let code' = hexText "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806317bf8bac1461003b578063acffee6b1461005d575b600080fd5b610043610067565b604051808215151515815260200191505060405180910390f35b610065610073565b005b60008060015414905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8a8fd6d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156100da57600080fd5b505afa1580156100ee573d6000803e3d6000fd5b505050506040513d602081101561010457600080fd5b810190808051906020019092919050505060018190555056fea265627a7a723158205d775f914dcb471365a430b5f5b2cfe819e615cbbb5b2f1ccc7da1fd802e43c364736f6c634300050b0032"-            stripped = stripBytecodeMetadata code'-        assertEqual "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;-                  }-                }-                |]--        (json, path') <- solidity' srccode-        let (Contracts solc', _, _) = fromJust $ readStdJSON json-            initCode = (solc' ^?! ix (path' <> ":A")).creationCode-        -- add constructor arguments-        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) ->---      withMaxSuccess 100000 $-      rlpdecode (rlpencode (BS bs)) == Just (BS bs)-    , testProperty "rlp encode is a partial inverse (bytes)" $ \(Bytes bs) ->---      withMaxSuccess 100000 $-        case rlpdecode bs of-          Just r -> rlpencode r == bs-          Nothing -> True-    ,  testProperty "rlp decode is a retraction (RLP)" $ \(RLPData r) ->---       withMaxSuccess 100000 $-       rlpdecode (rlpencode r) == Just r-    ]-  , testGroup "Merkle Patricia Trie"-    [  testProperty "update followed by delete is id" $ \(Bytes r, Bytes s, Bytes t) ->-        whenFail-        (putStrLn ("r:" <> (show (ByteStringS r))) >>-         putStrLn ("s:" <> (show (ByteStringS s))) >>-         putStrLn ("t:" <> (show (ByteStringS t)))) $---       withMaxSuccess 100000 $-       Patricia.insertValues [(r, BS.pack[1]), (s, BS.pack[2]), (t, BS.pack[3]),-                              (r, mempty), (s, mempty), (t, mempty)]-       === (Just $ Patricia.Literal Patricia.Empty)-    ]- , testGroup "Remote State Tests"-   [-   ]- , 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)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s [0x01] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-       assertEqual "Must be 0" 0 $ getVar ctr "arg1"-       putStrLn  $ "expected counterexample found, and it's correct: " <> (show $ 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)]) <- withSolvers Z3 1 Nothing $ \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-        assertBool "Overflow must occur" (toInteger x + toInteger y >= maxUint)-        putStrLn "expected counterexample found"-     ,-     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)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s [0x12] c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-        assertEqual "Division by 0 needs b=0" (getVar ctr "arg2") 0-        putStrLn "expected counterexample found"-     ,-      testCase "unused-args-fail" $ do-         Just c <- solcRuntime "C"-             [i|-             contract C {-               function fun(uint256 a) public pure {-                 assert(false);-               }-             }-             |]-         (_, [Cex _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s [0x1] c Nothing [] defaultVeriOpts-         putStrLn "expected counterexample found"-      ,-     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)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s [0x21] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-        assertBool "Enum is only defined for 0 and 1" $ (getVar ctr "arg1") > 1-        putStrLn "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 $ 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 <- withSolvers Z3 1 Nothing $ \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 (_, _)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s [0x32] c (Just (Sig "fun(uint8)" [AbiUIntType 8])) [] defaultVeriOpts-        -- assertBool "Access must be beyond element 2" $ (getVar ctr "arg1") > 1-        putStrLn "expected counterexample found"-      ,-      -- Note: we catch the assertion here, even though we are only able to explore partially-      testCase "alloc-too-much" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(uint256 a) external {-                uint[] memory arr = new uint[](a);-              }-             }-            |]-        (_, [Cex _]) <- withSolvers Z3 1 Nothing $ \s ->-          checkAssert s [0x41] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-        putStrLn "expected counterexample found"-      ,-      -- TODO the system currently does not allow for symbolic JUMP-      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 _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s [0x51] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-        putStrLn "expected counterexample found"- ]--  , testGroup "Dapp-Tests"-    [ testCase "Trivial-Pass" $ do-        let testFile = "test/contracts/pass/trivial.sol"-        runSolidityTest testFile ".*" >>= assertEqual "test result" True-    , testCase "DappTools" $ do-        -- quick smokecheck to make sure that we can parse dapptools style build outputs-        let cases =-              [ ("test/contracts/pass/trivial.sol", ".*", True)-              , ("test/contracts/pass/invariants.sol", "invariantTestThisBal", True)-              , ("test/contracts/pass/dsProvePass.sol", "proveEasy", True)-              , ("test/contracts/fail/trivial.sol", ".*", False)-              , ("test/contracts/fail/invariantFail.sol", "invariantCount", False)-              , ("test/contracts/fail/dsProveFail.sol", "prove_add", False)-              ]-        results <- forM cases $ \(testFile, match, expected) -> do-          actual <- runSolidityTestCustom testFile match Nothing False Nothing DappTools-          pure (actual == expected)-        assertBool "test result" (and results)-    , testCase "Trivial-Fail" $ do-        let testFile = "test/contracts/fail/trivial.sol"-        runSolidityTest testFile "testFalse" >>= assertEqual "test result" False-    , testCase "Abstract" $ do-        let testFile = "test/contracts/pass/abstract.sol"-        runSolidityTest testFile ".*" >>= assertEqual "test result" True-    , testCase "Constantinople" $ do-        let testFile = "test/contracts/pass/constantinople.sol"-        runSolidityTest testFile ".*" >>= assertEqual "test result" True-    , testCase "Prove-Tests-Pass" $ do-        let testFile = "test/contracts/pass/dsProvePass.sol"-        runSolidityTest testFile ".*" >>= assertEqual "test result" True-    , testCase "Prove-Tests-Fail" $ do-        let testFile = "test/contracts/fail/dsProveFail.sol"-        runSolidityTest testFile "prove_trivial" >>= assertEqual "test result" False-        runSolidityTest testFile "prove_add" >>= assertEqual "test result" False-        --runSolidityTest testFile "prove_smtTimeout" >>= assertEqual "test result" False-        runSolidityTest testFile "prove_multi" >>= assertEqual "test result" False-        -- TODO: implement overflow checking optimizations and enable, currently this runs forever-        --runSolidityTest testFile "prove_distributivity" >>= assertEqual "test result" False-    , testCase "Loop-Tests" $ do-        let testFile = "test/contracts/pass/loops.sol"-        runSolidityTestCustom testFile "prove_loop" (Just 10) False Nothing Foundry >>= assertEqual "test result" True-        runSolidityTestCustom testFile "prove_loop" (Just 100) False Nothing Foundry >>= assertEqual "test result" False-    , testCase "Invariant-Tests-Pass" $ do-        let testFile = "test/contracts/pass/invariants.sol"-        runSolidityTest testFile ".*" >>= assertEqual "test result" True-    , testCase "Invariant-Tests-Fail" $ do-        let testFile = "test/contracts/fail/invariantFail.sol"-        runSolidityTest testFile "invariantFirst" >>= assertEqual "test result" False-        runSolidityTest testFile "invariantCount" >>= assertEqual "test result" False-    , testCase "Cheat-Codes-Pass" $ do-        let testFile = "test/contracts/pass/cheatCodes.sol"-        runSolidityTest testFile ".*" >>= assertEqual "test result" True-    , testCase "Cheat-Codes-Fail" $ do-        let testFile = "test/contracts/fail/cheatCodes.sol"-        runSolidityTestCustom testFile "testBadFFI" Nothing False Nothing Foundry >>= assertEqual "test result" False-        runSolidityTestCustom testFile "test_prank_underflow" Nothing False Nothing Foundry >>= assertEqual "test result" False-    , testCase "Unwind" $ do-        let testFile = "test/contracts/pass/unwind.sol"-        runSolidityTest testFile ".*" >>= assertEqual "test result" True-    ]-  , testGroup "max-iterations"-    [ testCase "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{ maxIter = Just 3 }-        (e, [Qed _]) <- withSolvers Z3 1 Nothing $-          \s -> checkAssert s defaultPanicCodes c sig [] opts-        assertBool "The expression is not partial" $ isPartial e-    , testCase "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{ maxIter = Just 6 }-        (e, [Qed _]) <- withSolvers Z3 1 Nothing $-          \s -> checkAssert s defaultPanicCodes c sig [] opts-        assertBool "The expression is partial" $ not $ isPartial e-    , testCase "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;-              }-            }-            |]-        (e, [Qed _]) <- withSolvers Z3 1 Nothing $-          \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] (defaultVeriOpts{ maxIter = Just 5 })-        assertBool "The expression is not partial" $ Expr.containsNode isPartial e-    , testCase "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 dont' 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{ maxIter = Just 10, askSmtIters = 5 }-        (e, [Qed _]) <- withSolvers Z3 1 Nothing $-          \s -> checkAssert s defaultPanicCodes c sig [] opts-        assertBool "The expression is not partial" $ Expr.containsNode isPartial e-    , testCase "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{ maxIter = Just 5, askSmtIters = 1 }-        (e, [Qed _]) <- withSolvers Z3 1 Nothing $-          \s -> checkAssert s defaultPanicCodes c sig [] opts-        assertBool "The expression is partial" $ not (Expr.containsNode isPartial e)-    ]-  , testGroup "Symbolic execution"-      [-     testCase "require-test" $ do-        Just c <- solcRuntime "MyContract"-            [i|-            contract MyContract {-              function fun(int256 a) external pure {-              require(a <= 0);-              assert (a <= 0);-              }-             }-            |]-        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256)" [AbiIntType 256])) [] defaultVeriOpts-        putStrLn "Require works as expected"-     ,-     testCase "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 _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-       putStrLn "expected counterexample found"-     ,-     testCase "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-           }-         }-         |]-       (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-       putStrLn "this should always be true, due to bitwise OR with positive value"-    ,-    -- 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)-    testCase "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(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])-      (res, [Qed _]) <- withSolvers Z3 1 Nothing $ \s ->-        checkAssert s defaultPanicCodes c sig [] defaultVeriOpts-      putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"-     ,-     -- TODO look at tests here for SAR: https://github.com/dapphub/dapptools/blob/01ef8ea418c3fe49089a44d56013d8fcc34a1ec2/src/dapp-tests/pass/constantinople.sol#L250-     testCase "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);-              }-             }-            |]-        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256)" [AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts-        putStrLn "SAR works as expected"-     ,-     testCase "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);-              }-             }-            |]-        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256)" [AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts-        putStrLn "SAR works as expected"-     ,-     testCase "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);-              }-             }-            |]-        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256)" [AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts-        putStrLn "SAR works as expected"-     ,-     testCase "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);-              }-             }-            |]-        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256)" [AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts-        putStrLn "SAR works as expected"-     ,-     testCase "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);--              }-            }-            |]-        (_, [Qed _])  <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-        putStrLn "sdiv works as expected"-      ,-     testCase "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);--              }-            }-            |]-        (_, [Qed _])  <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-        putStrLn "sdiv works as expected"-      ,-     testCase "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);--              }-            }-            |]-        (_, [Qed _])  <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-        putStrLn "sdiv works as expected"-      ,-     testCase "signed-overflow-checks" $ do-        Just c <- solcRuntime "C"-            [i|-            contract C {-              function fun(uint160 a) external {-                  int256 j = int256(uint256(a)) + 1;-                  assert(false);-              }-            }-            |]-        (_, [Cex _])  <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint160)" [AbiUIntType 160])) [] defaultVeriOpts-        putStrLn "expected cex discovered"-      ,-     testCase "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-              }-            }-            |]-        (_, [Qed _])  <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-        putStrLn "signextend works as expected"-      ,-     testCase "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);-              }-            }-            |]-        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint8)" [AbiUIntType 256, AbiUIntType 8])) [] defaultVeriOpts-        putStrLn "signextend works as expected"-      ,-      testCase "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-              }-            }-            |]-        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint8)" [AbiUIntType 256, AbiUIntType 8])) [] defaultVeriOpts-        putStrLn "signextend works as expected"-      ,-      -- when b is too large, value is unchanged-      testCase "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);-              }-            }-            |]-        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint8)" [AbiUIntType 256, AbiUIntType 8])) [] defaultVeriOpts-        putStrLn "signextend works as expected"-     ,-     testCase "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);-              }-             }-            |]-        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-        putStrLn "SAR works as expected"-     ,-     testCase "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);-              }-             }-            |]-        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-        putStrLn "XOR works as expected"-      ,-      testCase "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));-              }-             }-            |]-        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-        putStrLn "XOR works as expected"-      ,-      testCase "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);-              }-            }-            |]-        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint16)" [AbiUIntType 16])) [] defaultVeriOpts-        putStrLn "DIV by zero is zero"-      ,-      -- Somewhat tautological since we are asserting the precondition-      -- on the same form as the actual "requires" clause.-      testCase "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-        (res, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> verifyContract s safeAdd (Just (Sig "add(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts AbstractStore (Just pre) (Just post)-        putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"-     ,--      testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s ->-          verifyContract s safeAdd (Just (Sig "add(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts AbstractStore (Just pre) (Just post)-        putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"-      ,-      testCase "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 = Expr.litAddr $ prestate.state.codeContract-                  prex = Expr.readStorage' this (Lit 0) prestate.env.storage-              in case leaf of-                Success _ _ _ postStore -> Expr.add prex (Expr.mul (Lit 2) y) .== (Expr.readStorage' this (Lit 0) postStore)-                _ -> PBool True-        (res, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> verifyContract s c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts AbstractStore (Just pre) (Just post)-        putStrLn $ "successfully explored: " <> show (Expr.numBranches 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-        testCase "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()-                          }-                        }-                      }-                    }-                    |]-            Just c <- yulRuntime "Neg" src-            (res, [Qed _]) <- withSolvers Z3 4 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "hello(address)" [AbiAddressType])) [] defaultVeriOpts-            putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"-        ,-        testCase "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')-                        _ -> internalError "expected 2 args"-                    this = Expr.litAddr $ prestate.state.codeContract-                    prestore = prestate.env.storage-                    prex = Expr.readStorage' this x prestore-                    prey = Expr.readStorage' this y prestore-                in case poststate of-                     Success _ _ _ poststore -> let-                           postx = Expr.readStorage' this x poststore-                           posty = Expr.readStorage' this y poststore-                       in Expr.add prex prey .== Expr.add postx posty-                     _ -> PBool True-          (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> verifyContract s c (Just (Sig "f(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts AbstractStore (Just pre) (Just post)-          putStrLn "Correct, this can never fail"-        ,-        -- Inspired by these `msg.sender == to` token bugs-        -- which break linearity of totalSupply.-        testCase "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 poststate =-                let (x,y) = case getStaticAbiArgs 2 prestate of-                        [x',y'] -> (x',y')-                        _ -> internalError "expected 2 args"-                    this = Expr.litAddr $ prestate.state.codeContract-                    prestore =  prestate.env.storage-                    prex = Expr.readStorage' this x prestore-                    prey = Expr.readStorage' this y prestore-                in case poststate of-                     Success _ _ _ poststore -> let-                           postx = Expr.readStorage' this x poststore-                           posty = Expr.readStorage' this y poststore-                       in Expr.add prex prey .== Expr.add postx posty-                     _ -> PBool True-          (_, [Cex (_, ctr)]) <- withSolvers Z3 1 Nothing $ \s -> verifyContract s c (Just (Sig "f(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts AbstractStore (Just pre) (Just post)-          let x = getVar ctr "arg1"-          let y = getVar ctr "arg2"-          putStrLn $ "y:" <> show y-          putStrLn $ "x:" <> show x-          assertEqual "Catch storage collisions" x y-          putStrLn "expected counterexample found"-        ,-        testCase "simple-assert" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              function foo() external pure {-                assert(false);-              }-             }-            |]-          (_, [Cex (Failure _ _ (Revert msg), _)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo()" [])) [] defaultVeriOpts-          assertEqual "incorrect revert msg" msg (ConcreteBuf $ panicMsg 0x01)-        ,-        testCase "simple-assert-2" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              function foo(uint256 x) external pure {-                assert(x != 10);-              }-             }-            |]-          (_, [(Cex (_, ctr))]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          assertEqual "Must be 10" 10 $ getVar ctr "arg1"-          putStrLn "Got 10 Cex, as expected"-        ,-        testCase "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)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          let ints = map (flip getVar "arg1") [a,b]-          assertBool "0 must be one of the Cex-es" $ isJust $ List.elemIndex 0 ints-          putStrLn "expected 2 counterexamples found, one Cex is the 0 value"-        ,-        testCase "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)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          let x = getVar a "arg1"-          let y = getVar b "arg1"-          assertBool "At least one has to be 0, to go through the first assert" (x == 0 || y == 0)-          putStrLn "expected 2 counterexamples found."-        ,-        testCase "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 _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-          putStrLn "expected 2 counterexamples found"-        ,-        testCase "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)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-          assertEqual "Must be 666" 666 $ getVar ctr "arg2"-          putStrLn "Found arg2 Ctx to be 666"-        ,-        -- LSB is zeroed out, byte(31,x) takes LSB, so y==0 always holds-        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLn $ "successfully explored: " <> show (Expr.numBranches 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-        testCase "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)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          assertBool "last byte must be non-zero" $ ((Data.Bits..&.) (getVar ctr "arg1") 0xff) > 0-          putStrLn "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-        testCase "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)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          assertBool "second to last byte must be non-zero" $ ((Data.Bits..&.) (getVar ctr "arg1") 0xff00) > 0-          putStrLn "Expected counterexample found"-        ,-        -- Reverse of thest above-        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"-        ,-        -- Bitwise OR operation test-        testCase "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);-              }-            }-            |]-          (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLn "When OR-ing with full 1's we should get back full 1's"-        ,-        -- Bitwise OR operation test-        testCase "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);-              }-            }-            |]-          (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLn "When OR-ing with a byte of 1's, we should get 1's back there"-        ,-        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "deposit(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"-        ,-        testCase "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)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s allPanicCodes c (Just (Sig "deposit(uint8)" [AbiUIntType 8])) [] defaultVeriOpts-          assertEqual "Must be 255" 255 $ getVar ctr "arg1"-          putStrLn  $ "expected counterexample found, and it's correct: " <> (show $ getVar ctr "arg1")-        ,-        testCase "explore function dispatch" $ do-          Just c <- solcRuntime "A"-            [i|-            contract A {-              function f(uint x) public pure returns (uint) {-                return x;-              }-            }-            |]-          (res, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts-          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"-        ,-        testCase "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);-              }-            }-            |]-          (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts-          putStrLn "in bounds byte reads return the expected value"-        ,-        testCase "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);-              }-            }-            |]-          (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLn "div/mod/sdiv/smod by zero works as expected during constant propagation"-        ,-        testCase "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);-              }-            }-            |]-          (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts-          putStrLn "oob byte reads always return 0"-        ,-        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"-        ,-        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"-        ,-        testCase "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)]) <- withSolvers Z3 1 Nothing $ \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"-          assertEqual "x==y for hash collision" x y-          assertEqual "w==z for hash collision" w z-          putStrLn "expected counterexample found"-        ,-        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts-          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"-        ,-        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"-        ,-        testCase "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);-                assembly {-                  x := calldataload(z)-                }-                assert(x == 0);-              }-            }-            |]-          (res, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"-        ,-        testCase "multiple-contracts" $ do-          let code' =-                [i|-                  contract C {-                    uint x;-                    A constant a = A(0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B);--                    function call_A() public view {-                      // should fail since a.x() can be anything-                      assert(a.x() == x);-                    }-                  }-                  contract A {-                    uint public x;-                  }-                |]-              aAddr = Addr 0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B-          Just c <- solcRuntime "C" code'-          Just a <- solcRuntime "A" code'-          (_, [Cex (_, cex)]) <- withSolvers Z3 1 Nothing $ \s -> do-            let vm0 = abstractVM (mkCalldata (Just (Sig "call_A()" [])) []) c Nothing AbstractStore-            let vm = vm0-                  & set (#state % #callvalue) (Lit 0)-                  & over (#env % #contracts)-                       (Map.insert aAddr (initialContract (RuntimeCode (ConcreteRuntimeCode a))))-            verify s defaultVeriOpts vm (Just $ checkAssertions defaultPanicCodes)--          let storeCex = cex.store-              addrC = into $ createAddress ethrunAddress 1-              addrA = W256 0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B-              testCex = Map.size storeCex == 2 &&-                        case (Map.lookup addrC storeCex, Map.lookup addrA storeCex) of-                          (Just sC, Just sA) -> Map.size sC == 1 && Map.size sA == 1 &&-                            case (Map.lookup 0 sC, Map.lookup 0 sA) of-                              (Just x, Just y) -> x /= y-                              _ -> False-                          _ -> False-          assertBool "Did not find expected storage cex" testCex-          putStrLn "expected counterexample found"-        ,-        expectFail $ testCase "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 _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "call_A()" [])) [] defaultVeriOpts-          putStrLn "expected counterexample found"-        ,-        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "kecc(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"-        ,-        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"-        ,-        ignoreTest $ testCase "safemath distributivity (yul)" $ do-          let yulsafeDistributivity = hex "6355a79a6260003560e01c14156016576015601f565b5b60006000fd60a1565b603d602d604435600435607c565b6039602435600435607c565b605d565b6052604b604435602435605d565b600435607c565b141515605a57fe5b5b565b6000828201821115151560705760006000fd5b82820190505b92915050565b6000818384048302146000841417151560955760006000fd5b82820290505b92915050565b"-          let vm =  abstractVM (mkCalldata (Just (Sig "distributivity(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) []) yulsafeDistributivity Nothing AbstractStore-          (_, [Qed _]) <-  withSolvers Z3 1 Nothing $ \s -> verify s defaultVeriOpts vm (Just $ checkAssertions defaultPanicCodes)-          putStrLn "Proven"-        ,-        testCase "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");-                  }-                }-              }-            |]--          (_, [Qed _]) <- withSolvers Z3 1 (Just 99999999) $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "distributivity(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts-          putStrLn "Proven"-        ,-        testCase "storage-cex-1" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              uint x;-              uint y;-              function fun(uint256 a) external{-                assert (x == y);-              }-            }-            |]-          (_, [(Cex (_, cex))]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s [0x01] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          let addr = into $ createAddress ethrunAddress 1-              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-          assertBool "Did not find expected storage cex" testCex-          putStrLn "Expected counterexample found"-        ,-        testCase "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))]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s [0x01] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts-          let addr = into $ createAddress ethrunAddress 1-              a = getVar cex "arg1"-              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 (10 + a) s) of-                                      (Just x, Just y) -> x >= y-                                      _ -> False-                          _ -> False-          assertBool "Did not find expected storage cex" testCex-          putStrLn "Expected counterexample found"-        ,-        testCase "storage-cex-concrete" $ do-          Just c <- solcRuntime "C"-            [i|-            contract C {-              uint x;-              uint y;-              function fun(uint256 a) external{-                assert (x != y);-              }-            }-            |]-          (_, [Cex (_, cex)]) <- withSolvers Z3 1 Nothing $ \s -> verifyContract s c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts EmptyStore Nothing (Just $ checkAssertions [0x01])-          let testCex = Map.null cex.store-          assertBool "Did not find expected storage cex" testCex-          putStrLn "Expected counterexample found"- ]-  , testGroup "Equivalence checking"-    [-      testCase "eq-yul-simple-cex" $ do-        Just aPrgm <- yul ""-          [i|-          {-            calldatacopy(0, 0, 32)-            switch mload(0)-            case 0 { }-            case 1 { }-            default { invalid() }-          }-          |]-        Just bPrgm <- yul ""-          [i|-          {-            calldatacopy(0, 0, 32)-            switch mload(0)-            case 0 { }-            case 2 { }-            default { invalid() }-          }-          |]-        withSolvers Z3 3 Nothing $ \s -> do-          a <- equivalenceCheck s aPrgm bPrgm defaultVeriOpts (mkCalldata Nothing [])-          assertBool "Must have a difference" (any isCex a)-      ,-      testCase "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 Nothing $ \s -> do-          a <- equivalenceCheck s aPrgm bPrgm defaultVeriOpts (mkCalldata Nothing [])-          assertEqual "Must have no difference" [Qed ()] a-          return ()-      ,-      testCase "eq-sol-exp-cex" $ do-        -- These yul programs are not equivalent: (try --calldata $(seth --to-uint256 2) for example)-        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 Z3 3 Nothing $ \s -> do-          a <- equivalenceCheck s aPrgm bPrgm defaultVeriOpts (mkCalldata Nothing [])-          assertEqual "Must be different" (any isCex a) True-          return ()-      , testCase "eq-all-yul-optimization-tests" $ do-        let opts = defaultVeriOpts{ 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"--                    -- unexpected symbolic arg ----                    -- OpCreate2-                    , "expressionSimplifier/create2_and_mask.yul"--                    -- OpCreate-                    , "expressionSimplifier/create_and_mask.yul"-                    , "expressionSimplifier/large_byte_access.yul"--                    -- OpMload-                    , "yulOptimizerTests/expressionSplitter/inside_function.yul"-                    , "fullInliner/double_inline.yul"-                    , "fullInliner/inside_condition.yul"-                    , "fullInliner/large_function_multi_use.yul"-                    , "fullInliner/large_function_single_use.yul"-                    , "fullInliner/no_inline_into_big_global_context.yul"-                    , "fullSimplify/invariant.yul"-                    , "fullSuite/abi_example1.yul"-                    , "ssaAndBack/for_loop.yul"-                    , "ssaAndBack/multi_assign_multi_var_if.yul"-                    , "ssaAndBack/multi_assign_multi_var_switch.yul"-                    , "ssaAndBack/two_vars.yul"-                    , "ssaTransform/multi_assign.yul"-                    , "ssaTransform/multi_decl.yul"-                    , "expressionSplitter/inside_function.yul"-                    , "fullSuite/ssaReverseComplex.yul"--                    -- OpMstore-                    , "commonSubexpressionEliminator/function_scopes.yul"-                    , "commonSubexpressionEliminator/variable_for_variable.yul"-                    , "expressionSplitter/trivial.yul"-                    , "fullInliner/multi_return.yul"-                    , "fullSimplify/constant_propagation.yul"-                    , "fullSimplify/identity_rules_complex.yul"-                    , "fullSuite/medium.yul"-                    , "loadResolver/memory_with_msize.yul"-                    , "loadResolver/merge_known_write.yul"-                    , "loadResolver/merge_known_write_with_distance.yul"-                    , "loadResolver/merge_unknown_write.yul"-                    , "loadResolver/reassign_value_expression.yul"-                    , "loadResolver/second_mstore_with_delta.yul"-                    , "loadResolver/second_store_with_delta.yul"-                    , "loadResolver/simple.yul"-                    , "loadResolver/simple_memory.yul"-                    , "fullSuite/ssaReverse.yul"-                    , "rematerialiser/cheap_caller.yul"-                    , "rematerialiser/non_movable_instruction.yul"-                    , "rematerialiser/for_break.yul"-                    , "rematerialiser/for_continue.yul"-                    , "rematerialiser/for_continue_2.yul"-                    , "ssaAndBack/multi_assign.yul"-                    , "ssaAndBack/multi_assign_if.yul"-                    , "ssaAndBack/multi_assign_switch.yul"-                    , "ssaAndBack/simple.yul"-                    , "ssaReverser/simple.yul"-                    , "loopInvariantCodeMotion/simple_storage.yul"--                    -- OpMstore8-                    , "loadResolver/memory_with_different_kinds_of_invalidation.yul"--                    -- OpRevert-                    , "ssaAndBack/ssaReverse.yul"-                    , "redundantAssignEliminator/for_continue_3.yul"-                    , "controlFlowSimplifier/terminating_for_revert.yul"--                    -- invalid test ---                    -- https://github.com/ethereum/solidity/issues/9500-                    , "commonSubexpressionEliminator/object_access.yul"-                    , "expressionSplitter/object_access.yul"-                    , "fullSuite/stack_compressor_msize.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--                    -- wrong number of args ---                    , "wordSizeTransform/functional_instruction.yul"-                    , "wordSizeTransform/if.yul"-                    , "wordSizeTransform/or_bool_renamed.yul"-                    , "wordSizeTransform/switch_1.yul"-                    , "wordSizeTransform/switch_2.yul"-                    , "wordSizeTransform/switch_3.yul"-                    , "wordSizeTransform/switch_4.yul"-                    , "wordSizeTransform/switch_5.yul"-                    , "unusedFunctionParameterPruner/too_many_arguments.yul"--                    -- 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"--                    -- 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"--                    -- TODO check what's wrong with these!-                    , "loadResolver/keccak_short.yul" -- ACTUAL bug -- keccak-                    , "reasoningBasedSimplifier/signed_division.yul" -- ACTUAL bug, SDIV-                    ]--        solcRepo <- fromMaybe (internalError "cannot find solidity repo") <$> (lookupEnv "HEVM_SOLIDITY_REPO")-        let testDir = solcRepo <> "/test/libyul/yulOptimizerTests"-        dircontents <- 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 <- recursiveList fullpaths []-        let filesFiltered = filter (\file -> not $ any (`List.isSubsequenceOf` 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-          origcont <- 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 <- getCurrentTime-          putStrLn $ "Checking file: " <> f-          when opts.debug $ 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 -----------------"-          Just aPrgm <- yul "" $ T.pack $ unlines filteredASym-          Just bPrgm <- yul "" $ T.pack $ unlines filteredBSym-          procs <- getNumProcessors-          withSolvers CVC5 (unsafeInto procs) (Just 100) $ \s -> do-            res <- equivalenceCheck s aPrgm bPrgm opts (mkCalldata Nothing [])-            end <- getCurrentTime-            case any isCex res of-              False -> do-                print $ "OK. Took " <> (show $ diffUTCTime end start) <> " seconds"-                let timeouts = filter isTimeout res-                unless (null timeouts) $ do-                  putStrLn $ "But " <> (show $ length timeouts) <> " timeout(s) occurred"-                  internalError "Encountered timeouts"-              True -> do-                putStrLn $ "Not OK: " <> show f <> " Got: " <> show res-                internalError "Was NOT equivalent"-           )-    ]-  ]-  where-    (===>) = assertSolidityComputation---checkEquiv :: (Typeable a) => Expr a -> Expr a -> IO Bool-checkEquiv l r = withSolvers Z3 1 (Just 10) $ \solvers -> do-  if l == r-     then do-       putStrLn "skip"-       pure True-     else do-       let smt = assertProps [l ./= r]-       res <- checkSat solvers smt-       print res-       pure $ case res of-         Unsat -> True-         EVM.Solvers.Unknown -> True-         Sat _ -> False-         Error _ -> False---- | 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 :: ByteString -> ByteString -> IO (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 :: ByteString -> IO (Maybe VM)-loadVM x = do-  vm1 <- Stepper.interpret (Fetch.zero 0 Nothing) (vmForEthrunCreation x) 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 >> Stepper.run)-       pure $ Just vm2-     _ -> pure Nothing-  where-    prepVm target targetCode = Stepper.evm $ do-      replaceCodeOfSelf (RuntimeCode $ ConcreteRuntimeCode targetCode)-      resetState-      assign (#state % #gas) 0xffffffffffffffff -- kludge-      loadContract target--hex :: ByteString -> ByteString-hex s =-  case BS16.decodeBase16 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 :: Text -> ByteString -> IO (Maybe ByteString)-runFunction c input = do-  Just x <- singleContract "X" c-  runSimpleVM x input--runStatements-  :: Text -> [AbiValue] -> AbiType-  -> IO (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 $ Vector.fromList args))--getStaticAbiArgs :: Int -> VM -> [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 $ Vector.fromList types) (BS.fromStrict (BS.drop 4 bs)) of-        AbiTuple xy' -> xy'-        _ -> internalError "AbiTuple expected"-  in Vector.toList xy--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 Word256 where-  arbitrary = liftM2 fromHiAndLo arbitrary arbitrary--instance Arbitrary W256 where-  arbitrary = fmap W256 arbitrary--instance Arbitrary (Expr Storage) where-  arbitrary = sized genStorage--instance Arbitrary (Expr EWord) where-  arbitrary = sized defaultWord--instance Arbitrary (Expr Byte) where-  arbitrary = sized genByte--instance Arbitrary (Expr Buf) where-  arbitrary = sized defaultBuf--instance Arbitrary (Expr End) where-  arbitrary = sized genEnd---- 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---- 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 EWord, Expr Storage)-  deriving (Show, Eq)--instance Arbitrary GenWriteStorageExpr where-  arbitrary = do-    addr <- arbitrary-    slot <- arbitrary-    let mkStore = oneof-          [ pure EmptyStore-          , 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 matchAddr = liftM2 (SStore addr) arbitrary arbitrary-                  matchBoth = fmap (SStore addr slot) arbitrary-                  addWrites :: Expr Storage -> Int -> Gen (Expr Storage)-                  addWrites b 0 = pure b-                  addWrites b n = liftM4 SStore arbitrary arbitrary arbitrary (addWrites b (n - 1))-              s <- arbitrary-              addMatch <- oneof [ matchAddr, matchBoth ]-              let withMatch = addMatch s-              newWrites <- oneof [ pure 0, pure 1, fmap (`mod` 5) arbitrary ]-              addWrites withMatch newWrites-          , arbitrary-          ]-    store <- mkStore-    pure $ GenWriteStorageExpr (addr, 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 (fromIntegral (1_000_000 :: Int))) , (1, fmap Lit arbitrary) ]-    fmap GenWriteByteIdx mkIdx--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 fromIntegral (arbitrary :: Gen Natural)--genName :: Gen Text--- In order not to generate SMT reserved words, we prepend with "esc_"-genName = fmap (T.pack . ("esc_" <> )) $ 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- [ fmap (Failure mempty mempty . Revert) subBuf- , liftM4 Success (return mempty) (return mempty) subBuf subStore- , liftM3 ITE subWord subEnd subEnd- ]- where-   subBuf = defaultBuf (sz `div` 2)-   subStore = genStorage (sz `div` 2)-   subWord = defaultWord (sz `div` 2)-   subEnd = genEnd (sz `div` 2)--genWord :: Int -> Int -> Gen (Expr EWord)-genWord litFreq 0 = frequency-  [ (litFreq, do-      val <- frequency-       [ (10, fmap (`mod` 100) arbitrary)-       , (1, arbitrary)-       ]-      pure $ Lit val-    )-  , (1, oneof-      [ pure Origin-      , pure Coinbase-      , pure Timestamp-      , pure BlockNumber-      , pure PrevRandao-      , pure GasLimit-      , pure ChainId-      , pure BaseFee-      , fmap CallValue genNat-      , fmap Caller genNat-      , fmap Address genNat-      --, liftM2 SelfBalance arbitrary arbitrary-      --, liftM2 Gas arbitrary arbitrary-      , fmap Lit arbitrary-      , fmap Var genName-      ]-    )-  ]-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-    , liftM3 SLoad subWord subWord subStore-    , liftM2 ReadWord subWord 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)--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)--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-  , 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 subWord (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)--genStorage :: Int -> Gen (Expr Storage)-genStorage 0 = oneof-  [ pure AbstractStore-  , pure EmptyStore-  --, fmap ConcreteStore arbitrary-  ]-genStorage sz = liftM4 SStore subWord subWord subWord subStore-  where-    subStore = genStorage (sz `div` 10)-    subWord = defaultWord (sz `div` 5)--data Invocation-  = SolidityCall Text [AbiValue]-  deriving Show--assertSolidityComputation :: Invocation -> AbiValue -> IO ()-assertSolidityComputation (SolidityCall s args) x =-  do y <- runStatements s args (abiValueType x)-     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))+import Control.Monad.ST (RealWorld, stToIO)+import Control.Monad.State.Strict+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.String.Here+import Data.Text (Text)+import Data.Text qualified as T+import Data.Text.IO qualified as T+import Data.Time (diffUTCTime, getCurrentTime)+import Data.Typeable+import Data.Vector qualified as V+import Data.Word (Word8)+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 Optics.Core hiding (pre, re, elements)+import Optics.State++import EVM hiding (choose)+import EVM.ABI+import EVM.Assembler+import EVM.Exec+import EVM.Expr qualified as Expr+import EVM.Fetch qualified as Fetch+import EVM.Format (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.Tracing qualified as Tracing+import EVM.Test.Utils+import EVM.Traversals+import EVM.Types++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"+  [ Tracing.tests+  , testGroup "simplify-storage"+    [ 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);+            }+          }+        }+        |]+       expr <- withSolvers Z3 1 Nothing $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] debugVeriOpts+       assertEqual "Expression is not clean." (badStoresInExpr expr) 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 $ 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);+            }+          }+        }+        |]+       expr <- withSolvers Z3 1 Nothing $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] debugVeriOpts+       -- T.writeFile "symbolic-index.expr" $ formatExpr expr+       assertEqual "Expression is not clean." (badStoresInExpr expr) False+    , expectFail $ 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);+            }+          }+        }+        |]+       expr <- withSolvers Z3 1 Nothing $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] debugVeriOpts+       assertEqual "Expression is not clean." (badStoresInExpr expr) 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);+            }+          }+        }+        |]+       expr <- withSolvers Z3 1 Nothing $ \s -> getExpr s c (Just (Sig "transfer(uint256)" [AbiUIntType 256])) [] (debugVeriOpts { maxIter = Just 5 })+       assertEqual "Expression is not clean." (badStoresInExpr expr) 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);+            }+          }+        }+        |]+       expr <- withSolvers Z3 1 Nothing $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] (debugVeriOpts { maxIter = Just 5 })+       assertEqual "Expression is not clean." (badStoresInExpr expr) 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);+            }+          }+        }+        |]+       expr <- withSolvers Z3 1 Nothing $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] debugVeriOpts+       assertEqual "Expression is not clean." (badStoresInExpr expr) 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);+            }+          }+        }+        |]+       expr <- withSolvers Z3 1 Nothing $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] debugVeriOpts+       -- putStrLn $ T.unpack $ formatExpr expr+       assertEqual "Expression is not clean." (badStoresInExpr expr) 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);+            }+          }+        }+        |]+       expr <- withSolvers Z3 1 Nothing $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] debugVeriOpts+       assertEqual "Expression is not clean." (badStoresInExpr expr) 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);+            }+          }+        }+       |]+       expr <- withSolvers Z3 1 Nothing $ \s -> getExpr s c (Just (Sig "transfer(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] debugVeriOpts+       -- putStrLn $ T.unpack $ formatExpr expr+       assertEqual "Expression is not clean." (badStoresInExpr expr) False+    ]+  , testGroup "StorageTests"+    [ testCase "read-from-sstore" $ assertEqual ""+        (Lit 0xab)+        (Expr.readStorage' (Lit 0x0) (SStore (Lit 0x0) (Lit 0xab) (AbstractStore (LitAddr 0x0))))+    , testCase "read-from-concrete" $ assertEqual ""+        (Lit 0xab)+        (Expr.readStorage' (Lit 0x0) (ConcreteStore $ Map.fromList [(0x0, 0xab)]))+    , testCase "read-past-write" $ assertEqual ""+        (Lit 0xab)+        (Expr.readStorage' (Lit 0x0) (SStore (Lit 0x1) (Var "b") (ConcreteStore $ Map.fromList [(0x0, 0xab)])))+    , testCase "accessStorage uses fetchedStorage" $ do+        let dummyContract =+              (initialContract (RuntimeCode (ConcreteRuntimeCode mempty)))+                { external = True }+        vm <- stToIO $ vmForEthrunCreation ""+            -- perform the initial access+        vm1 <- 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))) ->+                  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))) ->+                      stToIO $ execStateT (continue 1337) vm2+                    _ -> internalError "unexpected result"+            -- perform the same access as for vm1+        vm4 <- stToIO $ execStateT (EVM.accessStorage (LitAddr 0) (Lit 0) (pure . pure ())) vm3++        -- there won't be query now as accessStorage uses fetch cache+        assertBool (show vm4.result) (isNothing vm4.result)+    ]+  , testGroup "SimplifierUnitTests"+    -- common overflow cases that the simplifier was getting wrong+    [ testCase "bufLength-simp" $ do+      let+        a = BufLength (ConcreteBuf "ab")+        simp = Expr.simplify a+      assertEqual "Must be simplified down to a Lit" simp (Lit 2)+    , testCase "writeWord-overflow" $ do+        let e = ReadByte (Lit 0x0) (WriteWord (Lit 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd) (Lit 0x0) (ConcreteBuf "\255\255\255\255"))+        b <- checkEquiv e (Expr.simplify e)+        assertBool "Simplifier failed" b+    , testCase "CopySlice-overflow" $ do+        let e = ReadWord (Lit 0x0) (CopySlice (Lit 0x0) (Lit 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc) (Lit 0x6) (ConcreteBuf "\255\255\255\255\255\255") (ConcreteBuf ""))+        b <- checkEquiv e (Expr.simplify e)+        assertBool "Simplifier failed" b+    , testCase "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+        assertBool "must be equivalent" 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 (min n 50)) $ testGroup "SimplifierTests"+    [ testProperty  "buffer-simplification" $ \(expr :: Expr Buf) -> ioProperty $ do+        let simplified = Expr.simplify expr+        checkEquiv expr simplified+    , testProperty "store-simplification" $ \(expr :: Expr Storage) -> ioProperty $ do+        let simplified = Expr.simplify expr+        checkEquiv expr simplified+    , testProperty "byte-simplification" $ \(expr :: Expr Byte) -> ioProperty $ do+        let simplified = Expr.simplify expr+        checkEquiv expr simplified+    , testProperty "word-simplification" $ \(ZeroDepthWord expr) -> ioProperty $ do+        let simplified = Expr.simplify expr+        checkEquiv expr simplified+    , testProperty "readStorage-equivalance" $ \(store, slot) -> ioProperty $ do+        let simplified = Expr.readStorage' slot store+            full = SLoad slot store+        checkEquiv simplified full+    , testProperty "writeStorage-equivalance" $ \(val, GenWriteStorageExpr (slot, store)) -> ioProperty $ do+        let simplified = Expr.writeStorage slot val store+            full = SStore slot val store+        checkEquiv simplified full+    , testProperty "readWord-equivalance" $ \(buf, idx) -> ioProperty $ do+        let simplified = Expr.readWord idx buf+            full = ReadWord idx buf+        checkEquiv simplified full+    , testProperty "writeWord-equivalance" $ \(idx, val, WriteWordBuf buf) -> ioProperty $ do+        let simplified = Expr.writeWord idx val buf+            full = WriteWord idx val buf+        checkEquiv simplified full+    , testProperty "arith-simplification" $ \(_ :: Int) -> ioProperty $ do+        expr <- generate . sized $ genWordArith 15+        let simplified = Expr.simplify expr+        checkEquiv expr simplified+    , testProperty "readByte-equivalance" $ \(buf, idx) -> ioProperty $ do+        let simplified = Expr.readByte idx buf+            full = ReadByte idx buf+        checkEquiv simplified full+    -- 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+        checkEquiv simplified full+    , 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+        checkEquiv simplified full+    , testProperty "indexWord-equivalence" $ \(src, LitWord @50 idx) -> ioProperty $ do+        let simplified = Expr.indexWord idx src+            full = IndexWord idx src+        checkEquiv simplified full+    , 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+        checkEquiv simplified full+    , 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 -> 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 <- generate $ fixLength buf+        case Expr.toList input of+          Nothing -> do+            putStrLn "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) -> ioProperty $ do+        let simplified = Expr.simplifyProps [p]+        case simplified of+          [] -> checkEquivProp (PBool True) p+          [val@(PBool _)] -> checkEquivProp val p+          _ -> assertFailure "must evaluate down to a literal bool"+    , testProperty "simplifyProp-equivalence-sym" $ \(p) -> ioProperty $ do+        let simplified = Expr.simplifyProp p+        checkEquivProp simplified p+    , testProperty "simpProp-equivalence-sym" $ \(ps :: [Prop]) -> ioProperty $ do+        let simplified = pand (Expr.simplifyProps ps)+        checkEquivProp simplified (pand ps)+    , testProperty "simpProp-equivalence-sym" $ \(LitProp p) -> ioProperty $ do+        let simplified = pand (Expr.simplifyProps [p])+        checkEquivProp simplified p+    -- This would need to be a fuzz test I think. The SMT encoding of Keccak is not precise+    -- enough for this to succeed+    , ignoreTest $ testProperty "storage-slot-simp-property" $ \(StorageExp s) -> ioProperty $ do+        T.writeFile "unsimplified.expr" $ formatExpr s+        let simplified = Expr.simplify s+        T.writeFile "simplified.expr" $ formatExpr simplified+        checkEquiv simplified s+    ]+  , testGroup "simpProp-concrete-tests" [+      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-concrete-or-eq-rem" $ 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+  ]+  , testGroup "MemoryTests"+    [ 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-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 "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 "encodeConcreteStore-overwrite" $+      assertEqual ""+        "(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)])+    , 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)+    ]+  , testGroup "ABI"+    [ testProperty "Put/get inverse" $ \x ->+        case runGetOrFail (getAbi (abiValueType x)) (runPut (putAbi x)) of+          Right ("", _, x') -> x' == x+          _ -> False+    ]+  , testGroup "Solidity-Expressions"+    [ testCase "Trivial" $+        SolidityCall "x = 3;" []+          ===> AbiUInt 256 3++    , testCase "Arithmetic" $ do+        SolidityCall "x = a + 1;"+          [AbiUInt 256 1] ===> AbiUInt 256 2+        SolidityCall "unchecked { x = a - 1; }"+          [AbiUInt 8 0] ===> AbiUInt 8 255++    , testCase "keccak256()" $+        SolidityCall "x = uint(keccak256(abi.encodePacked(a)));"+          [AbiString ""] ===> AbiUInt 256 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470++    , testProperty "abi encoding vs. solidity" $ withMaxSuccess 20 $ forAll (arbitrary >>= genAbiValue) $+      \y -> ioProperty $ 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])+          assertEqual "abi encoding mismatch" solidityEncoded (AbiBytesDynamic hevmEncoded)++    , testProperty "abi encoding vs. solidity (2 args)" $ withMaxSuccess 20 $ forAll (arbitrary >>= bothM genAbiValue) $+      \(x', y') -> ioProperty $ 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'])+          assertEqual "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:+    , testProperty "abi encoding vs. solidity (function pointer)" $ withMaxSuccess 20 $ forAll (genAbiValue AbiFunctionType) $+      \y -> ioProperty $ 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])+          assertEqual "abi encoding mismatch" solidityEncoded (AbiBytesDynamic hevmEncoded)+    ]++  , testGroup "Precompiled contracts"+      [ testGroup "Example (reverse)"+          [ testCase "success" $+              assertEqual "example contract reverses"+                (execute 0xdeadbeef "foobar" 6) (Just "raboof")+          , testCase "failure" $+              assertEqual "example contract fails on length mismatch"+                (execute 0xdeadbeef "foobar" 5) Nothing+          ]++      , testGroup "ECRECOVER"+          [ testCase "success" $ do+              let+                r = hex "c84e55cee2032ea541a32bf6749e10c8b9344c92061724c4e751600f886f4732"+                s = hex "1542b6457e91098682138856165381453b3d0acae2470286fd8c8a09914b1b5d"+                v = hex "000000000000000000000000000000000000000000000000000000000000001c"+                h = hex "513954cf30af6638cb8f626bd3f8c39183c26784ce826084d9d267868a18fb31"+                a = hex "0000000000000000000000002d5e56d45c63150d937f2182538a0f18510cb11f"+              assertEqual "successful recovery"+                (Just a)+                (execute 1 (h <> v <> r <> s) 32)+          , testCase "fail on made up values" $ do+              let+                r = hex "c84e55cee2032ea541a32bf6749e10c8b9344c92061724c4e751600f886f4731"+                s = hex "1542b6457e91098682138856165381453b3d0acae2470286fd8c8a09914b1b5d"+                v = hex "000000000000000000000000000000000000000000000000000000000000001c"+                h = hex "513954cf30af6638cb8f626bd3f8c39183c26784ce826084d9d267868a18fb31"+              assertEqual "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 "Unresolved link detection"+    [ testCase "holes detected" $ do+        let code' = "608060405234801561001057600080fd5b5060405161040f38038061040f83398181016040528101906100329190610172565b73__$f3cbc3eb14e5bd0705af404abcf6f741ec$__63ab5c1ffe826040518263ffffffff1660e01b81526004016100699190610217565b60206040518083038186803b15801561008157600080fd5b505af4158015610095573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100b99190610145565b50506103c2565b60006100d36100ce84610271565b61024c565b9050828152602081018484840111156100ef576100ee610362565b5b6100fa8482856102ca565b509392505050565b600081519050610111816103ab565b92915050565b600082601f83011261012c5761012b61035d565b5b815161013c8482602086016100c0565b91505092915050565b60006020828403121561015b5761015a61036c565b5b600061016984828501610102565b91505092915050565b6000602082840312156101885761018761036c565b5b600082015167ffffffffffffffff8111156101a6576101a5610367565b5b6101b284828501610117565b91505092915050565b60006101c6826102a2565b6101d081856102ad565b93506101e08185602086016102ca565b6101e981610371565b840191505092915050565b60006102016003836102ad565b915061020c82610382565b602082019050919050565b6000604082019050818103600083015261023181846101bb565b90508181036020830152610244816101f4565b905092915050565b6000610256610267565b905061026282826102fd565b919050565b6000604051905090565b600067ffffffffffffffff82111561028c5761028b61032e565b5b61029582610371565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b60008115159050919050565b60005b838110156102e85780820151818401526020810190506102cd565b838111156102f7576000848401525b50505050565b61030682610371565b810181811067ffffffffffffffff821117156103255761032461032e565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f6261720000000000000000000000000000000000000000000000000000000000600082015250565b6103b4816102be565b81146103bf57600080fd5b50565b603f806103d06000396000f3fe6080604052600080fdfea26469706673582212207d03b26e43dc3d116b0021ddc9817bde3762a3b14315351f11fc4be384fd14a664736f6c63430008060033"+        assertBool "linker hole not detected" (containsLinkerHole code'),+      testCase "no false positives" $ do+        let code' = "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806317bf8bac1461003b578063acffee6b1461005d575b600080fd5b610043610067565b604051808215151515815260200191505060405180910390f35b610065610073565b005b60008060015414905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8a8fd6d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156100da57600080fd5b505afa1580156100ee573d6000803e3d6000fd5b505050506040513d602081101561010457600080fd5b810190808051906020019092919050505060018190555056fea265627a7a723158205d775f914dcb471365a430b5f5b2cfe819e615cbbb5b2f1ccc7da1fd802e43c364736f6c634300050b0032"+        assertBool "false positive" (not . containsLinkerHole $ code')+    ]++  , testGroup "metadata stripper"+    [ testCase "it strips the metadata for solc => 0.6" $ do+        let code' = hexText "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806317bf8bac1461003b578063acffee6b1461005d575b600080fd5b610043610067565b604051808215151515815260200191505060405180910390f35b610065610073565b005b60008060015414905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8a8fd6d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156100da57600080fd5b505afa1580156100ee573d6000803e3d6000fd5b505050506040513d602081101561010457600080fd5b810190808051906020019092919050505060018190555056fea265627a7a723158205d775f914dcb471365a430b5f5b2cfe819e615cbbb5b2f1ccc7da1fd802e43c364736f6c634300050b0032"+            stripped = stripBytecodeMetadata code'+        assertEqual "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"+  [+     testCase "assert-fail" $ do+       Just c <- solcRuntime "MyContract"+           [i|+           contract MyContract {+             function fun(uint256 a) external pure {+               assert(a != 0);+             }+            }+           |]+       (_, [Cex (_, ctr)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s [0x01] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+       assertEqual "Must be 0" 0 $ getVar ctr "arg1"+       putStrLn  $ "expected counterexample found, and it's correct: " <> (show $ 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)]) <- withSolvers Z3 1 Nothing $ \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+        assertBool "Overflow must occur" (toInteger x + toInteger y >= maxUint)+        putStrLn "expected counterexample found"+     ,+     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)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s [0x12] c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+        assertEqual "Division by 0 needs b=0" (getVar ctr "arg2") 0+        putStrLn "expected counterexample found"+     ,+      testCase "unused-args-fail" $ do+         Just c <- solcRuntime "C"+             [i|+             contract C {+               function fun(uint256 a) public pure {+                 assert(false);+               }+             }+             |]+         (_, [Cex _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s [0x1] c Nothing [] defaultVeriOpts+         putStrLn "expected counterexample found"+      ,+     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)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s [0x21] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+        assertBool "Enum is only defined for 0 and 1" $ (getVar ctr "arg1") > 1+        putStrLn "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 $ 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 <- withSolvers Z3 1 Nothing $ \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 (_, _)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s [0x32] c (Just (Sig "fun(uint8)" [AbiUIntType 8])) [] defaultVeriOpts+        -- assertBool "Access must be beyond element 2" $ (getVar ctr "arg1") > 1+        putStrLn "expected counterexample found"+      ,+      -- Note: we catch the assertion here, even though we are only able to explore partially+      testCase "alloc-too-much" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(uint256 a) external {+                uint[] memory arr = new uint[](a);+              }+             }+            |]+        (_, [Cex _]) <- withSolvers Z3 1 Nothing $ \s ->+          checkAssert s [0x41] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+        putStrLn "expected counterexample found"+      ,+      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);+              }+            }+          |]+        Right e <- reachableUserAsserts c (Just $ Sig "f(address,uint256)" [AbiAddressType, AbiUIntType 256])+        assertBool "The expression is not partial" $ Expr.containsNode isPartial e+      ,+      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}();+                }+              }+            |]+        r <- allBranchesFail c Nothing+        assertBool "all branches must fail" (isRight r)+      ,+      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);+                }+              }+            |]+        Right e <- reachableUserAsserts c Nothing+        T.putStrLn $ formatExpr e+        assertBool "The expression is not partial" $ Expr.containsNode isPartial e+      ,+      -- 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)]) <- withSolvers Z3 1 Nothing $+          \s -> checkAssert s [0x51] c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+        let a = fromJust $ Map.lookup (Var "arg1") cex.vars+        assertEqual "unexpected cex value" a 44+        putStrLn "expected counterexample found"+  ]+  , 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...+    [ testCase "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 Z3 1 Nothing $ \s -> do+          let calldata = (WriteWord (Lit 0x0) (Var "u") (ConcreteBuf ""), [])+          initVM <- stToIO $ abstractVM calldata initCode Nothing True+          expr <- Expr.simplify <$> interpret (Fetch.oracle s Nothing) Nothing 1 StackBased initVM runExpr+          assertBool "unexptected partial execution" (not $ Expr.containsNode isPartial expr)+    , testCase "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 expr <- reachableUserAsserts c (Just $ Sig "foo(uint256)" [AbiUIntType 256])+        assertBool "unexptected partial execution" (not $ Expr.containsNode isPartial expr)+    , testCase "jump-into-symbolic-region" $ do+        let+          -- our initCode just jumps directly to the end+          code = BS.pack . mapMaybe maybeLitByte $ 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+              ])+        withSolvers Z3 1 Nothing $ \s -> do+          vm <- stToIO $ loadSymVM runtimecode (Lit 0) initCode False+          expr <- Expr.simplify <$> interpret (Fetch.oracle s Nothing) Nothing 1 StackBased vm runExpr+          case expr of+            Partial _ _ (JumpIntoSymbolicCode _ _) -> assertBool "" True+            _ -> assertBool "did not encounter expected partial node" False+    ]+  , testGroup "Dapp-Tests"+    [ testCase "Trivial-Pass" $ do+        let testFile = "test/contracts/pass/trivial.sol"+        runSolidityTest testFile ".*" >>= assertEqual "test result" True+    , testCase "DappTools" $ do+        -- quick smokecheck to make sure that we can parse dapptools style build outputs+        let cases =+              [ ("test/contracts/pass/trivial.sol", ".*", True)+              , ("test/contracts/pass/dsProvePass.sol", "proveEasy", True)+              , ("test/contracts/fail/trivial.sol", ".*", False)+              , ("test/contracts/fail/dsProveFail.sol", "prove_add", False)+              ]+        results <- forM cases $ \(testFile, match, expected) -> do+          actual <- runSolidityTestCustom testFile match Nothing Nothing False Nothing DappTools+          pure (actual == expected)+        assertBool "test result" (and results)+    , testCase "Trivial-Fail" $ do+        let testFile = "test/contracts/fail/trivial.sol"+        runSolidityTest testFile "prove_false" >>= assertEqual "test result" False+    , testCase "Abstract" $ do+        let testFile = "test/contracts/pass/abstract.sol"+        runSolidityTest testFile ".*" >>= assertEqual "test result" True+    , testCase "Constantinople" $ do+        let testFile = "test/contracts/pass/constantinople.sol"+        runSolidityTest testFile ".*" >>= assertEqual "test result" True+    , testCase "Prove-Tests-Pass" $ do+        let testFile = "test/contracts/pass/dsProvePass.sol"+        runSolidityTest testFile ".*" >>= assertEqual "test result" True+    , testCase "prefix-check-for-dapp" $ do+        let testFile = "test/contracts/fail/check-prefix.sol"+        runSolidityTest testFile "check_trivial" >>= assertEqual "test result" False+    , testCase "Prove-Tests-Fail" $ do+        let testFile = "test/contracts/fail/dsProveFail.sol"+        runSolidityTest testFile "prove_trivial" >>= assertEqual "test result" False+        runSolidityTest testFile "prove_trivial_dstest" >>= assertEqual "test result" False+        runSolidityTest testFile "prove_add" >>= assertEqual "test result" False+        runSolidityTestCustom testFile "prove_smtTimeout" (Just 1) Nothing False Nothing Foundry >>= assertEqual "test result" False+        runSolidityTest testFile "prove_multi" >>= assertEqual "test result" False+        -- TODO: implement overflow checking optimizations and enable, currently this runs forever+        --runSolidityTest testFile "prove_distributivity" >>= assertEqual "test result" False+    , testCase "Loop-Tests" $ do+        let testFile = "test/contracts/pass/loops.sol"+        runSolidityTestCustom testFile "prove_loop" Nothing (Just 10) False Nothing Foundry >>= assertEqual "test result" True+        runSolidityTestCustom testFile "prove_loop" Nothing (Just 100) False Nothing Foundry >>= assertEqual "test result" False+    , testCase "Cheat-Codes-Pass" $ do+        let testFile = "test/contracts/pass/cheatCodes.sol"+        runSolidityTest testFile ".*" >>= assertEqual "test result" True+    , testCase "Unwind" $ do+        let testFile = "test/contracts/pass/unwind.sol"+        runSolidityTest testFile ".*" >>= assertEqual "test result" True+    ]+  , testGroup "max-iterations"+    [ testCase "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{ maxIter = Just 3 }+        (e, [Qed _]) <- withSolvers Z3 1 Nothing $+          \s -> checkAssert s defaultPanicCodes c sig [] opts+        assertBool "The expression is not partial" $ isPartial e+    , testCase "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{ maxIter = Just 6 }+        (e, [Qed _]) <- withSolvers Z3 1 Nothing $+          \s -> checkAssert s defaultPanicCodes c sig [] opts+        assertBool "The expression is partial" $ not $ isPartial e+    , testCase "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;+              }+            }+            |]+        (e, [Qed _]) <- withSolvers Z3 1 Nothing $+          \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] (defaultVeriOpts{ maxIter = Just 5 })+        assertBool "The expression is not partial" $ Expr.containsNode isPartial e+    , testCase "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 dont' 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{ maxIter = Just 10, askSmtIters = 5 }+        (e, [Qed _]) <- withSolvers Z3 1 Nothing $+          \s -> checkAssert s defaultPanicCodes c sig [] opts+        assertBool "The expression is not partial" $ Expr.containsNode isPartial e+    , testCase "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{ maxIter = Just 5, askSmtIters = 1 }+        (e, [Qed _]) <- withSolvers Z3 1 Nothing $+          \s -> checkAssert s defaultPanicCodes c sig [] opts+        assertBool "The expression is partial" $ not (Expr.containsNode isPartial e)+    ]+  , testGroup "Symbolic Addresses"+    [ testCase "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]+        (expr, [Qed _]) <- withSolvers Z3 1 Nothing $ \s ->+          verifyContract s c (Just sig) [] defaultVeriOpts Nothing Nothing+        let isSuc (Success {}) = True+            isSuc _ = False+        case filter isSuc (flattenExpr expr) 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"+            assertEqual "balance mismatch" (Var "arg1") ca.balance+            assertEqual "code mismatch" (stripBytecodeMetadata a) (stripBytecodeMetadata code)+            assertEqual "nonce mismatch" (Just 1) ca.nonce+          _ -> assertBool "too many success nodes!" False+    , testCase "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+        assertBool "unexpected cex" (isRight res)+    -- TODO: implement missing aliasing rules+    , expectFail $ testCase "deployed-contract-addresses-cannot-alias" $ do+        Just c <- solcRuntime "C"+          [i|+            contract A {}+            contract C {+              function f() external {+                A a = new A();+                if (address(a) == address(this)) assert(false);+              }+            }+          |]+        res <- reachableUserAsserts c Nothing+        assertBool "should not be able to alias" (isRight res)+    , testCase "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++        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")+    , testCase "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+        assertEqual "should match" arg1 arg2+    -- TODO: fails due to missing aliasing rules+    , expectFail $ testCase "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+        assertBool "unexpected cex" (isRight cexs)+    , testCase "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++        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")+    , testCase "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++        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")+    , testCase "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++        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")+    , testCase "vm.load fails for a potentially aliased address" $ do+        Just c <- solcRuntime "C"+          [i|+            interface Vm {+              function load(address,bytes32) external;+            }+            contract C {+              function f() external {+                Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);+                vm.load(msg.sender, 0x0);+              }+            }+          |]+        (_, [Cex _]) <- withSolvers Z3 1 Nothing $ \s ->+          verifyContract s c Nothing [] defaultVeriOpts Nothing (Just $ checkBadCheatCode "load(address,bytes32)")+        pure ()+    , testCase "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);+              }+            }+          |]+        (_, [Cex _]) <- withSolvers Z3 1 Nothing $ \s ->+          verifyContract s c Nothing [] defaultVeriOpts Nothing (Just $ checkBadCheatCode "store(address,bytes32,bytes32)")+        pure ()+    -- TODO: make this work properly+    , testCase "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+        assertBool "should be partial" (Expr.containsNode isPartial e)+    , testCase "addresses-in-context-are-symbolic" $ do+        Just a <- solcRuntime "A"+          [i|+            contract A {+              function f() external {+                assert(msg.sender != address(0x0));+              }+            }+          |]+        Just b <- solcRuntime "B"+          [i|+            contract B {+              function f() external {+                assert(block.coinbase != address(0x1));+              }+            }+          |]+        Just c <- solcRuntime "C"+          [i|+            contract C {+              function f() external {+                assert(tx.origin != address(0x2));+              }+            }+          |]+        Just d <- solcRuntime "D"+          [i|+            contract D {+              function f() external {+                assert(address(this) != address(0x3));+              }+            }+          |]+        [acex,bcex,ccex,dcex] <- forM [a,b,c,d] $ \con -> do+          Left [cex] <- reachableUserAsserts con Nothing+          assertEqual "wrong number of addresses" 1 (length (Map.keys cex.addrs))+          pure cex++        assertEqual "wrong model for a" (Addr 0) (fromJust $ Map.lookup (SymAddr "caller") acex.addrs)+        assertEqual "wrong model for b" (Addr 1) (fromJust $ Map.lookup (SymAddr "coinbase") bcex.addrs)+        assertEqual "wrong model for c" (Addr 2) (fromJust $ Map.lookup (SymAddr "origin") ccex.addrs)+        assertEqual "wrong model for d" (Addr 3) (fromJust $ Map.lookup (SymAddr "entrypoint") dcex.addrs)+    ]+  , testGroup "Symbolic execution"+      [+     testCase "require-test" $ do+        Just c <- solcRuntime "MyContract"+            [i|+            contract MyContract {+              function fun(int256 a) external pure {+              require(a <= 0);+              assert (a <= 0);+              }+             }+            |]+        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256)" [AbiIntType 256])) [] defaultVeriOpts+        putStrLn "Require works as expected"+     ,+     testCase "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 _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+       putStrLn "expected counterexample found"+     ,+     testCase "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+           }+         }+         |]+       (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+       putStrLn "this should always be true, due to bitwise OR with positive value"+    ,+    -- 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)+    testCase "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(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])+      (res, [Qed _]) <- withSolvers Z3 1 Nothing $ \s ->+        checkAssert s defaultPanicCodes c sig [] defaultVeriOpts+      putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"+     ,+     -- TODO look at tests here for SAR: https://github.com/dapphub/dapptools/blob/01ef8ea418c3fe49089a44d56013d8fcc34a1ec2/src/dapp-tests/pass/constantinople.sol#L250+     testCase "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);+              }+             }+            |]+        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256)" [AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts+        putStrLn "SAR works as expected"+     ,+     testCase "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);+              }+             }+            |]+        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256)" [AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts+        putStrLn "SAR works as expected"+     ,+     testCase "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);+              }+             }+            |]+        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256)" [AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts+        putStrLn "SAR works as expected"+     ,+     testCase "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);+              }+             }+            |]+        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(int256,int256)" [AbiIntType 256, AbiIntType 256])) [] defaultVeriOpts+        putStrLn "SAR works as expected"+     ,+     testCase "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);++              }+            }+            |]+        (_, [Qed _])  <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+        putStrLn "sdiv works as expected"+      ,+     testCase "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);++              }+            }+            |]+        (_, [Qed _])  <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+        putStrLn "sdiv works as expected"+      ,+     testCase "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);++              }+            }+            |]+        (_, [Qed _])  <- withSolvers CVC5 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+        putStrLn "sdiv works as expected"+      ,+     testCase "signed-overflow-checks" $ do+        Just c <- solcRuntime "C"+            [i|+            contract C {+              function fun(int256 a) external returns (int256) {+                  return a + a;+              }+            }+            |]+        (_, [Cex (_, _)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s [0x11] c (Just (Sig "fun(int256)" [AbiIntType 256])) [] defaultVeriOpts+        putStrLn "expected cex discovered"+      ,+     testCase "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+              }+            }+            |]+        (_, [Qed _])  <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+        putStrLn "signextend works as expected"+      ,+     testCase "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);+              }+            }+            |]+        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint8)" [AbiUIntType 256, AbiUIntType 8])) [] defaultVeriOpts+        putStrLn "signextend works as expected"+      ,+      testCase "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+              }+            }+            |]+        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint8)" [AbiUIntType 256, AbiUIntType 8])) [] defaultVeriOpts+        putStrLn "signextend works as expected"+      ,+      -- when b is too large, value is unchanged+      testCase "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);+              }+            }+            |]+        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint8)" [AbiUIntType 256, AbiUIntType 8])) [] defaultVeriOpts+        putStrLn "signextend works as expected"+     ,+     testCase "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);+              }+             }+            |]+        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+        putStrLn "SAR works as expected"+     ,+     testCase "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);+              }+             }+            |]+        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+        putStrLn "XOR works as expected"+      ,+      testCase "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));+              }+             }+            |]+        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+        putStrLn "XOR works as expected"+      ,+      testCase "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 <- withSolvers Z3 1 Nothing $ \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"+            putStrLn $ "y:" <> show y+            putStrLn $ "x:" <> show x+            assertEqual "Addition is not commutative... that's wrong" False True+          (_, [Qed _]) -> do+            putStrLn "adding is commutative"+          _ -> internalError "Unexpected"+      ,+      testCase "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);+              }+            }+            |]+        (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint16)" [AbiUIntType 16])) [] defaultVeriOpts+        putStrLn "DIV by zero is zero"+      ,+      -- Somewhat tautological since we are asserting the precondition+      -- on the same form as the actual "requires" clause.+      testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s ->+          verifyContract s safeAdd sig [] defaultVeriOpts (Just pre) (Just post)+        putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"+     ,++      testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s ->+          verifyContract s safeAdd (Just (Sig "add(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts (Just pre) (Just post)+        putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"+      ,+      testCase "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'+                        _ -> error "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s ->+          verifyContract s c sig [] defaultVeriOpts (Just pre) (Just post)+        putStrLn $ "successfully explored: " <> show (Expr.numBranches 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+        testCase "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()+                          }+                        }+                      }+                    }+                    |]+            Just c <- yulRuntime "Neg" src+            (res, [Qed _]) <- withSolvers Z3 4 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "hello(address)" [AbiAddressType])) [] defaultVeriOpts+            putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"+        ,+        testCase "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])+          (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s ->+            verifyContract s c sig [] defaultVeriOpts (Just pre) (Just post)+          putStrLn "Correct, this can never fail"+        ,+        -- Inspired by these `msg.sender == to` token bugs+        -- which break linearity of totalSupply.+        testCase "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)]) <- withSolvers Z3 1 Nothing $ \s ->+            verifyContract s c sig [] defaultVeriOpts (Just pre) (Just post)+          let x = getVar ctr "arg1"+          let y = getVar ctr "arg2"+          putStrLn $ "y:" <> show y+          putStrLn $ "x:" <> show x+          assertEqual "Catch storage collisions" x y+          putStrLn "expected counterexample found"+        ,+        testCase "simple-assert" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              function foo() external pure {+                assert(false);+              }+             }+            |]+          (_, [Cex (Failure _ _ (Revert msg), _)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo()" [])) [] defaultVeriOpts+          assertEqual "incorrect revert msg" msg (ConcreteBuf $ panicMsg 0x01)+        ,+        testCase "simple-assert-2" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              function foo(uint256 x) external pure {+                assert(x != 10);+              }+             }+            |]+          (_, [(Cex (_, ctr))]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          assertEqual "Must be 10" 10 $ getVar ctr "arg1"+          putStrLn "Got 10 Cex, as expected"+        ,+        testCase "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)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          let ints = map (flip getVar "arg1") [a,b]+          assertBool "0 must be one of the Cex-es" $ isJust $ List.elemIndex 0 ints+          putStrLn "expected 2 counterexamples found, one Cex is the 0 value"+        ,+        testCase "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)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          let x = getVar a "arg1"+          let y = getVar b "arg1"+          assertBool "At least one has to be 0, to go through the first assert" (x == 0 || y == 0)+          putStrLn "expected 2 counterexamples found."+        ,+        testCase "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 _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+          putStrLn "expected 2 counterexamples found"+        ,+        testCase "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)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "fun(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+          assertEqual "Must be 666" 666 $ getVar ctr "arg2"+          putStrLn "Found arg2 Ctx to be 666"+        ,+        -- LSB is zeroed out, byte(31,x) takes LSB, so y==0 always holds+        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLn $ "successfully explored: " <> show (Expr.numBranches 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+        testCase "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)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          assertBool "last byte must be non-zero" $ ((Data.Bits..&.) (getVar ctr "arg1") 0xff) > 0+          putStrLn "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+        testCase "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)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          assertBool "second to last byte must be non-zero" $ ((Data.Bits..&.) (getVar ctr "arg1") 0xff00) > 0+          putStrLn "Expected counterexample found"+        ,+        -- Reverse of thest above+        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"+        ,+        -- Bitwise OR operation test+        testCase "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);+              }+            }+            |]+          (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLn "When OR-ing with full 1's we should get back full 1's"+        ,+        -- Bitwise OR operation test+        testCase "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);+              }+            }+            |]+          (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLn "When OR-ing with a byte of 1's, we should get 1's back there"+        ,+        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "deposit(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"+        ,+        testCase "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)]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s allPanicCodes c (Just (Sig "deposit(uint8)" [AbiUIntType 8])) [] defaultVeriOpts+          assertEqual "Must be 255" 255 $ getVar ctr "arg1"+          putStrLn  $ "expected counterexample found, and it's correct: " <> (show $ getVar ctr "arg1")+        ,+        testCase "explore function dispatch" $ do+          Just c <- solcRuntime "A"+            [i|+            contract A {+              function f(uint x) public pure returns (uint) {+                return x;+              }+            }+            |]+          (res, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"+        ,+        testCase "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);+              }+            }+            |]+          (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+          putStrLn "in bounds byte reads return the expected value"+        ,+        testCase "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);+              }+            }+            |]+          (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "foo(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLn "div/mod/sdiv/smod by zero works as expected during constant propagation"+        ,+        testCase "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);+              }+            }+            |]+          (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+          putStrLn "oob byte reads always return 0"+        ,+        testCase "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 ()+        ,+        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"+        ,+        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"+        ,+        testCase "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)]) <- withSolvers Z3 1 Nothing $ \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"+          assertEqual "x==y for hash collision" x y+          assertEqual "w==z for hash collision" w z+          putStrLn "expected counterexample found"+        ,+        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts+          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"+        ,+        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"+        ,+        testCase "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);+                assembly {+                  x := calldataload(z)+                }+                assert(x == 0);+              }+            }+            |]+          (res, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"+        ,+        testCase "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)]) <- withSolvers Z3 1 Nothing $ \s -> do+            vm <- stToIO $ abstractVM (mkCalldata (Just (Sig "call_A()" [])) []) c Nothing False+                    <&> set (#state % #callvalue) (Lit 0)+                    <&> over (#env % #contracts)+                       (Map.insert aAddr (initialContract (RuntimeCode (ConcreteRuntimeCode a))))+            verify s defaultVeriOpts vm (Just $ checkAssertions defaultPanicCodes)++          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+          assertBool "Did not find expected storage cex" testCex+          putStrLn "expected counterexample found"+        ,+        expectFail $ testCase "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 _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "call_A()" [])) [] defaultVeriOpts+          putStrLn "expected counterexample found"+        ,+        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "kecc(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"+        ,+        testCase "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, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "f(uint256)" [AbiUIntType 256])) [] defaultVeriOpts+          putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths"+        ,+        testCase "safemath-distributivity-yul" $ do+          let yulsafeDistributivity = hex "6355a79a6260003560e01c14156016576015601f565b5b60006000fd60a1565b603d602d604435600435607c565b6039602435600435607c565b605d565b6052604b604435602435605d565b600435607c565b141515605a57fe5b5b565b6000828201821115151560705760006000fd5b82820190505b92915050565b6000818384048302146000841417151560955760006000fd5b82820290505b92915050565b"+          vm <- stToIO $ abstractVM (mkCalldata (Just (Sig "distributivity(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) []) yulsafeDistributivity Nothing False+          (_, [Qed _]) <-  withSolvers Z3 1 Nothing $ \s -> verify s defaultVeriOpts vm (Just $ checkAssertions defaultPanicCodes)+          putStrLn "Proven"+        ,+        testCase "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");+                  }+                }+              }+            |]++          (_, [Qed _]) <- withSolvers CVC5 1 (Just 99999999) $ \s -> checkAssert s defaultPanicCodes c (Just (Sig "distributivity(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts+          putStrLn "Proven"+        ,+        testCase "storage-cex-1" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              uint x;+              uint y;+              function fun(uint256 a) external{+                assert (x == y);+              }+            }+            |]+          (_, [(Cex (_, cex))]) <- withSolvers Z3 1 Nothing $ \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+          assertBool "Did not find expected storage cex" testCex+          putStrLn "Expected counterexample found"+        ,+        testCase "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))]) <- withSolvers Z3 1 Nothing $ \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 -> Map.size s == 2 &&+                                    case (Map.lookup 0 s, Map.lookup (10 + a) s) of+                                      (Just x, Just y) -> x >= y+                                      _ -> False+                          _ -> False+          assertBool "Did not find expected storage cex" testCex+          putStrLn "Expected counterexample found"+        ,+        testCase "storage-cex-concrete" $ do+          Just c <- solcRuntime "C"+            [i|+            contract C {+              uint x;+              uint y;+              function fun(uint256 a) external{+                assert (x != y);+              }+            }+            |]+          let sig = Just (Sig "fun(uint256)" [AbiUIntType 256])+          (_, [Cex (_, cex)]) <- withSolvers Z3 1 Nothing $+            \s -> verifyContract s c sig [] defaultVeriOpts Nothing (Just $ 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+          assertBool "Did not find expected storage cex" testCex+          putStrLn "Expected counterexample found"+  ]+  , testGroup "simplification-working"+  [+    testCase "prop-simp-bool1" $ do+      let+        a = successGen [PAnd (PBool True) (PBool False)]+        b = Expr.simplify a+      assertEqual "Must simplify down" (successGen [PBool False]) b+    , testCase "prop-simp-bool2" $ do+      let+        a = successGen [POr (PBool True) (PBool False)]+        b = Expr.simplify a+      assertEqual "Must simplify down" (successGen []) b+    , testCase "prop-simp-LT" $ do+      let+        a = successGen [PLT (Lit 1) (Lit 2)]+        b = Expr.simplify a+      assertEqual "Must simplify down" (successGen []) b+    , testCase "prop-simp-GEq" $ do+      let+        a = successGen [PGEq (Lit 1) (Lit 2)]+        b = Expr.simplify a+      assertEqual "Must simplify down" (successGen [PBool False]) b+    , testCase "prop-simp-multiple" $ do+      let+        a = successGen [PBool False, PBool True]+        b = Expr.simplify a+      assertEqual "Must simplify down" (successGen [PBool False]) b+    , testCase "prop-simp-expr" $ do+      let+        a = successGen [PEq (Add (Lit 1) (Lit 2)) (Sub (Lit 4) (Lit 1))]+        b = Expr.simplify a+      assertEqual "Must simplify down" (successGen []) b+    , testCase "prop-simp-impl" $ do+      let+        a = successGen [PImpl (PBool False) (PEq (Var "abc") (Var "bcd"))]+        b = Expr.simplify a+      assertEqual "Must simplify down" (successGen []) b+  ]+  , testGroup "equivalence-checking"+    [+      testCase "eq-yul-simple-cex" $ do+        Just aPrgm <- yul ""+          [i|+          {+            calldatacopy(0, 0, 32)+            switch mload(0)+            case 0 { }+            case 1 { }+            default { invalid() }+          }+          |]+        Just bPrgm <- yul ""+          [i|+          {+            calldatacopy(0, 0, 32)+            switch mload(0)+            case 0 { }+            case 2 { }+            default { invalid() }+          }+          |]+        withSolvers Z3 3 Nothing $ \s -> do+          a <- equivalenceCheck s aPrgm bPrgm defaultVeriOpts (mkCalldata Nothing [])+          assertBool "Must have a difference" (any isCex a)+      ,+      testCase "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 Nothing $ \s -> do+          a <- equivalenceCheck s aPrgm bPrgm defaultVeriOpts (mkCalldata Nothing [])+          assertEqual "Must have no difference" [Qed ()] a+      ,+      testCase "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 Nothing $ \s -> do+          a <- equivalenceCheck s aPrgm bPrgm defaultVeriOpts (mkCalldata Nothing [])+          assertBool "Must differ" (all isCex a)+      ,+      -- TODO: this fails because we don't check equivalence of deployed contracts+      expectFail $ testCase "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 Nothing $ \s -> do+          a <- equivalenceCheck s aPrgm bPrgm defaultVeriOpts (mkCalldata Nothing [])+          assertBool "Must differ" (all isCex a)+      ,+      testCase "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 Nothing $ \s -> do+          let cd = mkCalldata (Just (Sig "a(address,address)" [AbiAddressType, AbiAddressType])) []+          a <- equivalenceCheck s aPrgm bPrgm defaultVeriOpts cd+          assertEqual "Must be different" (any isCex a) True+      ,+      testCase "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 Z3 3 Nothing $ \s -> do+          a <- equivalenceCheck s aPrgm bPrgm defaultVeriOpts (mkCalldata Nothing [])+          assertEqual "Must be different" (any isCex a) True+      , testCase "eq-all-yul-optimization-tests" $ do+        let opts = defaultVeriOpts{ 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"++                    -- unexpected symbolic arg --++                    -- OpCreate2+                    , "expressionSimplifier/create2_and_mask.yul"++                    -- OpCreate+                    , "expressionSimplifier/create_and_mask.yul"+                    , "expressionSimplifier/large_byte_access.yul"++                    -- OpMload+                    , "yulOptimizerTests/expressionSplitter/inside_function.yul"+                    , "fullInliner/double_inline.yul"+                    , "fullInliner/inside_condition.yul"+                    , "fullInliner/large_function_multi_use.yul"+                    , "fullInliner/large_function_single_use.yul"+                    , "fullInliner/no_inline_into_big_global_context.yul"+                    , "fullSimplify/invariant.yul"+                    , "fullSuite/abi_example1.yul"+                    , "ssaAndBack/for_loop.yul"+                    , "ssaAndBack/multi_assign_multi_var_if.yul"+                    , "ssaAndBack/multi_assign_multi_var_switch.yul"+                    , "ssaAndBack/two_vars.yul"+                    , "ssaTransform/multi_assign.yul"+                    , "ssaTransform/multi_decl.yul"+                    , "expressionSplitter/inside_function.yul"+                    , "fullSuite/ssaReverseComplex.yul"++                    -- OpMstore+                    , "commonSubexpressionEliminator/function_scopes.yul"+                    , "commonSubexpressionEliminator/variable_for_variable.yul"+                    , "expressionSplitter/trivial.yul"+                    , "fullInliner/multi_return.yul"+                    , "fullSimplify/constant_propagation.yul"+                    , "fullSimplify/identity_rules_complex.yul"+                    , "fullSuite/medium.yul"+                    , "loadResolver/memory_with_msize.yul"+                    , "loadResolver/merge_known_write.yul"+                    , "loadResolver/merge_known_write_with_distance.yul"+                    , "loadResolver/merge_unknown_write.yul"+                    , "loadResolver/reassign_value_expression.yul"+                    , "loadResolver/second_mstore_with_delta.yul"+                    , "loadResolver/second_store_with_delta.yul"+                    , "loadResolver/simple.yul"+                    , "loadResolver/simple_memory.yul"+                    , "fullSuite/ssaReverse.yul"+                    , "rematerialiser/cheap_caller.yul"+                    , "rematerialiser/non_movable_instruction.yul"+                    , "rematerialiser/for_break.yul"+                    , "rematerialiser/for_continue.yul"+                    , "rematerialiser/for_continue_2.yul"+                    , "ssaAndBack/multi_assign.yul"+                    , "ssaAndBack/multi_assign_if.yul"+                    , "ssaAndBack/multi_assign_switch.yul"+                    , "ssaAndBack/simple.yul"+                    , "ssaReverser/simple.yul"+                    , "loopInvariantCodeMotion/simple_storage.yul"++                    -- OpMstore8+                    , "loadResolver/memory_with_different_kinds_of_invalidation.yul"++                    -- OpRevert+                    , "ssaAndBack/ssaReverse.yul"+                    , "redundantAssignEliminator/for_continue_3.yul"+                    , "controlFlowSimplifier/terminating_for_revert.yul"++                    -- invalid test --+                    -- https://github.com/ethereum/solidity/issues/9500+                    , "commonSubexpressionEliminator/object_access.yul"+                    , "expressionSplitter/object_access.yul"+                    , "fullSuite/stack_compressor_msize.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++                    -- wrong number of args --+                    , "wordSizeTransform/functional_instruction.yul"+                    , "wordSizeTransform/if.yul"+                    , "wordSizeTransform/or_bool_renamed.yul"+                    , "wordSizeTransform/switch_1.yul"+                    , "wordSizeTransform/switch_2.yul"+                    , "wordSizeTransform/switch_3.yul"+                    , "wordSizeTransform/switch_4.yul"+                    , "wordSizeTransform/switch_5.yul"+                    , "unusedFunctionParameterPruner/too_many_arguments.yul"++                    -- 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"++                    -- 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"++                    -- TODO check what's wrong with these!+                    , "loadResolver/keccak_short.yul" -- ACTUAL bug -- keccak+                    , "reasoningBasedSimplifier/signed_division.yul" -- ACTUAL bug, SDIV+                    ]++        solcRepo <- fromMaybe (internalError "cannot find solidity repo") <$> (lookupEnv "HEVM_SOLIDITY_REPO")+        let testDir = solcRepo <> "/test/libyul/yulOptimizerTests"+        dircontents <- 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 <- recursiveList fullpaths []+        let filesFiltered = filter (\file -> not $ any (`List.isSubsequenceOf` 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+          origcont <- 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 <- getCurrentTime+          putStrLn $ "Checking file: " <> f+          when opts.debug $ 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 -----------------"+          Just aPrgm <- yul "" $ T.pack $ unlines filteredASym+          Just bPrgm <- yul "" $ T.pack $ unlines filteredBSym+          procs <- getNumProcessors+          withSolvers CVC5 (unsafeInto procs) (Just 100) $ \s -> do+            res <- equivalenceCheck s aPrgm bPrgm opts (mkCalldata Nothing [])+            end <- getCurrentTime+            case any isCex res of+              False -> do+                print $ "OK. Took " <> (show $ diffUTCTime end start) <> " seconds"+                let timeouts = filter isTimeout res+                unless (null timeouts) $ do+                  putStrLn $ "But " <> (show $ length timeouts) <> " timeout(s) occurred"+                  internalError "Encountered timeouts"+              True -> do+                putStrLn $ "Not OK: " <> show f <> " Got: " <> show res+                internalError "Was NOT equivalent"+           )+    ]+  ]+  where+    (===>) = assertSolidityComputation++checkEquivProp :: Prop -> Prop -> IO Bool+checkEquivProp = checkEquivBase (\l r -> PNeg (PImpl l r .&& PImpl r l))++checkEquiv :: (Typeable a) => Expr a -> Expr a -> IO Bool+checkEquiv = checkEquivBase (./=)++checkEquivBase :: Eq a => (a -> a -> Prop) -> a -> a -> IO Bool+checkEquivBase mkprop l r = withSolvers Z3 1 (Just 1) $ \solvers -> do+  if l == r+     then do+       putStrLn "skip"+       pure True+     else do+       let smt = assertPropsNoSimp abstRefineDefault [mkprop l r]+       res <- checkSat solvers smt+       print res+       pure $ case res of+         Unsat -> True+         EVM.Solvers.Unknown -> True+         Sat _ -> False+         Error _ -> False++-- | 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 :: ByteString -> ByteString -> IO (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 :: ByteString -> IO (Maybe (VM RealWorld))+loadVM x = do+  vm <- 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 >> Stepper.run)+       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++hex :: ByteString -> ByteString+hex s =+  case BS16.decodeBase16 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 :: Text -> ByteString -> IO (Maybe ByteString)+runFunction c input = do+  Just x <- singleContract "X" c+  runSimpleVM x input++runStatements+  :: Text -> [AbiValue] -> AbiType+  -> IO (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 s -> [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++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++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+  pure $ C c s b 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 ""))+                     , (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+  , liftM3 ITE subWord subEnd subEnd+  -- TODO Partial+  ]+  where+    subBuf = defaultBuf (sz `div` 2)+    subWord = defaultWord (sz `div` 2)+    subEnd = genEnd (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 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 subWord 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)++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)++-- Used to check for unsimplified expressions+newtype FoundBad = FoundBad { bad :: Bool } deriving (Show)+initFoundBad :: FoundBad+initFoundBad = FoundBad { bad = False }++-- Finds SLoad -> SStore. This should not occur in most scenarios+-- as we can simplify them away+badStoresInExpr :: Expr a -> Bool+badStoresInExpr expr = bad+  where+    FoundBad bad = execState (mapExprM findBadStore expr) initFoundBad+    findBadStore :: Expr a-> State FoundBad (Expr a)+    findBadStore e = case e of+      (SLoad _ (SStore _ _ _)) -> do+        put (FoundBad { bad = True })+        pure e+      _ -> pure e++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 subWord (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)++genStorage :: Int -> Gen (Expr Storage)+genStorage 0 = oneof+  [ fmap AbstractStore arbitrary+  , fmap ConcreteStore arbitrary+  ]+genStorage sz = liftM3 SStore subWord subWord subStore+  where+    subStore = genStorage (sz `div` 10)+    subWord = defaultWord (sz `div` 5)++data Invocation+  = SolidityCall Text [AbiValue]+  deriving Show++assertSolidityComputation :: Invocation -> AbiValue -> IO ()+assertSolidityComputation (SolidityCall s args) x =+  do y <- runStatements s args (abiValueType x)+     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 s+checkBadCheatCode sig _ = \case+  (Failure _ _ (BadCheatCode s)) -> (ConcreteBuf $ into s.unFunctionSelector) ./= (ConcreteBuf $ selector sig)+  _ -> PBool True++allBranchesFail :: ByteString -> Maybe Sig -> IO (Either [SMTCex] (Expr End))+allBranchesFail = checkPost (Just p)+  where+    p _ = \case+      Success _ _ _ _ -> PBool False+      _ -> PBool True++reachableUserAsserts :: ByteString -> Maybe Sig -> IO (Either [SMTCex] (Expr End))+reachableUserAsserts = checkPost (Just $ checkAssertions [0x01])++checkPost :: Maybe (Postcondition RealWorld) -> ByteString -> Maybe Sig -> IO (Either [SMTCex] (Expr End))+checkPost post c sig = do+  (e, res) <- withSolvers Z3 1 Nothing $ \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