hevm 0.50.2 → 0.50.3
raw patch · 32 files changed
+2069/−1577 lines, 32 filesdep +spawndep +stmdep +tasty-benchdep ~basedep ~bytestringdep ~herePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: spawn, stm, tasty-bench
Dependency ranges changed: base, bytestring, here, tasty, text
API changes (from Hackage documentation)
- EVM: [_accessedAddresses] :: SubState -> Set Addr
- EVM: [_accessedStorageKeys] :: SubState -> Set (Addr, W256)
- EVM: [_allowFFI] :: VM -> Bool
- EVM: [_balance] :: Contract -> W256
- EVM: [_baseFee] :: Block -> W256
- EVM: [_block] :: VM -> Block
- EVM: [_burned] :: VM -> Word64
- EVM: [_cache] :: VM -> Cache
- EVM: [_calldata] :: FrameState -> Expr Buf
- EVM: [_caller] :: FrameState -> Expr EWord
- EVM: [_callvalue] :: FrameState -> Expr EWord
- EVM: [_chainId] :: Env -> W256
- EVM: [_codeContract] :: FrameState -> Addr
- EVM: [_codeOps] :: Contract -> Vector (Int, Op)
- EVM: [_code] :: FrameState -> ContractCode
- EVM: [_codehash] :: Contract -> Expr EWord
- EVM: [_coinbase] :: Block -> Addr
- EVM: [_constraints] :: VM -> [Prop]
- EVM: [_contract] :: FrameState -> Addr
- EVM: [_contractcode] :: Contract -> ContractCode
- EVM: [_contracts] :: Env -> Map Addr Contract
- EVM: [_env] :: VM -> Env
- EVM: [_external] :: Contract -> Bool
- EVM: [_fetchedContracts] :: Cache -> Map Addr Contract
- EVM: [_fetchedStorage] :: Cache -> Map W256 (Map W256 W256)
- EVM: [_frameContext] :: Frame -> FrameContext
- EVM: [_frameState] :: Frame -> FrameState
- EVM: [_frames] :: VM -> [Frame]
- EVM: [_gas] :: FrameState -> Word64
- EVM: [_gaslimit] :: Block -> Word64
- EVM: [_gasprice] :: TxState -> W256
- EVM: [_isCreate] :: TxState -> Bool
- EVM: [_iterations] :: VM -> Map CodeLocation Int
- EVM: [_keccakEqs] :: VM -> [Prop]
- EVM: [_logs] :: VM -> [Expr Log]
- EVM: [_maxCodeSize] :: Block -> W256
- EVM: [_memorySize] :: FrameState -> Word64
- EVM: [_memory] :: FrameState -> Expr Buf
- EVM: [_nonce] :: Contract -> W256
- EVM: [_number] :: Block -> W256
- EVM: [_opIxMap] :: Contract -> Vector Int
- EVM: [_origStorage] :: Env -> Map W256 (Map W256 W256)
- EVM: [_origin] :: TxState -> Addr
- EVM: [_path] :: Cache -> Map (CodeLocation, Int) Bool
- EVM: [_pc] :: FrameState -> Int
- EVM: [_prevRandao] :: Block -> W256
- EVM: [_refunds] :: SubState -> [(Addr, Word64)]
- EVM: [_result] :: VM -> Maybe VMResult
- EVM: [_returndata] :: FrameState -> Expr Buf
- EVM: [_schedule] :: Block -> FeeSchedule Word64
- EVM: [_selfdestructs] :: SubState -> [Addr]
- EVM: [_sha3Crack] :: Env -> Map W256 ByteString
- EVM: [_stack] :: FrameState -> [Expr EWord]
- EVM: [_state] :: VM -> FrameState
- EVM: [_static] :: FrameState -> Bool
- EVM: [_storage] :: Env -> Expr Storage
- EVM: [_substate] :: TxState -> SubState
- EVM: [_timestamp] :: Block -> Expr EWord
- EVM: [_toAddr] :: TxState -> Addr
- EVM: [_touchedAccounts] :: SubState -> [Addr]
- EVM: [_traceContract] :: Trace -> Contract
- EVM: [_traceData] :: Trace -> TraceData
- EVM: [_traceOpIx] :: Trace -> Int
- EVM: [_traces] :: VM -> TreePos Empty Trace
- EVM: [_txPriorityFee] :: TxState -> W256
- EVM: [_txReversion] :: TxState -> Map Addr Contract
- EVM: [_tx] :: VM -> TxState
- EVM: [_txgaslimit] :: TxState -> Word64
- EVM: [_value] :: TxState -> Expr EWord
- EVM: [callContextAbi] :: FrameContext -> Maybe W256
- EVM: [callContextCodehash] :: FrameContext -> Expr EWord
- EVM: [callContextContext] :: FrameContext -> Addr
- EVM: [callContextData] :: FrameContext -> Expr Buf
- EVM: [callContextOffset] :: FrameContext -> W256
- EVM: [callContextReversion] :: FrameContext -> (Map Addr Contract, Expr Storage)
- EVM: [callContextSize] :: FrameContext -> W256
- EVM: [callContextSubState] :: FrameContext -> SubState
- EVM: [callContextTarget] :: FrameContext -> Addr
- EVM: [creationContextAddress] :: FrameContext -> Addr
- EVM: [creationContextCodehash] :: FrameContext -> Expr EWord
- EVM: [creationContextReversion] :: FrameContext -> Map Addr Contract
- EVM: [creationContextSubstate] :: FrameContext -> SubState
- EVM: [vmoptAddress] :: VMOpts -> Addr
- EVM: [vmoptAllowFFI] :: VMOpts -> Bool
- EVM: [vmoptBaseFee] :: VMOpts -> W256
- EVM: [vmoptBlockGaslimit] :: VMOpts -> Word64
- EVM: [vmoptCalldata] :: VMOpts -> (Expr Buf, [Prop])
- EVM: [vmoptCaller] :: VMOpts -> Expr EWord
- EVM: [vmoptChainId] :: VMOpts -> W256
- EVM: [vmoptCoinbase] :: VMOpts -> Addr
- EVM: [vmoptContract] :: VMOpts -> Contract
- EVM: [vmoptCreate] :: VMOpts -> Bool
- EVM: [vmoptGas] :: VMOpts -> Word64
- EVM: [vmoptGaslimit] :: VMOpts -> Word64
- EVM: [vmoptGasprice] :: VMOpts -> W256
- EVM: [vmoptMaxCodeSize] :: VMOpts -> W256
- EVM: [vmoptNumber] :: VMOpts -> W256
- EVM: [vmoptOrigin] :: VMOpts -> Addr
- EVM: [vmoptPrevRandao] :: VMOpts -> W256
- EVM: [vmoptPriorityFee] :: VMOpts -> W256
- EVM: [vmoptSchedule] :: VMOpts -> FeeSchedule Word64
- EVM: [vmoptStorageBase] :: VMOpts -> StorageBase
- EVM: [vmoptTimestamp] :: VMOpts -> Expr EWord
- EVM: [vmoptTxAccessList] :: VMOpts -> Map Addr [W256]
- EVM: [vmoptValue] :: VMOpts -> Expr EWord
- EVM.Dapp: [_contextEnv] :: DappContext -> Map Addr Contract
- EVM.Dapp: [_contextInfo] :: DappContext -> DappInfo
- EVM.Dapp: [_dappAbiMap] :: DappInfo -> Map Word32 Method
- EVM.Dapp: [_dappAstIdMap] :: DappInfo -> Map Int Value
- EVM.Dapp: [_dappAstSrcMap] :: DappInfo -> SrcMap -> Maybe Value
- EVM.Dapp: [_dappErrorMap] :: DappInfo -> Map W256 SolError
- EVM.Dapp: [_dappEventMap] :: DappInfo -> Map W256 Event
- EVM.Dapp: [_dappRoot] :: DappInfo -> FilePath
- EVM.Dapp: [_dappSolcByCode] :: DappInfo -> [(Code, SolcContract)]
- EVM.Dapp: [_dappSolcByHash] :: DappInfo -> Map W256 (CodeType, SolcContract)
- EVM.Dapp: [_dappSolcByName] :: DappInfo -> Map Text SolcContract
- EVM.Dapp: [_dappSources] :: DappInfo -> SourceCache
- EVM.Dapp: [_dappUnitTests] :: DappInfo -> [(Text, [(Test, [AbiType])])]
- EVM.Dapp: [immutableLocations] :: Code -> [Reference]
- EVM.Dapp: [raw] :: Code -> ByteString
- EVM.Dapp: contextEnv :: Lens' DappContext (Map Addr Contract)
- EVM.Dapp: contextInfo :: Lens' DappContext DappInfo
- EVM.Dapp: dappAbiMap :: Lens' DappInfo (Map Word32 Method)
- EVM.Dapp: dappAstIdMap :: Lens' DappInfo (Map Int Value)
- EVM.Dapp: dappAstSrcMap :: Lens' DappInfo (SrcMap -> Maybe Value)
- EVM.Dapp: dappErrorMap :: Lens' DappInfo (Map W256 SolError)
- EVM.Dapp: dappEventMap :: Lens' DappInfo (Map W256 Event)
- EVM.Dapp: dappRoot :: Lens' DappInfo FilePath
- EVM.Dapp: dappSolcByCode :: Lens' DappInfo [(Code, SolcContract)]
- EVM.Dapp: dappSolcByHash :: Lens' DappInfo (Map W256 (CodeType, SolcContract))
- EVM.Dapp: dappSolcByName :: Lens' DappInfo (Map Text SolcContract)
- EVM.Dapp: dappSources :: Lens' DappInfo SourceCache
- EVM.Dapp: dappUnitTests :: Lens' DappInfo [(Text, [(Test, [AbiType])])]
- EVM.Facts: [addr] :: Fact -> Addr
- EVM.Facts: [blob] :: Fact -> ByteString
- EVM.Facts: [dataASCII] :: Data -> ASCII
- EVM.Facts: [fileData] :: File -> Data
- EVM.Facts: [filePath] :: File -> Path
- EVM.Facts: [what] :: Fact -> W256
- EVM.Facts: [which] :: Fact -> W256
- EVM.FeeSchedule: [g_access_list_address] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_access_list_storage_key] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_balance] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_base] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_blockhash] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_call] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_callstipend] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_callvalue] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_codedeposit] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_cold_account_access] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_cold_sload] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_copy] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_create] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_ecadd] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_ecmul] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_exp] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_expbyte] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_extcode] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_extcodehash] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_fround] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_high] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_jumpdest] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_log] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_logdata] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_logtopic] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_low] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_memory] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_mid] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_newaccount] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_pairing_base] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_pairing_point] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_quaddivisor] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_selfdestruct] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_selfdestruct_newaccount] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_sha3] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_sha3word] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_sload] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_sreset] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_sset] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_transaction] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_txcreate] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_txdatanonzero] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_txdatazero] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_verylow] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_warm_storage_read] :: FeeSchedule n -> n
- EVM.FeeSchedule: [g_zero] :: FeeSchedule n -> n
- EVM.FeeSchedule: [r_block] :: FeeSchedule n -> n
- EVM.FeeSchedule: [r_sclear] :: FeeSchedule n -> n
- EVM.FeeSchedule: [r_selfdestruct] :: FeeSchedule n -> n
- EVM.SMT: Bitwuzla :: Solver
- EVM.SMT: CVC5 :: Solver
- EVM.SMT: Custom :: Text -> Solver
- EVM.SMT: Error :: Text -> CheckSatResult
- EVM.SMT: Sat :: SMTCex -> CheckSatResult
- EVM.SMT: SolverGroup :: Chan Task -> SolverGroup
- EVM.SMT: SolverInstance :: Solver -> Handle -> Handle -> Handle -> ProcessHandle -> SolverInstance
- EVM.SMT: Task :: SMT2 -> Chan CheckSatResult -> Task
- EVM.SMT: Unknown :: CheckSatResult
- EVM.SMT: Unsat :: CheckSatResult
- EVM.SMT: Z3 :: Solver
- EVM.SMT: [_process] :: SolverInstance -> ProcessHandle
- EVM.SMT: [_stderr] :: SolverInstance -> Handle
- EVM.SMT: [_stdin] :: SolverInstance -> Handle
- EVM.SMT: [_stdout] :: SolverInstance -> Handle
- EVM.SMT: [_type] :: SolverInstance -> Solver
- EVM.SMT: [blockContextV] :: CexVars -> [Text]
- EVM.SMT: [blockContext] :: SMTCex -> Map (Expr EWord) W256
- EVM.SMT: [buffersV] :: CexVars -> [Text]
- EVM.SMT: [buffers] :: SMTCex -> Map (Expr Buf) ByteString
- EVM.SMT: [calldataV] :: CexVars -> [Text]
- EVM.SMT: [resultChan] :: Task -> Chan CheckSatResult
- EVM.SMT: [script] :: Task -> SMT2
- EVM.SMT: [txContextV] :: CexVars -> [Text]
- EVM.SMT: [txContext] :: SMTCex -> Map (Expr EWord) W256
- EVM.SMT: [vars] :: SMTCex -> Map (Expr EWord) W256
- EVM.SMT: checkSat :: SolverGroup -> SMT2 -> IO CheckSatResult
- EVM.SMT: data CheckSatResult
- EVM.SMT: data Solver
- EVM.SMT: data SolverInstance
- EVM.SMT: data Task
- EVM.SMT: getValue :: SolverInstance -> Text -> IO Text
- EVM.SMT: instance GHC.Classes.Eq EVM.SMT.CheckSatResult
- EVM.SMT: instance GHC.Show.Show EVM.SMT.CheckSatResult
- EVM.SMT: instance GHC.Show.Show EVM.SMT.Solver
- EVM.SMT: isErr :: CheckSatResult -> Bool
- EVM.SMT: isSat :: CheckSatResult -> Bool
- EVM.SMT: isUnsat :: CheckSatResult -> Bool
- EVM.SMT: newtype SolverGroup
- EVM.SMT: readSExpr :: Handle -> IO [Text]
- EVM.SMT: sendCommand :: SolverInstance -> Text -> IO Text
- EVM.SMT: sendLine :: SolverInstance -> Text -> IO Text
- EVM.SMT: sendLine' :: SolverInstance -> Text -> IO ()
- EVM.SMT: sendScript :: SolverInstance -> SMT2 -> IO (Either Text ())
- EVM.SMT: solverArgs :: Solver -> Maybe Natural -> [Text]
- EVM.SMT: spawnSolver :: Solver -> Maybe Natural -> IO SolverInstance
- EVM.SMT: stopSolver :: SolverInstance -> IO ()
- EVM.SMT: withSolvers :: Solver -> Natural -> Maybe Natural -> (SolverGroup -> IO a) -> IO a
- EVM.Solidity: [_abiMap] :: SolcContract -> Map Word32 Method
- EVM.Solidity: [_constructorInputs] :: SolcContract -> [(Text, AbiType)]
- EVM.Solidity: [_contractName] :: SolcContract -> Text
- EVM.Solidity: [_creationCode] :: SolcContract -> ByteString
- EVM.Solidity: [_creationCodehash] :: SolcContract -> W256
- EVM.Solidity: [_creationSrcmap] :: SolcContract -> Seq SrcMap
- EVM.Solidity: [_errorMap] :: SolcContract -> Map W256 SolError
- EVM.Solidity: [_eventMap] :: SolcContract -> Map W256 Event
- EVM.Solidity: [_immutableReferences] :: SolcContract -> Map W256 [Reference]
- EVM.Solidity: [_methodInputs] :: Method -> [(Text, AbiType)]
- EVM.Solidity: [_methodMutability] :: Method -> Mutability
- EVM.Solidity: [_methodName] :: Method -> Text
- EVM.Solidity: [_methodOutput] :: Method -> [(Text, AbiType)]
- EVM.Solidity: [_methodSignature] :: Method -> Text
- EVM.Solidity: [_offset] :: StorageItem -> Int
- EVM.Solidity: [_refLength] :: Reference -> Int
- EVM.Solidity: [_refStart] :: Reference -> Int
- EVM.Solidity: [_runtimeCode] :: SolcContract -> ByteString
- EVM.Solidity: [_runtimeCodehash] :: SolcContract -> W256
- EVM.Solidity: [_runtimeSrcmap] :: SolcContract -> Seq SrcMap
- EVM.Solidity: [_slot] :: StorageItem -> Int
- EVM.Solidity: [_sourceAsts] :: SourceCache -> Map Text Value
- EVM.Solidity: [_sourceFiles] :: SourceCache -> [(Text, ByteString)]
- EVM.Solidity: [_sourceLines] :: SourceCache -> [Vector ByteString]
- EVM.Solidity: [_storageLayout] :: SolcContract -> Maybe (Map Text StorageItem)
- EVM.Solidity: [_type] :: StorageItem -> SlotType
- EVM.Solidity: [srcMapFile] :: SrcMap -> {-# UNPACK #-}Int
- EVM.Solidity: [srcMapJump] :: SrcMap -> JumpType
- EVM.Solidity: [srcMapLength] :: SrcMap -> {-# UNPACK #-}Int
- EVM.Solidity: [srcMapModifierDepth] :: SrcMap -> {-# UNPACK #-}Int
- EVM.Solidity: [srcMapOffset] :: SrcMap -> {-# UNPACK #-}Int
- EVM.Solidity: abiMap :: Lens' SolcContract (Map Word32 Method)
- EVM.Solidity: constructorInputs :: Lens' SolcContract [(Text, AbiType)]
- EVM.Solidity: contractName :: Lens' SolcContract Text
- EVM.Solidity: creationCode :: Lens' SolcContract ByteString
- EVM.Solidity: creationCodehash :: Lens' SolcContract W256
- EVM.Solidity: creationSrcmap :: Lens' SolcContract (Seq SrcMap)
- EVM.Solidity: errorMap :: Lens' SolcContract (Map W256 SolError)
- EVM.Solidity: eventMap :: Lens' SolcContract (Map W256 Event)
- EVM.Solidity: immutableReferences :: Lens' SolcContract (Map W256 [Reference])
- EVM.Solidity: methodInputs :: Lens' Method [(Text, AbiType)]
- EVM.Solidity: methodMutability :: Lens' Method Mutability
- EVM.Solidity: methodName :: Lens' Method Text
- EVM.Solidity: methodOutput :: Lens' Method [(Text, AbiType)]
- EVM.Solidity: methodSignature :: Lens' Method Text
- EVM.Solidity: runtimeCode :: Lens' SolcContract ByteString
- EVM.Solidity: runtimeCodehash :: Lens' SolcContract W256
- EVM.Solidity: runtimeSrcmap :: Lens' SolcContract (Seq SrcMap)
- EVM.Solidity: sourceAsts :: Lens' SourceCache (Map Text Value)
- EVM.Solidity: sourceFiles :: Lens' SourceCache [(Text, ByteString)]
- EVM.Solidity: sourceLines :: Lens' SourceCache [Vector ByteString]
- EVM.Solidity: storageLayout :: Lens' SolcContract (Maybe (Map Text StorageItem))
- EVM.SymExec: [askSmtIters] :: VeriOpts -> Maybe Integer
- EVM.SymExec: [debug] :: VeriOpts -> Bool
- EVM.SymExec: [maxIter] :: VeriOpts -> Maybe Integer
- EVM.SymExec: [rpcInfo] :: VeriOpts -> RpcInfo
- EVM.SymExec: [simp] :: VeriOpts -> Bool
- EVM.SymExec: containsA :: Eq a => Eq b => Eq c => ProofResult a b c -> [(d, e, ProofResult a b c)] -> Bool
- EVM.SymExec: type EquivalenceResult = ProofResult ([VM], [VM]) VM ()
- EVM.TTY: [_browserContractList] :: UiBrowserState -> List Name (Addr, Contract)
- EVM.TTY: [_browserVm] :: UiBrowserState -> UiVmState
- EVM.TTY: [_testOpts] :: UiTestPickerState -> UnitTestOptions
- EVM.TTY: [_testPickerDapp] :: UiTestPickerState -> DappInfo
- EVM.TTY: [_testPickerList] :: UiTestPickerState -> List Name (Text, Text)
- EVM.TTY: [_uiShowMemory] :: UiVmState -> Bool
- EVM.TTY: [_uiSnapshots] :: UiVmState -> Map Int (VM, Stepper ())
- EVM.TTY: [_uiStep] :: UiVmState -> Int
- EVM.TTY: [_uiStepper] :: UiVmState -> Stepper ()
- EVM.TTY: [_uiTestOpts] :: UiVmState -> UnitTestOptions
- EVM.TTY: [_uiVm] :: UiVmState -> VM
- EVM.Transaction: [accessAddress] :: AccessListEntry -> Addr
- EVM.Transaction: [accessStorageKeys] :: AccessListEntry -> [W256]
- EVM.Transaction: [txAccessList] :: Transaction -> [AccessListEntry]
- EVM.Transaction: [txData] :: Transaction -> ByteString
- EVM.Transaction: [txGasLimit] :: Transaction -> Word64
- EVM.Transaction: [txGasPrice] :: Transaction -> Maybe W256
- EVM.Transaction: [txMaxFeePerGas] :: Transaction -> Maybe W256
- EVM.Transaction: [txMaxPriorityFeeGas] :: Transaction -> Maybe W256
- EVM.Transaction: [txNonce] :: Transaction -> W256
- EVM.Transaction: [txR] :: Transaction -> W256
- EVM.Transaction: [txS] :: Transaction -> W256
- EVM.Transaction: [txToAddr] :: Transaction -> Maybe Addr
- EVM.Transaction: [txType] :: Transaction -> TxType
- EVM.Transaction: [txV] :: Transaction -> W256
- EVM.Transaction: [txValue] :: Transaction -> W256
- EVM.Types: [addressWord160] :: Addr -> Word160
- EVM.UnitTest: [askSmtIters] :: UnitTestOptions -> Maybe Integer
- EVM.UnitTest: [covMatch] :: UnitTestOptions -> Maybe Text
- EVM.UnitTest: [dapp] :: UnitTestOptions -> DappInfo
- EVM.UnitTest: [ffiAllowed] :: UnitTestOptions -> Bool
- EVM.UnitTest: [fuzzRuns] :: UnitTestOptions -> Int
- EVM.UnitTest: [match] :: UnitTestOptions -> Text
- EVM.UnitTest: [maxDepth] :: UnitTestOptions -> Maybe Int
- EVM.UnitTest: [maxIter] :: UnitTestOptions -> Maybe Integer
- EVM.UnitTest: [replay] :: UnitTestOptions -> Maybe (Text, ByteString)
- EVM.UnitTest: [rpcInfo] :: UnitTestOptions -> RpcInfo
- EVM.UnitTest: [smtDebug] :: UnitTestOptions -> Bool
- EVM.UnitTest: [smtTimeout] :: UnitTestOptions -> Maybe Natural
- EVM.UnitTest: [solver] :: UnitTestOptions -> Maybe Text
- EVM.UnitTest: [solvers] :: UnitTestOptions -> SolverGroup
- EVM.UnitTest: [srcContract] :: OpLocation -> Contract
- EVM.UnitTest: [srcOpIx] :: OpLocation -> Int
- EVM.UnitTest: [testAddress] :: TestVMParams -> Addr
- EVM.UnitTest: [testBalanceCreate] :: TestVMParams -> W256
- EVM.UnitTest: [testBaseFee] :: TestVMParams -> W256
- EVM.UnitTest: [testCaller] :: TestVMParams -> Addr
- EVM.UnitTest: [testChainId] :: TestVMParams -> W256
- EVM.UnitTest: [testCoinbase] :: TestVMParams -> Addr
- EVM.UnitTest: [testGasCall] :: TestVMParams -> Word64
- EVM.UnitTest: [testGasCreate] :: TestVMParams -> Word64
- EVM.UnitTest: [testGaslimit] :: TestVMParams -> Word64
- EVM.UnitTest: [testGasprice] :: TestVMParams -> W256
- EVM.UnitTest: [testMaxCodeSize] :: TestVMParams -> W256
- EVM.UnitTest: [testNumber] :: TestVMParams -> W256
- EVM.UnitTest: [testOrigin] :: TestVMParams -> Addr
- EVM.UnitTest: [testParams] :: UnitTestOptions -> TestVMParams
- EVM.UnitTest: [testPrevrandao] :: TestVMParams -> W256
- EVM.UnitTest: [testPriorityFee] :: TestVMParams -> W256
- EVM.UnitTest: [testTimestamp] :: TestVMParams -> W256
- EVM.UnitTest: [verbose] :: UnitTestOptions -> Maybe Int
- EVM.UnitTest: [vmModifier] :: UnitTestOptions -> VM -> VM
+ EVM: [$sel:_accessedAddresses:SubState] :: SubState -> Set Addr
+ EVM: [$sel:_accessedStorageKeys:SubState] :: SubState -> Set (Addr, W256)
+ EVM: [$sel:_allowFFI:VM] :: VM -> Bool
+ EVM: [$sel:_balance:Contract] :: Contract -> W256
+ EVM: [$sel:_baseFee:Block] :: Block -> W256
+ EVM: [$sel:_block:VM] :: VM -> Block
+ EVM: [$sel:_burned:VM] :: VM -> Word64
+ EVM: [$sel:_cache:VM] :: VM -> Cache
+ EVM: [$sel:_calldata:FrameState] :: FrameState -> Expr Buf
+ EVM: [$sel:_caller:FrameState] :: FrameState -> Expr EWord
+ EVM: [$sel:_callvalue:FrameState] :: FrameState -> Expr EWord
+ EVM: [$sel:_chainId:Env] :: Env -> W256
+ EVM: [$sel:_code:FrameState] :: FrameState -> ContractCode
+ EVM: [$sel:_codeContract:FrameState] :: FrameState -> Addr
+ EVM: [$sel:_codeOps:Contract] :: Contract -> Vector (Int, Op)
+ EVM: [$sel:_codehash:Contract] :: Contract -> Expr EWord
+ EVM: [$sel:_coinbase:Block] :: Block -> Addr
+ EVM: [$sel:_constraints:VM] :: VM -> [Prop]
+ EVM: [$sel:_contract:FrameState] :: FrameState -> Addr
+ EVM: [$sel:_contractcode:Contract] :: Contract -> ContractCode
+ EVM: [$sel:_contracts:Env] :: Env -> Map Addr Contract
+ EVM: [$sel:_env:VM] :: VM -> Env
+ EVM: [$sel:_external:Contract] :: Contract -> Bool
+ EVM: [$sel:_fetchedContracts:Cache] :: Cache -> Map Addr Contract
+ EVM: [$sel:_fetchedStorage:Cache] :: Cache -> Map W256 (Map W256 W256)
+ EVM: [$sel:_frameContext:Frame] :: Frame -> FrameContext
+ EVM: [$sel:_frameState:Frame] :: Frame -> FrameState
+ EVM: [$sel:_frames:VM] :: VM -> [Frame]
+ EVM: [$sel:_gas:FrameState] :: FrameState -> Word64
+ EVM: [$sel:_gaslimit:Block] :: Block -> Word64
+ EVM: [$sel:_gasprice:TxState] :: TxState -> W256
+ EVM: [$sel:_isCreate:TxState] :: TxState -> Bool
+ EVM: [$sel:_iterations:VM] :: VM -> Map CodeLocation Int
+ EVM: [$sel:_keccakEqs:VM] :: VM -> [Prop]
+ EVM: [$sel:_logs:VM] :: VM -> [Expr Log]
+ EVM: [$sel:_maxCodeSize:Block] :: Block -> W256
+ EVM: [$sel:_memory:FrameState] :: FrameState -> Expr Buf
+ EVM: [$sel:_memorySize:FrameState] :: FrameState -> Word64
+ EVM: [$sel:_nonce:Contract] :: Contract -> W256
+ EVM: [$sel:_number:Block] :: Block -> W256
+ EVM: [$sel:_opIxMap:Contract] :: Contract -> Vector Int
+ EVM: [$sel:_origStorage:Env] :: Env -> Map W256 (Map W256 W256)
+ EVM: [$sel:_origin:TxState] :: TxState -> Addr
+ EVM: [$sel:_overrideCaller:VM] :: VM -> Maybe (Expr EWord)
+ EVM: [$sel:_path:Cache] :: Cache -> Map (CodeLocation, Int) Bool
+ EVM: [$sel:_pc:FrameState] :: FrameState -> Int
+ EVM: [$sel:_prevRandao:Block] :: Block -> W256
+ EVM: [$sel:_refunds:SubState] :: SubState -> [(Addr, Word64)]
+ EVM: [$sel:_result:VM] :: VM -> Maybe VMResult
+ EVM: [$sel:_returndata:FrameState] :: FrameState -> Expr Buf
+ EVM: [$sel:_schedule:Block] :: Block -> FeeSchedule Word64
+ EVM: [$sel:_selfdestructs:SubState] :: SubState -> [Addr]
+ EVM: [$sel:_sha3Crack:Env] :: Env -> Map W256 ByteString
+ EVM: [$sel:_stack:FrameState] :: FrameState -> [Expr EWord]
+ EVM: [$sel:_state:VM] :: VM -> FrameState
+ EVM: [$sel:_static:FrameState] :: FrameState -> Bool
+ EVM: [$sel:_storage:Env] :: Env -> Expr Storage
+ EVM: [$sel:_substate:TxState] :: TxState -> SubState
+ EVM: [$sel:_timestamp:Block] :: Block -> Expr EWord
+ EVM: [$sel:_toAddr:TxState] :: TxState -> Addr
+ EVM: [$sel:_touchedAccounts:SubState] :: SubState -> [Addr]
+ EVM: [$sel:_traceContract:Trace] :: Trace -> Contract
+ EVM: [$sel:_traceData:Trace] :: Trace -> TraceData
+ EVM: [$sel:_traceOpIx:Trace] :: Trace -> Int
+ EVM: [$sel:_traces:VM] :: VM -> TreePos Empty Trace
+ EVM: [$sel:_tx:VM] :: VM -> TxState
+ EVM: [$sel:_txPriorityFee:TxState] :: TxState -> W256
+ EVM: [$sel:_txReversion:TxState] :: TxState -> Map Addr Contract
+ EVM: [$sel:_txgaslimit:TxState] :: TxState -> Word64
+ EVM: [$sel:_value:TxState] :: TxState -> Expr EWord
+ EVM: [$sel:callContextAbi:CreationContext] :: FrameContext -> Maybe W256
+ EVM: [$sel:callContextCodehash:CreationContext] :: FrameContext -> Expr EWord
+ EVM: [$sel:callContextContext:CreationContext] :: FrameContext -> Addr
+ EVM: [$sel:callContextData:CreationContext] :: FrameContext -> Expr Buf
+ EVM: [$sel:callContextOffset:CreationContext] :: FrameContext -> W256
+ EVM: [$sel:callContextReversion:CreationContext] :: FrameContext -> (Map Addr Contract, Expr Storage)
+ EVM: [$sel:callContextSize:CreationContext] :: FrameContext -> W256
+ EVM: [$sel:callContextSubState:CreationContext] :: FrameContext -> SubState
+ EVM: [$sel:callContextTarget:CreationContext] :: FrameContext -> Addr
+ EVM: [$sel:creationContextAddress:CreationContext] :: FrameContext -> Addr
+ EVM: [$sel:creationContextCodehash:CreationContext] :: FrameContext -> Expr EWord
+ EVM: [$sel:creationContextReversion:CreationContext] :: FrameContext -> Map Addr Contract
+ EVM: [$sel:creationContextSubstate:CreationContext] :: FrameContext -> SubState
+ EVM: [$sel:vmoptAddress:VMOpts] :: VMOpts -> Addr
+ EVM: [$sel:vmoptAllowFFI:VMOpts] :: VMOpts -> Bool
+ EVM: [$sel:vmoptBaseFee:VMOpts] :: VMOpts -> W256
+ EVM: [$sel:vmoptBlockGaslimit:VMOpts] :: VMOpts -> Word64
+ EVM: [$sel:vmoptCalldata:VMOpts] :: VMOpts -> (Expr Buf, [Prop])
+ EVM: [$sel:vmoptCaller:VMOpts] :: VMOpts -> Expr EWord
+ EVM: [$sel:vmoptChainId:VMOpts] :: VMOpts -> W256
+ EVM: [$sel:vmoptCoinbase:VMOpts] :: VMOpts -> Addr
+ EVM: [$sel:vmoptContract:VMOpts] :: VMOpts -> Contract
+ EVM: [$sel:vmoptCreate:VMOpts] :: VMOpts -> Bool
+ EVM: [$sel:vmoptGas:VMOpts] :: VMOpts -> Word64
+ EVM: [$sel:vmoptGaslimit:VMOpts] :: VMOpts -> Word64
+ EVM: [$sel:vmoptGasprice:VMOpts] :: VMOpts -> W256
+ EVM: [$sel:vmoptMaxCodeSize:VMOpts] :: VMOpts -> W256
+ EVM: [$sel:vmoptNumber:VMOpts] :: VMOpts -> W256
+ EVM: [$sel:vmoptOrigin:VMOpts] :: VMOpts -> Addr
+ EVM: [$sel:vmoptPrevRandao:VMOpts] :: VMOpts -> W256
+ EVM: [$sel:vmoptPriorityFee:VMOpts] :: VMOpts -> W256
+ EVM: [$sel:vmoptSchedule:VMOpts] :: VMOpts -> FeeSchedule Word64
+ EVM: [$sel:vmoptStorageBase:VMOpts] :: VMOpts -> StorageBase
+ EVM: [$sel:vmoptTimestamp:VMOpts] :: VMOpts -> Expr EWord
+ EVM: [$sel:vmoptTxAccessList:VMOpts] :: VMOpts -> Map Addr [W256]
+ EVM: [$sel:vmoptValue:VMOpts] :: VMOpts -> Expr EWord
+ EVM: overrideCaller :: Lens' VM (Maybe (Expr 'EWord))
+ EVM.Dapp: [$sel:abiMap:DappInfo] :: DappInfo -> Map Word32 Method
+ EVM.Dapp: [$sel:astIdMap:DappInfo] :: DappInfo -> Map Int Value
+ EVM.Dapp: [$sel:astSrcMap:DappInfo] :: DappInfo -> SrcMap -> Maybe Value
+ EVM.Dapp: [$sel:env:DappContext] :: DappContext -> Map Addr Contract
+ EVM.Dapp: [$sel:errorMap:DappInfo] :: DappInfo -> Map W256 SolError
+ EVM.Dapp: [$sel:eventMap:DappInfo] :: DappInfo -> Map W256 Event
+ EVM.Dapp: [$sel:immutableLocations:Code] :: Code -> [Reference]
+ EVM.Dapp: [$sel:info:DappContext] :: DappContext -> DappInfo
+ EVM.Dapp: [$sel:raw:Code] :: Code -> ByteString
+ EVM.Dapp: [$sel:root:DappInfo] :: DappInfo -> FilePath
+ EVM.Dapp: [$sel:solcByCode:DappInfo] :: DappInfo -> [(Code, SolcContract)]
+ EVM.Dapp: [$sel:solcByHash:DappInfo] :: DappInfo -> Map W256 (CodeType, SolcContract)
+ EVM.Dapp: [$sel:solcByName:DappInfo] :: DappInfo -> Map Text SolcContract
+ EVM.Dapp: [$sel:sources:DappInfo] :: DappInfo -> SourceCache
+ EVM.Dapp: [$sel:unitTests:DappInfo] :: DappInfo -> [(Text, [(Test, [AbiType])])]
+ 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.FeeSchedule: [$sel:g_access_list_address:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_access_list_storage_key:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_balance:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_base:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_blockhash:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_call:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_callstipend:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_callvalue:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_codedeposit:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_cold_account_access:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_cold_sload:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_copy:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_create:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_ecadd:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_ecmul:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_exp:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_expbyte:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_extcode:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_extcodehash:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_fround:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_high:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_jumpdest:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_log:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_logdata:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_logtopic:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_low:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_memory:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_mid:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_newaccount:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_pairing_base:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_pairing_point:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_quaddivisor:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_selfdestruct:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_selfdestruct_newaccount:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_sha3:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_sha3word:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_sload:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_sreset:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_sset:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_transaction:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_txcreate:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_txdatanonzero:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_txdatazero:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_verylow:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_warm_storage_read:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:g_zero:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:r_block:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:r_sclear:FeeSchedule] :: FeeSchedule n -> n
+ EVM.FeeSchedule: [$sel:r_selfdestruct:FeeSchedule] :: FeeSchedule n -> n
+ EVM.Op: OpBaseFee :: Op
+ EVM.SMT: [$sel:blockContext:SMTCex] :: SMTCex -> Map (Expr EWord) W256
+ EVM.SMT: [$sel:blockContextV:CexVars] :: CexVars -> [Text]
+ EVM.SMT: [$sel:buffers:SMTCex] :: SMTCex -> Map (Expr Buf) ByteString
+ EVM.SMT: [$sel:buffersV:CexVars] :: CexVars -> [Text]
+ EVM.SMT: [$sel:calldataV:CexVars] :: CexVars -> [Text]
+ EVM.SMT: [$sel:store:SMTCex] :: SMTCex -> Map W256 (Map W256 W256)
+ EVM.SMT: [$sel:storeReads:CexVars] :: CexVars -> [(Expr EWord, Expr EWord)]
+ EVM.SMT: [$sel:txContext:SMTCex] :: SMTCex -> Map (Expr EWord) W256
+ EVM.SMT: [$sel:txContextV:CexVars] :: CexVars -> [Text]
+ EVM.SMT: [$sel:vars:SMTCex] :: SMTCex -> Map (Expr EWord) W256
+ EVM.SMT: findStorageReads :: Prop -> [(Expr EWord, Expr EWord)]
+ EVM.SMT: getStore :: (Text -> IO Text) -> [(Expr EWord, Expr EWord)] -> IO (Map W256 (Map W256 W256))
+ EVM.SMT: interpret1DArray :: Map Symbol Term -> Term -> W256 -> W256
+ EVM.SMT: interpret2DArray :: Map Symbol Term -> Term -> W256 -> W256 -> W256
+ EVM.SMT: interpretNDArray :: (Map Symbol Term -> Term -> a) -> Map Symbol Term -> Term -> W256 -> a
+ EVM.SMT: isAbstractStorage :: Expr Storage -> Bool
+ EVM.SMT: referencedBlockContextGo :: Expr a -> [Builder]
+ EVM.SMT: referencedBufsGo :: Expr a -> [Builder]
+ EVM.SMT: referencedFrameContextGo :: Expr a -> [Builder]
+ EVM.SMT: referencedVarsGo :: Expr a -> [Builder]
+ EVM.Solidity: [$sel:abiMap:SolcContract] :: SolcContract -> Map Word32 Method
+ EVM.Solidity: [$sel:asts:SourceCache] :: SourceCache -> Map Text Value
+ EVM.Solidity: [$sel:constructorInputs:SolcContract] :: SolcContract -> [(Text, AbiType)]
+ EVM.Solidity: [$sel:contractName:SolcContract] :: SolcContract -> Text
+ EVM.Solidity: [$sel:creationCode:SolcContract] :: SolcContract -> ByteString
+ EVM.Solidity: [$sel:creationCodehash:SolcContract] :: SolcContract -> W256
+ EVM.Solidity: [$sel:creationSrcmap:SolcContract] :: SolcContract -> Seq SrcMap
+ EVM.Solidity: [$sel:errorMap:SolcContract] :: SolcContract -> Map W256 SolError
+ EVM.Solidity: [$sel:eventMap:SolcContract] :: SolcContract -> Map W256 Event
+ EVM.Solidity: [$sel:files:SourceCache] :: SourceCache -> [(Text, ByteString)]
+ EVM.Solidity: [$sel:immutableReferences:SolcContract] :: SolcContract -> Map W256 [Reference]
+ EVM.Solidity: [$sel:inputs:Method] :: Method -> [(Text, AbiType)]
+ EVM.Solidity: [$sel:length:Reference] :: Reference -> Int
+ EVM.Solidity: [$sel:lines:SourceCache] :: SourceCache -> [Vector ByteString]
+ EVM.Solidity: [$sel:methodSignature:Method] :: Method -> Text
+ EVM.Solidity: [$sel:mutability:Method] :: Method -> Mutability
+ EVM.Solidity: [$sel:name:Method] :: Method -> Text
+ EVM.Solidity: [$sel:offset:StorageItem] :: StorageItem -> Int
+ EVM.Solidity: [$sel:output:Method] :: Method -> [(Text, AbiType)]
+ EVM.Solidity: [$sel:runtimeCode:SolcContract] :: SolcContract -> ByteString
+ EVM.Solidity: [$sel:runtimeCodehash:SolcContract] :: SolcContract -> W256
+ EVM.Solidity: [$sel:runtimeSrcmap:SolcContract] :: SolcContract -> Seq SrcMap
+ EVM.Solidity: [$sel:slot:StorageItem] :: StorageItem -> Int
+ EVM.Solidity: [$sel:slotType:StorageItem] :: StorageItem -> SlotType
+ EVM.Solidity: [$sel:srcMapFile:SM] :: SrcMap -> {-# UNPACK #-}Int
+ EVM.Solidity: [$sel:srcMapJump:SM] :: SrcMap -> JumpType
+ EVM.Solidity: [$sel:srcMapLength:SM] :: SrcMap -> {-# UNPACK #-}Int
+ EVM.Solidity: [$sel:srcMapModifierDepth:SM] :: SrcMap -> {-# UNPACK #-}Int
+ EVM.Solidity: [$sel:srcMapOffset:SM] :: SrcMap -> {-# UNPACK #-}Int
+ EVM.Solidity: [$sel:start:Reference] :: Reference -> Int
+ EVM.Solidity: [$sel:storageLayout:SolcContract] :: SolcContract -> Maybe (Map Text StorageItem)
+ EVM.Solvers: Bitwuzla :: Solver
+ EVM.Solvers: CVC5 :: Solver
+ EVM.Solvers: Custom :: Text -> Solver
+ EVM.Solvers: Error :: Text -> CheckSatResult
+ EVM.Solvers: Sat :: SMTCex -> CheckSatResult
+ EVM.Solvers: SolverGroup :: Chan Task -> SolverGroup
+ EVM.Solvers: SolverInstance :: Solver -> Handle -> Handle -> Handle -> ProcessHandle -> SolverInstance
+ EVM.Solvers: Task :: SMT2 -> Chan CheckSatResult -> Task
+ EVM.Solvers: Unknown :: CheckSatResult
+ EVM.Solvers: Unsat :: CheckSatResult
+ EVM.Solvers: Z3 :: Solver
+ EVM.Solvers: [$sel:_process:SolverInstance] :: SolverInstance -> ProcessHandle
+ EVM.Solvers: [$sel:_stderr:SolverInstance] :: SolverInstance -> Handle
+ EVM.Solvers: [$sel:_stdin:SolverInstance] :: SolverInstance -> Handle
+ EVM.Solvers: [$sel:_stdout:SolverInstance] :: SolverInstance -> Handle
+ EVM.Solvers: [$sel:_type:SolverInstance] :: SolverInstance -> Solver
+ EVM.Solvers: [$sel:resultChan:Task] :: Task -> Chan CheckSatResult
+ EVM.Solvers: [$sel:script:Task] :: Task -> SMT2
+ EVM.Solvers: checkSat :: SolverGroup -> SMT2 -> IO CheckSatResult
+ EVM.Solvers: data CheckSatResult
+ EVM.Solvers: data Solver
+ EVM.Solvers: data SolverInstance
+ EVM.Solvers: data Task
+ EVM.Solvers: getValue :: SolverInstance -> Text -> IO Text
+ EVM.Solvers: instance GHC.Classes.Eq EVM.Solvers.CheckSatResult
+ EVM.Solvers: instance GHC.Show.Show EVM.Solvers.CheckSatResult
+ EVM.Solvers: instance GHC.Show.Show EVM.Solvers.Solver
+ EVM.Solvers: isErr :: CheckSatResult -> Bool
+ EVM.Solvers: isSat :: CheckSatResult -> Bool
+ EVM.Solvers: isUnsat :: CheckSatResult -> Bool
+ EVM.Solvers: newtype SolverGroup
+ EVM.Solvers: readSExpr :: Handle -> IO [Text]
+ EVM.Solvers: sendCommand :: SolverInstance -> Text -> IO Text
+ EVM.Solvers: sendLine :: SolverInstance -> Text -> IO Text
+ EVM.Solvers: sendLine' :: SolverInstance -> Text -> IO ()
+ EVM.Solvers: sendScript :: SolverInstance -> SMT2 -> IO (Either Text ())
+ EVM.Solvers: solverArgs :: Solver -> Maybe Natural -> [Text]
+ EVM.Solvers: spawnSolver :: Solver -> Maybe Natural -> IO SolverInstance
+ EVM.Solvers: stopSolver :: SolverInstance -> IO ()
+ EVM.Solvers: withSolvers :: Solver -> Natural -> Maybe Natural -> (SolverGroup -> IO a) -> IO a
+ EVM.SymExec: [$sel:askSmtIters:VeriOpts] :: VeriOpts -> Maybe Integer
+ EVM.SymExec: [$sel:debug:VeriOpts] :: VeriOpts -> Bool
+ EVM.SymExec: [$sel:maxIter:VeriOpts] :: VeriOpts -> Maybe Integer
+ EVM.SymExec: [$sel:rpcInfo:VeriOpts] :: VeriOpts -> RpcInfo
+ EVM.SymExec: [$sel:simp:VeriOpts] :: VeriOpts -> Bool
+ EVM.SymExec: isCex :: ProofResult a b c -> Bool
+ EVM.SymExec: isTimeout :: ProofResult a b c -> Bool
+ EVM.SymExec: type EquivResult = ProofResult () (SMTCex) ()
+ EVM.SymExec: type UnsatCache = TVar [Set Prop]
+ EVM.TTY: [$sel:_browserContractList:UiBrowserState] :: UiBrowserState -> List Name (Addr, Contract)
+ EVM.TTY: [$sel:_browserVm:UiBrowserState] :: UiBrowserState -> UiVmState
+ EVM.TTY: [$sel:_testOpts:UiTestPickerState] :: UiTestPickerState -> UnitTestOptions
+ EVM.TTY: [$sel:_testPickerDapp:UiTestPickerState] :: UiTestPickerState -> DappInfo
+ EVM.TTY: [$sel:_testPickerList:UiTestPickerState] :: UiTestPickerState -> List Name (Text, Text)
+ EVM.TTY: [$sel:_uiShowMemory:UiVmState] :: UiVmState -> Bool
+ EVM.TTY: [$sel:_uiSnapshots:UiVmState] :: UiVmState -> Map Int (VM, Stepper ())
+ EVM.TTY: [$sel:_uiStep:UiVmState] :: UiVmState -> Int
+ EVM.TTY: [$sel:_uiStepper:UiVmState] :: UiVmState -> Stepper ()
+ EVM.TTY: [$sel:_uiTestOpts:UiVmState] :: UiVmState -> UnitTestOptions
+ EVM.TTY: [$sel:_uiVm:UiVmState] :: UiVmState -> VM
+ EVM.Transaction: [$sel:accessAddress:AccessListEntry] :: AccessListEntry -> Addr
+ EVM.Transaction: [$sel:accessStorageKeys:AccessListEntry] :: AccessListEntry -> [W256]
+ EVM.Transaction: [$sel:txAccessList:Transaction] :: Transaction -> [AccessListEntry]
+ EVM.Transaction: [$sel:txData:Transaction] :: Transaction -> ByteString
+ EVM.Transaction: [$sel:txGasLimit:Transaction] :: Transaction -> Word64
+ EVM.Transaction: [$sel:txGasPrice:Transaction] :: Transaction -> Maybe W256
+ EVM.Transaction: [$sel:txMaxFeePerGas:Transaction] :: Transaction -> Maybe W256
+ EVM.Transaction: [$sel:txMaxPriorityFeeGas:Transaction] :: Transaction -> Maybe W256
+ EVM.Transaction: [$sel:txNonce:Transaction] :: Transaction -> W256
+ EVM.Transaction: [$sel:txR:Transaction] :: Transaction -> W256
+ EVM.Transaction: [$sel:txS:Transaction] :: Transaction -> W256
+ EVM.Transaction: [$sel:txToAddr:Transaction] :: Transaction -> Maybe Addr
+ EVM.Transaction: [$sel:txType:Transaction] :: Transaction -> TxType
+ EVM.Transaction: [$sel:txV:Transaction] :: Transaction -> W256
+ EVM.Transaction: [$sel:txValue:Transaction] :: Transaction -> W256
+ EVM.Types: StackUnderrun :: Error
+ EVM.Types: [$sel:addressWord160:Addr] :: Addr -> Word160
+ EVM.Types: pand :: [Prop] -> Prop
+ EVM.Types: por :: [Prop] -> Prop
+ EVM.UnitTest: [$sel:askSmtIters:UnitTestOptions] :: UnitTestOptions -> Maybe Integer
+ EVM.UnitTest: [$sel:covMatch:UnitTestOptions] :: UnitTestOptions -> Maybe Text
+ EVM.UnitTest: [$sel:dapp:UnitTestOptions] :: UnitTestOptions -> DappInfo
+ EVM.UnitTest: [$sel:ffiAllowed:UnitTestOptions] :: UnitTestOptions -> Bool
+ EVM.UnitTest: [$sel:fuzzRuns:UnitTestOptions] :: UnitTestOptions -> Int
+ EVM.UnitTest: [$sel:match:UnitTestOptions] :: UnitTestOptions -> Text
+ EVM.UnitTest: [$sel:maxDepth:UnitTestOptions] :: UnitTestOptions -> Maybe Int
+ EVM.UnitTest: [$sel:maxIter:UnitTestOptions] :: UnitTestOptions -> Maybe Integer
+ EVM.UnitTest: [$sel:replay:UnitTestOptions] :: UnitTestOptions -> Maybe (Text, ByteString)
+ EVM.UnitTest: [$sel:rpcInfo:UnitTestOptions] :: UnitTestOptions -> RpcInfo
+ EVM.UnitTest: [$sel:smtDebug:UnitTestOptions] :: UnitTestOptions -> Bool
+ EVM.UnitTest: [$sel:smtTimeout:UnitTestOptions] :: UnitTestOptions -> Maybe Natural
+ EVM.UnitTest: [$sel:solver:UnitTestOptions] :: UnitTestOptions -> Maybe Text
+ EVM.UnitTest: [$sel:solvers:UnitTestOptions] :: UnitTestOptions -> SolverGroup
+ EVM.UnitTest: [$sel:srcContract:OpLocation] :: OpLocation -> Contract
+ EVM.UnitTest: [$sel:srcOpIx:OpLocation] :: OpLocation -> Int
+ EVM.UnitTest: [$sel:testAddress:TestVMParams] :: TestVMParams -> Addr
+ EVM.UnitTest: [$sel:testBalanceCreate:TestVMParams] :: TestVMParams -> W256
+ EVM.UnitTest: [$sel:testBaseFee:TestVMParams] :: TestVMParams -> W256
+ EVM.UnitTest: [$sel:testCaller:TestVMParams] :: TestVMParams -> Addr
+ EVM.UnitTest: [$sel:testChainId:TestVMParams] :: TestVMParams -> W256
+ EVM.UnitTest: [$sel:testCoinbase:TestVMParams] :: TestVMParams -> Addr
+ EVM.UnitTest: [$sel:testGasCall:TestVMParams] :: TestVMParams -> Word64
+ EVM.UnitTest: [$sel:testGasCreate:TestVMParams] :: TestVMParams -> Word64
+ EVM.UnitTest: [$sel:testGaslimit:TestVMParams] :: TestVMParams -> Word64
+ EVM.UnitTest: [$sel:testGasprice:TestVMParams] :: TestVMParams -> W256
+ EVM.UnitTest: [$sel:testMaxCodeSize:TestVMParams] :: TestVMParams -> W256
+ EVM.UnitTest: [$sel:testNumber:TestVMParams] :: TestVMParams -> W256
+ EVM.UnitTest: [$sel:testOrigin:TestVMParams] :: TestVMParams -> Addr
+ EVM.UnitTest: [$sel:testParams:UnitTestOptions] :: UnitTestOptions -> TestVMParams
+ EVM.UnitTest: [$sel:testPrevrandao:TestVMParams] :: TestVMParams -> W256
+ EVM.UnitTest: [$sel:testPriorityFee:TestVMParams] :: TestVMParams -> W256
+ EVM.UnitTest: [$sel:testTimestamp:TestVMParams] :: TestVMParams -> W256
+ EVM.UnitTest: [$sel:verbose:UnitTestOptions] :: UnitTestOptions -> Maybe Int
+ EVM.UnitTest: [$sel:vmModifier:UnitTestOptions] :: UnitTestOptions -> VM -> VM
- EVM: VM :: Maybe VMResult -> FrameState -> [Frame] -> Env -> Block -> TxState -> [Expr Log] -> TreePos Empty Trace -> Cache -> Word64 -> Map CodeLocation Int -> [Prop] -> [Prop] -> Bool -> VM
+ EVM: VM :: Maybe VMResult -> FrameState -> [Frame] -> Env -> Block -> TxState -> [Expr Log] -> TreePos Empty Trace -> Cache -> Word64 -> Map CodeLocation Int -> [Prop] -> [Prop] -> Bool -> Maybe (Expr EWord) -> VM
- EVM: traceTopLog :: MonadState VM m => [Expr Log] -> m ()
+ EVM: traceTopLog :: [Expr Log] -> EVM ()
- EVM: withTraceLocation :: MonadState VM m => TraceData -> m Trace
+ EVM: withTraceLocation :: TraceData -> EVM Trace
- EVM.Exec: exec :: MonadState VM m => m VMResult
+ EVM.Exec: exec :: State VM VMResult
- EVM.Exec: execWhile :: MonadState VM m => (VM -> Bool) -> m Int
+ EVM.Exec: execWhile :: (VM -> Bool) -> State VM Int
- EVM.Exec: run :: MonadState VM m => m VM
+ EVM.Exec: run :: State VM VM
- EVM.SMT: CexVars :: [Text] -> [Text] -> [Text] -> [Text] -> CexVars
+ EVM.SMT: CexVars :: [Text] -> [Text] -> [(Expr EWord, Expr EWord)] -> [Text] -> [Text] -> CexVars
- EVM.SMT: SMTCex :: Map (Expr EWord) W256 -> Map (Expr Buf) ByteString -> Map (Expr EWord) W256 -> Map (Expr EWord) W256 -> SMTCex
+ EVM.SMT: SMTCex :: Map (Expr EWord) W256 -> Map (Expr Buf) ByteString -> Map W256 (Map W256 W256) -> Map (Expr EWord) W256 -> Map (Expr EWord) W256 -> SMTCex
- EVM.SMT: getBufs :: SolverInstance -> [Text] -> IO (Map (Expr Buf) ByteString)
+ EVM.SMT: getBufs :: (Text -> IO Text) -> [Text] -> IO (Map (Expr Buf) ByteString)
- EVM.SMT: getVars :: (Text -> Expr EWord) -> SolverInstance -> [Text] -> IO (Map (Expr EWord) W256)
+ EVM.SMT: getVars :: (Text -> Expr EWord) -> (Text -> IO Text) -> [Text] -> IO (Map (Expr EWord) W256)
- EVM.SymExec: equivalenceCheck :: SolverGroup -> ByteString -> ByteString -> VeriOpts -> Maybe (Text, [AbiType]) -> IO [(Maybe SMTCex, Prop, ProofResult () () ())]
+ EVM.SymExec: equivalenceCheck :: SolverGroup -> ByteString -> ByteString -> VeriOpts -> Maybe (Text, [AbiType]) -> IO [EquivResult]
Files
- CHANGELOG.md +14/−0
- bench/bench.hs +85/−0
- bench/contracts/erc20.sol +42/−0
- hevm-cli/hevm-cli.hs +133/−131
- hevm.cabal +62/−8
- src/EVM.hs +238/−241
- src/EVM/CSE.hs +14/−14
- src/EVM/Dapp.hs +62/−75
- src/EVM/Debug.hs +5/−5
- src/EVM/Dev.hs +1/−0
- src/EVM/Exec.hs +17/−18
- src/EVM/Expr.hs +22/−0
- src/EVM/Facts.hs +13/−13
- src/EVM/Fetch.hs +1/−0
- src/EVM/Format.hs +54/−60
- src/EVM/Keccak.hs +3/−3
- src/EVM/Op.hs +2/−0
- src/EVM/SMT.hs +273/−407
- src/EVM/Solidity.hs +92/−119
- src/EVM/Solvers.hs +239/−0
- src/EVM/Stepper.hs +3/−3
- src/EVM/StorageLayout.hs +9/−12
- src/EVM/SymExec.hs +181/−87
- src/EVM/TTY.hs +112/−125
- src/EVM/Transaction.hs +46/−46
- src/EVM/Types.hs +10/−3
- src/EVM/UnitTest.hs +80/−88
- test/BlockchainTests.hs +38/−41
- test/EVM/TestUtils.hs +1/−1
- test/contracts/pass/cheatCodes.sol +15/−0
- test/rpc.hs +1/−0
- test/test.hs +201/−77
CHANGELOG.md view
@@ -5,6 +5,20 @@ 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.50.3] - 2023-02-17++### Fixed++- `hevm symbolic` exits with status code `1` if counterexamples or timeouts are found++### Added++- New cheatcode `prank(address)` that sets `msg.sender` to the specified address for the next call.+- Improved equivalence checker that avoids checking similar branches more than once.+- Improved simplification for arithmetic expressions+- Construction of storage counterexamples based on the model returned by the SMT solver.+- Static binaries for macos+ ## [0.50.2] - 2023-01-06 ### Fixed
+ bench/bench.hs view
@@ -0,0 +1,85 @@+module Main where++import GHC.Natural++import qualified Paths_hevm as Paths++import Test.Tasty (localOption)+import Test.Tasty.Bench+import Data.Functor+import Data.String.Here+import Data.ByteString (ByteString)+import qualified Data.Text as T++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++findPanics :: Solver -> Natural -> Maybe Integer -> ByteString -> IO ()+findPanics solver count iters c = do+ (_, res) <- withSolvers solver count Nothing $ \s -> do+ let opts = defaultVeriOpts+ { maxIter = iters+ , askSmtIters = (+ 1) <$> iters+ }+ checkAssert s allPanicCodes c Nothing [] opts+ putStrLn "done"+++-- constructs a benchmark suite that checks the given bytecode for reachable+-- 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 c name iters counts = localOption WallTime $ env c (bgroup name . bmarks)+ where+ bmarks c' = concat $ [+ [ bench ("cvc5-" <> show i) $ nfIO $ findPanics CVC5 i iters c'+ , bench ("z3-" <> show i) $ nfIO $ findPanics Z3 i iters c'+ ]+ | i <- counts+ ]++main :: IO ()+main = defaultMain+ [ mkbench erc20 "erc20" Nothing [1, 2, 4, 8, 16]+ , mkbench (pure vat) "vat" Nothing [4]+ , mkbench (pure deposit) "deposit" (Just 32) [4]+ , mkbench (pure uniV2Pair) "uniV2" (Just 10) [4]+ ]+++--- Helpers ----------------------------------------------------------------------------------------+++debugContract :: ByteString -> IO ()+debugContract c = withSolvers CVC5 4 Nothing $ \solvers -> do+ let prestate = abstractVM Nothing [] c Nothing SymbolicS+ void $ TTY.runFromVM solvers Nothing Nothing emptyDapp prestate+++--- Bytecodes --------------------------------------------------------------------------------------+++erc20 :: IO ByteString+erc20 = do+ source <- readFile =<< Paths.getDataFileName "bench/contracts/erc20.sol"+ Just c <- solcRuntime "ERC20" (T.pack source)+ pure c++-- requires solc-0.6.12 to build from source+vat :: ByteString+vat = hexByteString "vat" "608060405234801561001057600080fd5b50600436106101c45760003560e01c80637cdd3fde116100f9578063bb35783b11610097578063dc4d20fa11610071578063dc4d20fa1461096c578063f059212a146109b0578063f24e23eb14610a08578063f37ac61c14610a76576101c4565b8063bb35783b14610848578063bf353dbb146108b6578063d9638d361461090e576101c4565b80639c52a7f1116100d35780639c52a7f11461074a578063a3b22fc41461078e578063b65337df146107d2578063babe8a3f1461082a576101c4565b80637cdd3fde14610652578063870c616d146106aa578063957aa58c1461072c576101c4565b80634538c4eb11610166578063692450091161014057806369245009146104ac5780636c25b346146104b6578063760887031461050e5780637bab3f40146105b0576101c4565b80634538c4eb146103785780636111be2e146103f057806365fae35e14610468576101c4565b80632424be5c116101a25780632424be5c1461028b57806329ae8114146102f45780632d61a3551461032c5780633b6631951461034a576101c4565b80630dca59c1146101c95780631a0b287e146101e7578063214414d514610229575b600080fd5b6101d1610aa4565b6040518082815260200191505060405180910390f35b610227600480360360608110156101fd57600080fd5b81019080803590602001909291908035906020019092919080359060200190929190505050610aaa565b005b6102756004803603604081101561023f57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d23565b6040518082815260200191505060405180910390f35b6102d7600480360360408110156102a157600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d48565b604051808381526020018281526020019250505060405180910390f35b61032a6004803603604081101561030a57600080fd5b810190808035906020019092919080359060200190929190505050610d79565b005b610334610f4b565b6040518082815260200191505060405180910390f35b6103766004803603602081101561036057600080fd5b8101908080359060200190929190505050610f51565b005b6103da6004803603604081101561038e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506110bb565b6040518082815260200191505060405180910390f35b6104666004803603608081101561040657600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506110e0565b005b6104aa6004803603602081101561047e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506112be565b005b6104b4611431565b005b6104f8600480360360208110156104cc57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506114ef565b6040518082815260200191505060405180910390f35b6105ae600480360360c081101561052457600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190929190505050611507565b005b610650600480360360c08110156105c657600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190929190505050611cbe565b005b6106a86004803603606081101561066857600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611f88565b005b61072a600480360360a08110156106c057600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001909291905050506120ef565b005b6107346124ee565b6040518082815260200191505060405180910390f35b61078c6004803603602081101561076057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506124f4565b005b6107d0600480360360208110156107a457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612667565b005b610828600480360360608110156107e857600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506126eb565b005b6108326128fb565b6040518082815260200191505060405180910390f35b6108b46004803603606081101561085e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050612901565b005b6108f8600480360360208110156108cc57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612a9a565b6040518082815260200191505060405180910390f35b61093a6004803603602081101561092457600080fd5b8101908080359060200190929190505050612ab2565b604051808681526020018581526020018481526020018381526020018281526020019550505050505060405180910390f35b6109ae6004803603602081101561098257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612ae8565b005b6109f2600480360360208110156109c657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612b6d565b6040518082815260200191505060405180910390f35b610a7460048036036060811015610a1e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050612b85565b005b610aa260048036036020811015610a8c57600080fd5b8101908080359060200190929190505050612d7a565b005b60075481565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414610b5e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f5661742f6e6f742d617574686f72697a6564000000000000000000000000000081525060200191505060405180910390fd5b6001600a5414610bd6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600c8152602001807f5661742f6e6f742d6c697665000000000000000000000000000000000000000081525060200191505060405180910390fd5b7f73706f7400000000000000000000000000000000000000000000000000000000821415610c1e57806002600085815260200190815260200160002060020181905550610d1e565b7f6c696e6500000000000000000000000000000000000000000000000000000000821415610c6657806002600085815260200190815260200160002060030181905550610d1d565b7f6475737400000000000000000000000000000000000000000000000000000000821415610cae57806002600085815260200190815260200160002060040181905550610d1c565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f5661742f66696c652d756e7265636f676e697a65642d706172616d000000000081525060200191505060405180910390fd5b5b5b505050565b6004602052816000526040600020602052806000526040600020600091509150505481565b6003602052816000526040600020602052806000526040600020600091509150508060000154908060010154905082565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414610e2d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f5661742f6e6f742d617574686f72697a6564000000000000000000000000000081525060200191505060405180910390fd5b6001600a5414610ea5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600c8152602001807f5661742f6e6f742d6c697665000000000000000000000000000000000000000081525060200191505060405180910390fd5b7f4c696e6500000000000000000000000000000000000000000000000000000000821415610ed95780600981905550610f47565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f5661742f66696c652d756e7265636f676e697a65642d706172616d000000000081525060200191505060405180910390fd5b5050565b60085481565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414611005576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f5661742f6e6f742d617574686f72697a6564000000000000000000000000000081525060200191505060405180910390fd5b6000600260008381526020019081526020016000206001015414611091576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f5661742f696c6b2d616c72656164792d696e697400000000000000000000000081525060200191505060405180910390fd5b6b033b2e3c9fd0803ce8000000600260008381526020019081526020016000206001018190555050565b6001602052816000526040600020602052806000526040600020600091509150505481565b6110ea8333612ebf565b61115c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f5661742f6e6f742d616c6c6f776564000000000000000000000000000000000081525060200191505060405180910390fd5b6111b66004600086815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482612f7f565b6004600086815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506112646004600086815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482612f99565b6004600086815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050505050565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414611372576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f5661742f6e6f742d617574686f72697a6564000000000000000000000000000081525060200191505060405180910390fd5b6001600a54146113ea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600c8152602001807f5661742f6e6f742d6c697665000000000000000000000000000000000000000081525060200191505060405180910390fd5b60016000808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054146114e5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f5661742f6e6f742d617574686f72697a6564000000000000000000000000000081525060200191505060405180910390fd5b6000600a81905550565b60056020528060005260406000206000915090505481565b6001600a541461157f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600c8152602001807f5661742f6e6f742d6c697665000000000000000000000000000000000000000081525060200191505060405180910390fd5b6115876130b2565b6003600088815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060400160405290816000820154815260200160018201548152505090506116006130cc565b600260008981526020019081526020016000206040518060a00160405290816000820154815260200160018201548152602001600282015481526020016003820154815260200160048201548152505090506000816020015114156116cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f5661742f696c6b2d6e6f742d696e69740000000000000000000000000000000081525060200191505060405180910390fd5b6116db826000015185612fb3565b8260000181815250506116f2826020015184612fb3565b826020018181525050611709816000015184612fb3565b8160000181815250506000611722826020015185612ff2565b905060006117388360200151856020015161302d565b905061174660075483612fb3565b600781905550611782600086131561177d856060015161176e8760000151886020015161302d565b11156009546007541115613059565b613066565b6117f4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f5661742f6365696c696e672d657863656564656400000000000000000000000081525060200191505060405180910390fd5b61182361180960008713156000891215613059565b61181b8660000151866040015161302d565b831115613066565b611895576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600c8152602001807f5661742f6e6f742d73616665000000000000000000000000000000000000000081525060200191505060405180910390fd5b6118b96118aa60008713156000891215613059565b6118b48b33612ebf565b613066565b61192b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f5661742f6e6f742d616c6c6f7765642d7500000000000000000000000000000081525060200191505060405180910390fd5b611942600087131561193d8a33612ebf565b613066565b6119b4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f5661742f6e6f742d616c6c6f7765642d7600000000000000000000000000000081525060200191505060405180910390fd5b6119cb60008612156119c68933612ebf565b613066565b611a3d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f5661742f6e6f742d616c6c6f7765642d7700000000000000000000000000000081525060200191505060405180910390fd5b611a5560008560200151148460800151831015613066565b611ac7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260088152602001807f5661742f6475737400000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b611b21600460008c815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205487613073565b600460008c815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550611bbe600560008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205483612fb3565b600560008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555083600360008c815260200190815260200160002060008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082015181600001556020820151816001015590505082600260008c8152602001908152602001600020600082015181600001556020820151816001015560408201518160020155606082015181600301556080820151816004015590505050505050505050505050565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414611d72576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f5661742f6e6f742d617574686f72697a6564000000000000000000000000000081525060200191505060405180910390fd5b60006003600088815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000600260008981526020019081526020016000209050611deb826000015485612fb3565b8260000181905550611e01826001015484612fb3565b8260010181905550611e17816000015484612fb3565b81600001819055506000611e2f826001015485612ff2565b9050611e8b600460008b815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205486613073565b600460008b815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550611f28600660008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482613073565b600660008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550611f7760085482613073565b600881905550505050505050505050565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541461203c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f5661742f6e6f742d617574686f72697a6564000000000000000000000000000081525060200191505060405180910390fd5b6120966004600085815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482612fb3565b6004600085815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505050565b60006003600087815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060006003600088815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060006002600089815260200190815260200160002090506121bc836000015486613073565b83600001819055506121d2836001015485613073565b83600101819055506121e8826000015486612fb3565b82600001819055506121fe826001015485612fb3565b8260010181905550600061221a8460010154836001015461302d565b905060006122308460010154846001015461302d565b905061224e61223f8a33612ebf565b6122498a33612ebf565b613059565b6122c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f5661742f6e6f742d616c6c6f776564000000000000000000000000000000000081525060200191505060405180910390fd5b6122d28560000154846002015461302d565b821115612347576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f5661742f6e6f742d736166652d7372630000000000000000000000000000000081525060200191505060405180910390fd5b6123598460000154846002015461302d565b8111156123ce576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f5661742f6e6f742d736166652d6473740000000000000000000000000000000081525060200191505060405180910390fd5b6123e683600401548310156000876001015414613066565b612458576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600c8152602001807f5661742f647573742d737263000000000000000000000000000000000000000081525060200191505060405180910390fd5b61247083600401548210156000866001015414613066565b6124e2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600c8152602001807f5661742f647573742d647374000000000000000000000000000000000000000081525060200191505060405180910390fd5b50505050505050505050565b600a5481565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054146125a8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f5661742f6e6f742d617574686f72697a6564000000000000000000000000000081525060200191505060405180910390fd5b6001600a5414612620576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600c8152602001807f5661742f6e6f742d6c697665000000000000000000000000000000000000000081525060200191505060405180910390fd5b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050565b60018060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541461279f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f5661742f6e6f742d617574686f72697a6564000000000000000000000000000081525060200191505060405180910390fd5b6001600a5414612817576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600c8152602001807f5661742f6e6f742d6c697665000000000000000000000000000000000000000081525060200191505060405180910390fd5b600060026000858152602001908152602001600020905061283c816001015483612fb3565b81600101819055506000612854826000015484612ff2565b905061289f600560008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482612fb3565b600560008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506128ee60075482612fb3565b6007819055505050505050565b60095481565b61290b8333612ebf565b61297d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f5661742f6e6f742d616c6c6f776564000000000000000000000000000000000081525060200191505060405180910390fd5b6129c6600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482612f7f565b600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550612a52600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482612f99565b600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505050565b60006020528060005260406000206000915090505481565b60026020528060005260406000206000915090508060000154908060010154908060020154908060030154908060040154905085565b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050565b60066020528060005260406000206000915090505481565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414612c39576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f5661742f6e6f742d617574686f72697a6564000000000000000000000000000081525060200191505060405180910390fd5b612c82600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482612f99565b600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550612d0e600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482612f99565b600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550612d5d60085482612f99565b600881905550612d6f60075482612f99565b600781905550505050565b6000339050612dc8600660008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205483612f7f565b600660008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550612e54600560008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205483612f7f565b600560008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550612ea360085483612f7f565b600881905550612eb560075483612f7f565b6007819055505050565b6000612f778273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161460018060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414613066565b905092915050565b6000828284039150811115612f9357600080fd5b92915050565b6000828284019150811015612fad57600080fd5b92915050565b60008183019050600082121580612fca5750828111155b612fd357600080fd5b600082131580612fe35750828110155b612fec57600080fd5b92915050565b60008183029050600083121561300757600080fd5b600082148061301e57508282828161301b57fe5b05145b61302757600080fd5b92915050565b60008082148061304a575082828385029250828161304757fe5b04145b61305357600080fd5b92915050565b6000818316905092915050565b6000818317905092915050565b6000818303905060008213158061308a5750828111155b61309357600080fd5b6000821215806130a35750828110155b6130ac57600080fd5b92915050565b604051806040016040528060008152602001600081525090565b6040518060a001604052806000815260200160008152602001600081526020016000815260200160008152509056fea2646970667358221220a6981c73e66c77a4325e4426a14f7d3876fbe831438d34792d1fdac37b02a51d64736f6c634300060c0033"++-- requires solc-0.6.13 to build+deposit :: ByteString+deposit = hexByteString "deposit" "60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100b6578063621fd130146101e3578063c5f2892f14610273575b600080fd5b34801561005057600080fd5b5061009c6004803603602081101561006757600080fd5b8101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919050505061029e565b604051808215151515815260200191505060405180910390f35b6101e1600480360360808110156100cc57600080fd5b81019080803590602001906401000000008111156100e957600080fd5b8201836020820111156100fb57600080fd5b8035906020019184600183028401116401000000008311171561011d57600080fd5b90919293919293908035906020019064010000000081111561013e57600080fd5b82018360208201111561015057600080fd5b8035906020019184600183028401116401000000008311171561017257600080fd5b90919293919293908035906020019064010000000081111561019357600080fd5b8201836020820111156101a557600080fd5b803590602001918460018302840111640100000000831117156101c757600080fd5b909192939192939080359060200190929190505050610370565b005b3480156101ef57600080fd5b506101f8610fd0565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561023857808201518184015260208101905061021d565b50505050905090810190601f1680156102655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561027f57600080fd5b50610288610fe2565b6040518082815260200191505060405180910390f35b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061036957507f85640907000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b603087879050146103cc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806116ec6026913960400191505060405180910390fd5b60208585905014610428576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260368152602001806116836036913960400191505060405180910390fd5b60608383905014610484576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602981526020018061175f6029913960400191505060405180910390fd5b670de0b6b3a76400003410156104e5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806117396026913960400191505060405180910390fd5b6000633b9aca0034816104f457fe5b061461054b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806116b96033913960400191505060405180910390fd5b6000633b9aca00348161055a57fe5b04905067ffffffffffffffff80168111156105c0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260278152602001806117126027913960400191505060405180910390fd5b60606105cb82611314565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a610600602054611314565b60405180806020018060200180602001806020018060200186810386528e8e82818152602001925080828437600081840152601f19601f82011690508083019250505086810385528c8c82818152602001925080828437600081840152601f19601f82011690508083019250505086810384528a818151815260200191508051906020019080838360005b838110156106a657808201518184015260208101905061068b565b50505050905090810190601f1680156106d35780820380516001836020036101000a031916815260200191505b508681038352898982818152602001925080828437600081840152601f19601f820116905080830192505050868103825287818151815260200191508051906020019080838360005b8381101561073757808201518184015260208101905061071c565b50505050905090810190601f1680156107645780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b6040516020018084848082843780830192505050826fffffffffffffffffffffffffffffffff19166fffffffffffffffffffffffffffffffff1916815260100193505050506040516020818303038152906040526040518082805190602001908083835b6020831061080e57805182526020820191506020810190506020830392506107eb565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015610850573d6000803e3d6000fd5b5050506040513d602081101561086557600080fd5b8101908080519060200190929190505050905060006002808888600090604092610891939291906115da565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108eb57805182526020820191506020810190506020830392506108c8565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa15801561092d573d6000803e3d6000fd5b5050506040513d602081101561094257600080fd5b8101908080519060200190929190505050600289896040908092610968939291906115da565b6000801b604051602001808484808284378083019250505082815260200193505050506040516020818303038152906040526040518082805190602001908083835b602083106109cd57805182526020820191506020810190506020830392506109aa565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015610a0f573d6000803e3d6000fd5b5050506040513d6020811015610a2457600080fd5b810190808051906020019092919050505060405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310610a8e5780518252602082019150602081019050602083039250610a6b565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015610ad0573d6000803e3d6000fd5b5050506040513d6020811015610ae557600080fd5b810190808051906020019092919050505090506000600280848c8c604051602001808481526020018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610b615780518252602082019150602081019050602083039250610b3e565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015610ba3573d6000803e3d6000fd5b5050506040513d6020811015610bb857600080fd5b8101908080519060200190929190505050600286600060401b866040516020018084805190602001908083835b60208310610c085780518252602082019150602081019050602083039250610be5565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610c935780518252602082019150602081019050602083039250610c70565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015610cd5573d6000803e3d6000fd5b5050506040513d6020811015610cea57600080fd5b810190808051906020019092919050505060405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310610d545780518252602082019150602081019050602083039250610d31565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015610d96573d6000803e3d6000fd5b5050506040513d6020811015610dab57600080fd5b81019080805190602001909291905050509050858114610e16576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252605481526020018061162f6054913960600191505060405180910390fd5b6001602060020a0360205410610e77576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061160e6021913960400191505060405180910390fd5b60016020600082825401925050819055506000602054905060008090505b6020811015610fb75760018083161415610ec8578260008260208110610eb757fe5b018190555050505050505050610fc7565b600260008260208110610ed757fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310610f335780518252602082019150602081019050602083039250610f10565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015610f75573d6000803e3d6000fd5b5050506040513d6020811015610f8a57600080fd5b8101908080519060200190929190505050925060028281610fa757fe5b0491508080600101915050610e95565b506000610fc057fe5b5050505050505b50505050505050565b6060610fdd602054611314565b905090565b6000806000602054905060008090505b60208110156111d057600180831614156110e05760026000826020811061101557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310611071578051825260208201915060208101905060208303925061104e565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa1580156110b3573d6000803e3d6000fd5b5050506040513d60208110156110c857600080fd5b810190808051906020019092919050505092506111b6565b600283602183602081106110f057fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061114b5780518252602082019150602081019050602083039250611128565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa15801561118d573d6000803e3d6000fd5b5050506040513d60208110156111a257600080fd5b810190808051906020019092919050505092505b600282816111c057fe5b0491508080600101915050610ff2565b506002826111df602054611314565b600060401b6040516020018084815260200183805190602001908083835b6020831061122057805182526020820191506020810190506020830392506111fd565b6001836020036101000a0380198251168184511680821785525050505050509050018267ffffffffffffffff191667ffffffffffffffff1916815260180193505050506040516020818303038152906040526040518082805190602001908083835b602083106112a55780518252602082019150602081019050602083039250611282565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa1580156112e7573d6000803e3d6000fd5b5050506040513d60208110156112fc57600080fd5b81019080805190602001909291905050509250505090565b6060600867ffffffffffffffff8111801561132e57600080fd5b506040519080825280601f01601f1916602001820160405280156113615781602001600182028036833780820191505090505b50905060008260c01b90508060076008811061137957fe5b1a60f81b8260008151811061138a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350806006600881106113c657fe5b1a60f81b826001815181106113d757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060056008811061141357fe5b1a60f81b8260028151811061142457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060046008811061146057fe5b1a60f81b8260038151811061147157fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350806003600881106114ad57fe5b1a60f81b826004815181106114be57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350806002600881106114fa57fe5b1a60f81b8260058151811061150b57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060016008811061154757fe5b1a60f81b8260068151811061155857fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060006008811061159457fe5b1a60f81b826007815181106115a557fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b600080858511156115ea57600080fd5b838611156115f757600080fd5b600185028301915084860390509450949250505056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a2646970667358221220e859959599195b69dd53a5a8e7d6218056606794a75a0aadd9fecf8dc64eae7064736f6c634300060b0033"++-- requires waffle to be installed to build+uniV2Pair :: ByteString+uniV2Pair = hexByteString "uniV2" "608060405234801561001057600080fd5b50600436106101a95760003560e01c80636a627842116100f9578063ba9a7a5611610097578063d21220a711610071578063d21220a7146108c4578063d505accf1461090e578063dd62ed3e146109a7578063fff6cae914610a1f576101a9565b8063ba9a7a5614610818578063bc25cf7714610836578063c45a01551461087a576101a9565b80637ecebe00116100d35780637ecebe001461067857806389afcb44146106d057806395d89b411461072f578063a9059cbb146107b2576101a9565b80636a627842146105aa57806370a08231146106025780637464fc3d1461065a576101a9565b806323b872dd116101665780633644e515116101405780633644e515146104ec578063485cc9551461050a5780635909c0d51461056e5780635a3d54931461058c576101a9565b806323b872dd1461042457806330adf81f146104aa578063313ce567146104c8576101a9565b8063022c0d9f146101ae57806306fdde031461025b5780630902f1ac146102de578063095ea7b3146103565780630dfe1681146103bc57806318160ddd14610406575b600080fd5b610259600480360360808110156101c457600080fd5b810190808035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561021557600080fd5b82018360208201111561022757600080fd5b8035906020019184600183028401116401000000008311171561024957600080fd5b9091929391929390505050610a29565b005b610263611216565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156102a3578082015181840152602081019050610288565b50505050905090810190601f1680156102d05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102e661124f565b60405180846dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff168152602001836dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff1681526020018263ffffffff1663ffffffff168152602001935050505060405180910390f35b6103a26004803603604081101561036c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506112ac565b604051808215151515815260200191505060405180910390f35b6103c46112c3565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61040e6112e9565b6040518082815260200191505060405180910390f35b6104906004803603606081101561043a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506112ef565b604051808215151515815260200191505060405180910390f35b6104b26114ba565b6040518082815260200191505060405180910390f35b6104d06114e1565b604051808260ff1660ff16815260200191505060405180910390f35b6104f46114e6565b6040518082815260200191505060405180910390f35b61056c6004803603604081101561052057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506114ec565b005b610576611635565b6040518082815260200191505060405180910390f35b61059461163b565b6040518082815260200191505060405180910390f35b6105ec600480360360208110156105c057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611641565b6040518082815260200191505060405180910390f35b6106446004803603602081101561061857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611af2565b6040518082815260200191505060405180910390f35b610662611b0a565b6040518082815260200191505060405180910390f35b6106ba6004803603602081101561068e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611b10565b6040518082815260200191505060405180910390f35b610712600480360360208110156106e657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611b28565b604051808381526020018281526020019250505060405180910390f35b610737612115565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561077757808201518184015260208101905061075c565b50505050905090810190601f1680156107a45780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6107fe600480360360408110156107c857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061214e565b604051808215151515815260200191505060405180910390f35b610820612165565b6040518082815260200191505060405180910390f35b6108786004803603602081101561084c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061216b565b005b610882612446565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6108cc61246c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6109a5600480360360e081101561092457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050612492565b005b610a09600480360360408110156109bd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506127d6565b6040518082815260200191505060405180910390f35b610a276127fb565b005b6001600c5414610aa1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f556e697377617056323a204c4f434b454400000000000000000000000000000081525060200191505060405180910390fd5b6000600c819055506000851180610ab85750600084115b610b0d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602581526020018061397c6025913960400191505060405180910390fd5b600080610b1861124f565b5091509150816dffffffffffffffffffffffffffff1687108015610b4b5750806dffffffffffffffffffffffffffff1686105b610ba0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806139c56021913960400191505060405180910390fd5b6000806000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506000600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508173ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614158015610c5957508073ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614155b610ccb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f556e697377617056323a20494e56414c49445f544f000000000000000000000081525060200191505060405180910390fd5b60008b1115610ce057610cdf828a8d612a7b565b5b60008a1115610cf557610cf4818a8c612a7b565b5b6000888890501115610ddd578873ffffffffffffffffffffffffffffffffffffffff166310d1e85c338d8d8c8c6040518663ffffffff1660e01b8152600401808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509650505050505050600060405180830381600087803b158015610dc457600080fd5b505af1158015610dd8573d6000803e3d6000fd5b505050505b8173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015610e5a57600080fd5b505afa158015610e6e573d6000803e3d6000fd5b505050506040513d6020811015610e8457600080fd5b810190808051906020019092919050505093508073ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015610f1457600080fd5b505afa158015610f28573d6000803e3d6000fd5b505050506040513d6020811015610f3e57600080fd5b810190808051906020019092919050505092505050600089856dffffffffffffffffffffffffffff16038311610f75576000610f8b565b89856dffffffffffffffffffffffffffff160383035b9050600089856dffffffffffffffffffffffffffff16038311610faf576000610fc5565b89856dffffffffffffffffffffffffffff160383035b90506000821180610fd65750600081115b61102b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260248152602001806139a16024913960400191505060405180910390fd5b6000611067611044600385612cc890919063ffffffff16565b6110596103e888612cc890919063ffffffff16565b612d5d90919063ffffffff16565b905060006110a5611082600385612cc890919063ffffffff16565b6110976103e888612cc890919063ffffffff16565b612d5d90919063ffffffff16565b90506110ef620f42406110e1896dffffffffffffffffffffffffffff168b6dffffffffffffffffffffffffffff16612cc890919063ffffffff16565b612cc890919063ffffffff16565b6111028284612cc890919063ffffffff16565b1015611176576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600c8152602001807f556e697377617056323a204b000000000000000000000000000000000000000081525060200191505060405180910390fd5b505061118484848888612de0565b8873ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d82284848f8f6040518085815260200184815260200183815260200182815260200194505050505060405180910390a35050505050506001600c819055505050505050565b6040518060400160405280600a81526020017f556e69737761702056320000000000000000000000000000000000000000000081525081565b6000806000600860009054906101000a90046dffffffffffffffffffffffffffff1692506008600e9054906101000a90046dffffffffffffffffffffffffffff1691506008601c9054906101000a900463ffffffff169050909192565b60006112b933848461315e565b6001905092915050565b600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054146114a45761142382600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054612d5d90919063ffffffff16565b600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b6114af848484613249565b600190509392505050565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c960001b81565b601281565b60035481565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146115af576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f556e697377617056323a20464f5242494444454e00000000000000000000000081525060200191505060405180910390fd5b81600660006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b60095481565b600a5481565b60006001600c54146116bb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f556e697377617056323a204c4f434b454400000000000000000000000000000081525060200191505060405180910390fd5b6000600c819055506000806116ce61124f565b50915091506000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561177457600080fd5b505afa158015611788573d6000803e3d6000fd5b505050506040513d602081101561179e57600080fd5b810190808051906020019092919050505090506000600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561185257600080fd5b505afa158015611866573d6000803e3d6000fd5b505050506040513d602081101561187c57600080fd5b8101908080519060200190929190505050905060006118b4856dffffffffffffffffffffffffffff1684612d5d90919063ffffffff16565b905060006118db856dffffffffffffffffffffffffffff1684612d5d90919063ffffffff16565b905060006118e987876133dd565b9050600080549050600081141561193d576119296103e861191b6119168688612cc890919063ffffffff16565b6135be565b612d5d90919063ffffffff16565b985061193860006103e8613620565b6119a0565b61199d886dffffffffffffffffffffffffffff166119648387612cc890919063ffffffff16565b8161196b57fe5b04886dffffffffffffffffffffffffffff166119908487612cc890919063ffffffff16565b8161199757fe5b0461373a565b98505b600089116119f9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180613a0e6028913960400191505060405180910390fd5b611a038a8a613620565b611a0f86868a8a612de0565b8115611a8757611a806008600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16600860009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16612cc890919063ffffffff16565b600b819055505b3373ffffffffffffffffffffffffffffffffffffffff167f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f8585604051808381526020018281526020019250505060405180910390a250505050505050506001600c81905550919050565b60016020528060005260406000206000915090505481565b600b5481565b60046020528060005260406000206000915090505481565b6000806001600c5414611ba3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f556e697377617056323a204c4f434b454400000000000000000000000000000081525060200191505060405180910390fd5b6000600c81905550600080611bb661124f565b50915091506000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506000600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905060008273ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611c8857600080fd5b505afa158015611c9c573d6000803e3d6000fd5b505050506040513d6020811015611cb257600080fd5b8101908080519060200190929190505050905060008273ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611d4457600080fd5b505afa158015611d58573d6000803e3d6000fd5b505050506040513d6020811015611d6e57600080fd5b810190808051906020019092919050505090506000600160003073ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490506000611dd188886133dd565b905060008054905080611ded8685612cc890919063ffffffff16565b81611df457fe5b049a5080611e0b8585612cc890919063ffffffff16565b81611e1257fe5b04995060008b118015611e25575060008a115b611e7a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260288152602001806139e66028913960400191505060405180910390fd5b611e843084613753565b611e8f878d8d612a7b565b611e9a868d8c612a7b565b8673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611f1757600080fd5b505afa158015611f2b573d6000803e3d6000fd5b505050506040513d6020811015611f4157600080fd5b810190808051906020019092919050505094508573ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611fd157600080fd5b505afa158015611fe5573d6000803e3d6000fd5b505050506040513d6020811015611ffb57600080fd5b8101908080519060200190929190505050935061201a85858b8b612de0565b81156120925761208b6008600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16600860009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16612cc890919063ffffffff16565b600b819055505b8b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fdccd412f0b1252819cb1fd330b93224ca42612892bb3f4f789976e6d819364968d8d604051808381526020018281526020019250505060405180910390a35050505050505050506001600c81905550915091565b6040518060400160405280600681526020017f554e492d5632000000000000000000000000000000000000000000000000000081525081565b600061215b338484613249565b6001905092915050565b6103e881565b6001600c54146121e3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f556e697377617056323a204c4f434b454400000000000000000000000000000081525060200191505060405180910390fd5b6000600c819055506000600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506000600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506123398284612334600860009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156122eb57600080fd5b505afa1580156122ff573d6000803e3d6000fd5b505050506040513d602081101561231557600080fd5b8101908080519060200190929190505050612d5d90919063ffffffff16565b612a7b565b61243981846124346008600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156123eb57600080fd5b505afa1580156123ff573d6000803e3d6000fd5b505050506040513d602081101561241557600080fd5b8101908080519060200190929190505050612d5d90919063ffffffff16565b612a7b565b50506001600c8190555050565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b42841015612508576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f556e697377617056323a2045585049524544000000000000000000000000000081525060200191505060405180910390fd5b60006003547f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c960001b898989600460008e73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000815480929190600101919050558a604051602001808781526020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200184815260200183815260200182815260200196505050505050506040516020818303038152906040528051906020012060405160200180807f190100000000000000000000000000000000000000000000000000000000000081525060020183815260200182815260200192505050604051602081830303815290604052805190602001209050600060018286868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156126da573d6000803e3d6000fd5b505050602060405103519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415801561274e57508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b6127c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f556e697377617056323a20494e56414c49445f5349474e41545552450000000081525060200191505060405180910390fd5b6127cb89898961315e565b505050505050505050565b6002602052816000526040600020602052806000526040600020600091509150505481565b6001600c5414612873576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f556e697377617056323a204c4f434b454400000000000000000000000000000081525060200191505060405180910390fd5b6000600c81905550612a71600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561291d57600080fd5b505afa158015612931573d6000803e3d6000fd5b505050506040513d602081101561294757600080fd5b8101908080519060200190929190505050600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156129f757600080fd5b505afa158015612a0b573d6000803e3d6000fd5b505050506040513d6020811015612a2157600080fd5b8101908080519060200190929190505050600860009054906101000a90046dffffffffffffffffffffffffffff166008600e9054906101000a90046dffffffffffffffffffffffffffff16612de0565b6001600c81905550565b600060608473ffffffffffffffffffffffffffffffffffffffff166040518060400160405280601981526020017f7472616e7366657228616464726573732c75696e743235362900000000000000815250805190602001208585604051602401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040518082805190602001908083835b60208310612ba85780518252602082019150602081019050602083039250612b85565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612c0a576040519150601f19603f3d011682016040523d82523d6000602084013e612c0f565b606091505b5091509150818015612c4f5750600081511480612c4e5750808060200190516020811015612c3c57600080fd5b81019080805190602001909291905050505b5b612cc1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f556e697377617056323a205452414e534645525f4641494c454400000000000081525060200191505060405180910390fd5b5050505050565b600080821480612ce55750828283850292508281612ce257fe5b04145b612d57576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f64732d6d6174682d6d756c2d6f766572666c6f7700000000000000000000000081525060200191505060405180910390fd5b92915050565b6000828284039150811115612dda576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f64732d6d6174682d7375622d756e646572666c6f77000000000000000000000081525060200191505060405180910390fd5b92915050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6dffffffffffffffffffffffffffff168411158015612e5057507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6dffffffffffffffffffffffffffff168311155b612ec2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f556e697377617056323a204f564552464c4f570000000000000000000000000081525060200191505060405180910390fd5b60006401000000004281612ed257fe5b06905060006008601c9054906101000a900463ffffffff168203905060008163ffffffff16118015612f1557506000846dffffffffffffffffffffffffffff1614155b8015612f3257506000836dffffffffffffffffffffffffffff1614155b15613014578063ffffffff16612f7785612f4b8661386d565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1661389890919063ffffffff16565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16026009600082825401925050819055508063ffffffff16612fe584612fb98761386d565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1661389890919063ffffffff16565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1602600a600082825401925050819055505b85600860006101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff160217905550846008600e6101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff160217905550816008601c6101000a81548163ffffffff021916908363ffffffff1602179055507f1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1600860009054906101000a90046dffffffffffffffffffffffffffff166008600e9054906101000a90046dffffffffffffffffffffffffffff1660405180836dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff168152602001826dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff1681526020019250505060405180910390a1505050505050565b80600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040518082815260200191505060405180910390a3505050565b61329b81600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054612d5d90919063ffffffff16565b600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061333081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546138f890919063ffffffff16565b600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a3505050565b600080600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663017e7e586040518163ffffffff1660e01b815260040160206040518083038186803b15801561344857600080fd5b505afa15801561345c573d6000803e3d6000fd5b505050506040513d602081101561347257600080fd5b81019080805190602001909291905050509050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141591506000600b54905082156135a4576000811461359f57600061350a613505866dffffffffffffffffffffffffffff16886dffffffffffffffffffffffffffff16612cc890919063ffffffff16565b6135be565b90506000613517836135be565b90508082111561359c57600061354a6135398385612d5d90919063ffffffff16565b600054612cc890919063ffffffff16565b9050600061357483613566600587612cc890919063ffffffff16565b6138f890919063ffffffff16565b9050600081838161358157fe5b0490506000811115613598576135978782613620565b5b5050505b50505b6135b6565b600081146135b5576000600b819055505b5b505092915050565b6000600382111561360d5781905060006001600284816135da57fe5b040190505b81811015613607578091506002818285816135f657fe5b0401816135ff57fe5b0490506135df565b5061361b565b6000821461361a57600190505b5b919050565b613635816000546138f890919063ffffffff16565b60008190555061368d81600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546138f890919063ffffffff16565b600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35050565b6000818310613749578161374b565b825b905092915050565b6137a581600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054612d5d90919063ffffffff16565b600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506137fd81600054612d5d90919063ffffffff16565b600081905550600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35050565b60006e010000000000000000000000000000826dffffffffffffffffffffffffffff16029050919050565b6000816dffffffffffffffffffffffffffff167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16816138ef57fe5b04905092915050565b6000828284019150811015613975576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f64732d6d6174682d6164642d6f766572666c6f7700000000000000000000000081525060200191505060405180910390fd5b9291505056fe556e697377617056323a20494e53554646494349454e545f4f55545055545f414d4f554e54556e697377617056323a20494e53554646494349454e545f494e5055545f414d4f554e54556e697377617056323a20494e53554646494349454e545f4c4951554944495459556e697377617056323a20494e53554646494349454e545f4c49515549444954595f4255524e4544556e697377617056323a20494e53554646494349454e545f4c49515549444954595f4d494e544544a265627a7a723158201d9cc769ec1244e0335eb71f99a94869ae6ea3596ebb2c69f8185f53f440e2c664736f6c63430005100032"
+ bench/contracts/erc20.sol view
@@ -0,0 +1,42 @@+contract ERC20 {+ // --- ERC20 Data ---+ string public constant name = "TOKEN";+ string public constant symbol = "TKN";+ 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);++ constructor(uint supply) public {+ balanceOf[msg.sender] = supply;+ totalSupply = supply;+ }++ // --- 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, "erc20/insufficient-balance");+ if (src != msg.sender && allowance[src][msg.sender] != type(uint).max) {+ require(allowance[src][msg.sender] >= wad, "erc20/insufficient-allowance");+ allowance[src][msg.sender] -= wad;+ }+ balanceOf[src] -= wad;+ balanceOf[dst] += wad;+ emit Transfer(src, dst, wad);+ return true;+ }+ function approve(address usr, uint wad) external returns (bool) {+ allowance[msg.sender][usr] = wad;+ emit Approval(msg.sender, usr, wad);+ return true;+ }+}
hevm-cli/hevm-cli.hs view
@@ -16,6 +16,7 @@ import EVM.Debug import qualified EVM.Expr as Expr import EVM.SMT+import EVM.Solvers import qualified EVM.TTY as TTY import EVM.Solidity import EVM.Expr (litAddr)@@ -187,8 +188,8 @@ optsMode :: Command Options.Unwrapped -> Mode optsMode x- | Main.debug x = Debug- | jsontrace x = JsonTrace+ | x.debug = Debug+ | x.jsontrace = JsonTrace | otherwise = Run applyCache :: (Maybe String, Maybe String) -> IO (EVM.VM -> EVM.VM)@@ -211,51 +212,51 @@ unitTestOptions :: Command Options.Unwrapped -> SolverGroup -> String -> IO UnitTestOptions unitTestOptions cmd solvers testFile = do- let root = fromMaybe "." (dappRoot cmd)+ let root = fromMaybe "." cmd.dappRoot srcInfo <- readSolc testFile >>= \case Nothing -> error "Could not read .sol.json file" Just (contractMap, sourceCache) -> pure $ dappInfo root contractMap sourceCache - vmModifier <- applyCache (state cmd, cache cmd)+ vmModifier <- applyCache (cmd.state, cmd.cache) - params <- getParametersFromEnvironmentVariables (rpc cmd)+ params <- getParametersFromEnvironmentVariables cmd.rpc let- testn = testNumber params+ testn = params.testNumber block' = if 0 == testn then EVM.Fetch.Latest else EVM.Fetch.BlockNumber testn pure EVM.UnitTest.UnitTestOptions- { EVM.UnitTest.solvers = solvers- , EVM.UnitTest.rpcInfo = case rpc cmd of+ { solvers = solvers+ , rpcInfo = case cmd.rpc of Just url -> Just (block', url) Nothing -> Nothing- , EVM.UnitTest.maxIter = maxIterations cmd- , EVM.UnitTest.askSmtIters = askSmtIterations cmd- , EVM.UnitTest.smtDebug = smtdebug cmd- , EVM.UnitTest.smtTimeout = smttimeout cmd- , EVM.UnitTest.solver = solver cmd- , EVM.UnitTest.covMatch = pack <$> covMatch cmd- , EVM.UnitTest.verbose = verbose cmd- , EVM.UnitTest.match = pack $ fromMaybe ".*" (match cmd)- , EVM.UnitTest.maxDepth = depth cmd- , EVM.UnitTest.fuzzRuns = fromMaybe 100 (fuzzRuns cmd)- , EVM.UnitTest.replay = do- arg' <- replay cmd+ , 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 return (fst arg', LazyByteString.fromStrict (hexByteString "--replay" $ strip0x $ snd arg'))- , EVM.UnitTest.vmModifier = vmModifier- , EVM.UnitTest.testParams = params- , EVM.UnitTest.dapp = srcInfo- , EVM.UnitTest.ffiAllowed = ffi cmd+ , vmModifier = vmModifier+ , testParams = params+ , dapp = srcInfo+ , ffiAllowed = cmd.ffi } main :: IO () main = do cmd <- Options.unwrapRecord "hevm -- Ethereum evaluator" let- root = fromMaybe "." (dappRoot cmd)+ root = fromMaybe "." cmd.dappRoot case cmd of Version {} -> putStrLn (showVersion Paths.version) Symbolic {} -> withCurrentDirectory root $ assert cmd@@ -265,12 +266,12 @@ DappTest {} -> withCurrentDirectory root $ do cores <- num <$> getNumProcessors- withSolvers Z3 cores (smttimeout cmd) $ \solvers -> do- testFile <- findJsonFile (jsonFile cmd)+ withSolvers Z3 cores cmd.smttimeout $ \solvers -> do+ testFile <- findJsonFile cmd.jsonFile testOpts <- unitTestOptions cmd solvers testFile- case (coverage cmd, optsMode cmd) of+ case (cmd.coverage, optsMode cmd) of (False, Run) -> do- res <- dappTest testOpts testFile (cache cmd)+ res <- dappTest testOpts testFile cmd.cache unless res exitFailure (False, Debug) -> liftIO $ TTY.main testOpts root testFile (False, JsonTrace) -> error "json traces not implemented for dappTest"@@ -298,21 +299,21 @@ equivalence :: Command Options.Unwrapped -> IO () equivalence cmd = do- let bytecodeA = hexByteString "--code" . strip0x $ codeA cmd- bytecodeB = hexByteString "--code" . strip0x $ codeB cmd+ let bytecodeA = hexByteString "--code" . strip0x $ cmd.codeA+ bytecodeB = hexByteString "--code" . strip0x $ cmd.codeB veriOpts = VeriOpts { simp = True , debug = False- , maxIter = maxIterations cmd- , askSmtIters = askSmtIterations cmd+ , maxIter = cmd.maxIterations+ , askSmtIters = cmd.askSmtIterations , rpcInfo = Nothing } withSolvers Z3 3 Nothing $ \s -> do res <- equivalenceCheck s bytecodeA bytecodeB veriOpts Nothing- case containsA (Cex()) res of+ case not (any isCex res) of False -> do putStrLn "No discrepancies found"- when (containsA (EVM.SymExec.Timeout()) res) $ do+ when (any isTimeout res) $ do putStrLn "But timeout(s) occurred" exitFailure True -> do@@ -321,8 +322,8 @@ getSrcInfo :: Command Options.Unwrapped -> IO DappInfo getSrcInfo cmd =- let root = fromMaybe "." (dappRoot cmd)- in case (jsonFile cmd) of+ let root = fromMaybe "." cmd.dappRoot+ in case cmd.jsonFile of Nothing -> pure emptyDapp Just json -> readSolc json >>= \case@@ -339,10 +340,10 @@ -- 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 (block cmd)- rpcinfo = (,) block' <$> rpc cmd+ let block' = maybe EVM.Fetch.Latest EVM.Fetch.BlockNumber cmd.block+ rpcinfo = (,) block' <$> cmd.rpc decipher = hexByteString "bytes" . strip0x- calldata' <- case (Main.calldata cmd, sig cmd) of+ calldata' <- case (cmd.calldata, cmd.sig) of -- fully abstract calldata (Nothing, Nothing) -> pure ( AbstractBuf "txdata"@@ -350,7 +351,7 @@ -- this is way larger than would ever be allowed by the gas limit -- and avoids spurious counterexamples during abi decoding -- TODO: can we encode calldata as an array with a smaller length?- , [Expr.bufLength (AbstractBuf "txtdata") .< (Lit (2 ^ (64 :: Integer)))]+ , [Expr.bufLength (AbstractBuf "txdata") .< (Lit (2 ^ (64 :: Integer)))] ) -- fully concrete calldata@@ -358,25 +359,25 @@ -- calldata according to given abi with possible specializations from the `arg` list (Nothing, Just sig') -> do method' <- functionAbi sig'- let typs = snd <$> view methodInputs method'- pure $ symCalldata (view methodSignature method') typs (arg cmd) mempty+ let typs = snd <$> method'.inputs+ pure $ symCalldata method'.methodSignature typs cmd.arg (AbstractBuf "txdata") _ -> error "incompatible options: calldata and abi" preState <- symvmFromCommand cmd calldata'- let errCodes = fromMaybe defaultPanicCodes (assertions cmd)+ let errCodes = fromMaybe defaultPanicCodes cmd.assertions cores <- num <$> getNumProcessors- let solverCount = fromMaybe cores (numSolvers cmd)- withSolvers EVM.SMT.Z3 solverCount (smttimeout cmd) $ \solvers -> do- if Main.debug cmd then do+ let solverCount = fromMaybe cores cmd.numSolvers+ withSolvers EVM.Solvers.Z3 solverCount cmd.smttimeout $ \solvers -> do+ if cmd.debug then do srcInfo <- getSrcInfo cmd void $ TTY.runFromVM solvers rpcinfo- (maxIterations cmd)+ cmd.maxIterations srcInfo preState else do- let opts = VeriOpts { simp = True, debug = smtdebug cmd, maxIter = maxIterations cmd, askSmtIters = askSmtIterations cmd, rpcInfo = rpcinfo}+ let opts = VeriOpts { simp = True, debug = cmd.smtdebug, maxIter = cmd.maxIterations, askSmtIters = cmd.askSmtIterations, rpcInfo = rpcinfo} (expr, res) <- verify solvers opts preState (Just $ checkAssertions errCodes) case res of [Qed _] -> putStrLn "\nQED: No reachable property violations discovered\n"@@ -396,16 +397,17 @@ , "" ] <> fmap (formatExpr) (getTimeouts cexs) T.putStrLn $ T.unlines (counterexamples <> unknowns)- when (showTree cmd) $ do+ exitFailure+ when cmd.showTree $ do putStrLn "=== Expression ===\n" T.putStrLn $ formatExpr expr putStrLn ""- when (showReachableTree cmd) $ do+ when cmd.showReachableTree $ do reached <- reachable solvers expr putStrLn "=== Reachable Expression ===\n" T.putStrLn (formatExpr . snd $ reached) putStrLn ""- when (getModels cmd) $ do+ when cmd.getModels $ do putStrLn $ "=== Models for " <> show (Expr.numBranches expr) <> " branches ===\n" ms <- produceModels solvers expr forM_ ms (showModel (fst calldata'))@@ -427,13 +429,13 @@ readSolc solcFile >>= \case Just (contractMap, sourceCache) -> do- let unitTests = findUnitTests (EVM.UnitTest.match opts) $ Map.elems contractMap+ let unitTests = findUnitTests opts.match $ Map.elems contractMap covs <- mconcat <$> mapM (coverageForUnitTestContract opts contractMap sourceCache) unitTests let dapp = dappInfo "." contractMap sourceCache f (k, vs) = do- when (shouldPrintCoverage (EVM.UnitTest.covMatch opts) k) $ do+ when (shouldPrintCoverage opts.covMatch k) $ do putStr ("\x1b[0m" ++ "————— hevm coverage for ") -- Prefixed with color reset putStrLn (unpack k ++ " —————") putStrLn ""@@ -470,7 +472,7 @@ case optsMode cmd of Run -> do vm' <- execStateT (EVM.Stepper.interpret (EVM.Fetch.oracle solvers rpcinfo) . void $ EVM.Stepper.execFully) vm- when (trace cmd) $ T.hPutStr stderr (showTraceTree dapp vm')+ when cmd.trace $ T.hPutStr stderr (showTraceTree dapp vm') case view EVM.result vm' of Nothing -> error "internal error; no EVM result"@@ -488,11 +490,11 @@ ConcreteBuf msg' -> msg' _ -> "<symbolic>" print $ ByteStringS msg- case state cmd of+ case cmd.state of Nothing -> pure () Just path -> Git.saveFacts (Git.RepoAt path) (Facts.vmFacts vm')- case cache cmd of+ case cmd.cache of Nothing -> pure () Just path -> Git.saveFacts (Git.RepoAt path) (Facts.cacheFacts (view EVM.cache vm'))@@ -500,15 +502,15 @@ Debug -> void $ TTY.runFromVM solvers rpcinfo Nothing dapp vm --JsonTrace -> void $ execStateT (interpretWithTrace fetcher EVM.Stepper.runFully) vm _ -> error "TODO"- where block' = maybe EVM.Fetch.Latest EVM.Fetch.BlockNumber (block cmd)- rpcinfo = (,) block' <$> rpc cmd+ 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 EVM.VM vmFromCommand cmd = do- withCache <- applyCache (state cmd, cache cmd)+ withCache <- applyCache (cmd.state, cmd.cache) - (miner,ts,baseFee,blockNum,prevRan) <- case rpc cmd of+ (miner,ts,baseFee,blockNum,prevRan) <- case cmd.rpc of Nothing -> return (0,Lit 0,0,0,0) Just url -> EVM.Fetch.fetchBlockFrom block' url >>= \case Nothing -> error "Could not fetch block"@@ -519,7 +521,7 @@ , _prevRandao ) - contract <- case (rpc cmd, address cmd, code cmd) of+ contract <- case (cmd.rpc, cmd.address, cmd.code) of (Just url, Just addr', Just c) -> do EVM.Fetch.fetchContractFrom block' url addr' >>= \case Nothing ->@@ -552,43 +554,43 @@ return $ EVM.Transaction.initTx $ withCache (vm0 baseFee miner ts' blockNum prevRan contract) where- block' = maybe EVM.Fetch.Latest EVM.Fetch.BlockNumber (block cmd)- value' = word value 0- caller' = addr caller 0- origin' = addr origin 0- calldata' = ConcreteBuf $ bytes Main.calldata ""+ 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 create cmd+ mkCode bs = if cmd.create then EVM.InitCode bs mempty else EVM.RuntimeCode (EVM.ConcreteRuntimeCode bs)- address' = if create cmd- then addr address (createAddress origin' (word nonce 0))- else addr address 0xacab+ address' = if cmd.create+ then addr (.address) (createAddress origin' (word (.nonce) 0))+ else addr (.address) 0xacab vm0 baseFee miner ts blockNum prevRan c = EVM.makeVm $ EVM.VMOpts- { EVM.vmoptContract = c- , EVM.vmoptCalldata = (calldata', [])- , EVM.vmoptValue = Lit value'- , EVM.vmoptAddress = address'- , EVM.vmoptCaller = litAddr caller'- , EVM.vmoptOrigin = origin'- , EVM.vmoptGas = word64 gas 0xffffffffffffffff- , EVM.vmoptBaseFee = baseFee- , EVM.vmoptPriorityFee = word priorityFee 0- , EVM.vmoptGaslimit = word64 gaslimit 0xffffffffffffffff- , EVM.vmoptCoinbase = addr coinbase miner- , EVM.vmoptNumber = word number blockNum- , EVM.vmoptTimestamp = Lit $ word timestamp ts- , EVM.vmoptBlockGaslimit = word64 gaslimit 0xffffffffffffffff- , EVM.vmoptGasprice = word gasprice 0- , EVM.vmoptMaxCodeSize = word maxcodesize 0xffffffff- , EVM.vmoptPrevRandao = word prevRandao prevRan- , EVM.vmoptSchedule = FeeSchedule.berlin- , EVM.vmoptChainId = word chainid 1- , EVM.vmoptCreate = create cmd- , EVM.vmoptStorageBase = EVM.Concrete- , EVM.vmoptTxAccessList = mempty -- TODO: support me soon- , EVM.vmoptAllowFFI = False+ { vmoptContract = c+ , vmoptCalldata = (calldata', [])+ , vmoptValue = Lit value'+ , vmoptAddress = address'+ , vmoptCaller = litAddr caller'+ , vmoptOrigin = origin'+ , vmoptGas = word64 (.gas) 0xffffffffffffffff+ , vmoptBaseFee = baseFee+ , vmoptPriorityFee = word (.priorityFee) 0+ , vmoptGaslimit = word64 (.gaslimit) 0xffffffffffffffff+ , vmoptCoinbase = addr (.coinbase) miner+ , vmoptNumber = word (.number) blockNum+ , vmoptTimestamp = Lit $ word (.timestamp) ts+ , vmoptBlockGaslimit = word64 (.gaslimit) 0xffffffffffffffff+ , vmoptGasprice = word (.gasprice) 0+ , vmoptMaxCodeSize = word (.maxcodesize) 0xffffffff+ , vmoptPrevRandao = word (.prevRandao) prevRan+ , vmoptSchedule = FeeSchedule.berlin+ , vmoptChainId = word (.chainid) 1+ , vmoptCreate = (.create) cmd+ , vmoptStorageBase = EVM.Concrete+ , vmoptTxAccessList = mempty -- TODO: support me soon+ , vmoptAllowFFI = False } word f def = fromMaybe def (f cmd) word64 f def = fromMaybe def (f cmd)@@ -597,7 +599,7 @@ symvmFromCommand :: Command Options.Unwrapped -> (Expr Buf, [Prop]) -> IO (EVM.VM) symvmFromCommand cmd calldata' = do- (miner,blockNum,baseFee,prevRan) <- case rpc cmd of+ (miner,blockNum,baseFee,prevRan) <- case cmd.rpc of Nothing -> return (0,0,0,0) Just url -> EVM.Fetch.fetchBlockFrom block' url >>= \case Nothing -> error "Could not fetch block"@@ -609,10 +611,10 @@ let caller' = Caller 0- ts = maybe Timestamp Lit (timestamp cmd)- callvalue' = maybe (CallValue 0) Lit (value cmd)+ ts = maybe Timestamp Lit cmd.timestamp+ callvalue' = maybe (CallValue 0) Lit cmd.value -- TODO: rework this, ConcreteS not needed anymore- let store = case storageModel cmd of+ let store = case cmd.storageModel of -- InitialS and SymbolicS can read and write to symbolic locations -- ConcreteS cannot (instead values can be fetched from rpc!) -- Initial defaults to 0 for uninitialized storage slots,@@ -620,18 +622,18 @@ Just InitialS -> EmptyStore Just ConcreteS -> ConcreteStore mempty Just SymbolicS -> AbstractStore- Nothing -> if create cmd then EmptyStore else AbstractStore+ Nothing -> if cmd.create then EmptyStore else AbstractStore - withCache <- applyCache (state cmd, cache cmd)+ withCache <- applyCache (cmd.state, cmd.cache) - contract' <- case (rpc cmd, address cmd, code cmd) of+ contract' <- case (cmd.rpc, cmd.address, cmd.code) of (Just url, Just addr', _) -> EVM.Fetch.fetchContractFrom block' url addr' >>= \case Nothing -> error "contract not found." Just contract' -> return contract'' where- contract'' = case code cmd of+ contract'' = case cmd.code of Nothing -> contract' -- if both code and url is given, -- fetch the contract and overwrite the code@@ -652,38 +654,38 @@ where decipher = hexByteString "bytes" . strip0x- block' = maybe EVM.Fetch.Latest EVM.Fetch.BlockNumber (block cmd)- origin' = addr origin 0- mkCode bs = if create cmd+ block' = maybe EVM.Fetch.Latest EVM.Fetch.BlockNumber cmd.block+ origin' = addr (.origin) 0+ mkCode bs = if cmd.create then EVM.InitCode bs mempty else EVM.RuntimeCode (EVM.ConcreteRuntimeCode bs)- address' = if create cmd- then addr address (createAddress origin' (word nonce 0))- else addr address 0xacab+ 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 = EVM.makeVm $ EVM.VMOpts- { EVM.vmoptContract = c- , EVM.vmoptCalldata = cd'- , EVM.vmoptValue = callvalue'- , EVM.vmoptAddress = address'- , EVM.vmoptCaller = caller'- , EVM.vmoptOrigin = origin'- , EVM.vmoptGas = word64 gas 0xffffffffffffffff- , EVM.vmoptGaslimit = word64 gaslimit 0xffffffffffffffff- , EVM.vmoptBaseFee = baseFee- , EVM.vmoptPriorityFee = word priorityFee 0- , EVM.vmoptCoinbase = addr coinbase miner- , EVM.vmoptNumber = word number blockNum- , EVM.vmoptTimestamp = ts- , EVM.vmoptBlockGaslimit = word64 gaslimit 0xffffffffffffffff- , EVM.vmoptGasprice = word gasprice 0- , EVM.vmoptMaxCodeSize = word maxcodesize 0xffffffff- , EVM.vmoptPrevRandao = word prevRandao prevRan- , EVM.vmoptSchedule = FeeSchedule.berlin- , EVM.vmoptChainId = word chainid 1- , EVM.vmoptCreate = create cmd- , EVM.vmoptStorageBase = EVM.Symbolic- , EVM.vmoptTxAccessList = mempty- , EVM.vmoptAllowFFI = False+ { vmoptContract = c+ , vmoptCalldata = cd'+ , vmoptValue = callvalue'+ , vmoptAddress = address'+ , vmoptCaller = caller'+ , vmoptOrigin = origin'+ , vmoptGas = word64 (.gas) 0xffffffffffffffff+ , vmoptGaslimit = word64 (.gaslimit) 0xffffffffffffffff+ , vmoptBaseFee = baseFee+ , vmoptPriorityFee = word (.priorityFee) 0+ , vmoptCoinbase = addr (.coinbase) miner+ , vmoptNumber = word (.number) blockNum+ , vmoptTimestamp = ts+ , vmoptBlockGaslimit = word64 (.gaslimit) 0xffffffffffffffff+ , vmoptGasprice = word (.gasprice) 0+ , vmoptMaxCodeSize = word (.maxcodesize) 0xffffffff+ , vmoptPrevRandao = word (.prevRandao) prevRan+ , vmoptSchedule = FeeSchedule.berlin+ , vmoptChainId = word (.chainid) 1+ , vmoptCreate = (.create) cmd+ , vmoptStorageBase = EVM.Symbolic+ , vmoptTxAccessList = mempty+ , vmoptAllowFFI = False } word f def = fromMaybe def (f cmd) addr f def = fromMaybe def (f cmd)
hevm.cabal view
@@ -2,7 +2,7 @@ name: hevm version:- 0.50.2+ 0.50.3 synopsis: Ethereum virtual machine evaluator description:@@ -24,6 +24,7 @@ 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@@ -45,6 +46,11 @@ default: False manual: True +flag devel+ description: Sets flag for compilation during development+ default: False+ manual: True+ source-repository head type: git location: https://github.com/ethereum/hevm.git@@ -52,9 +58,14 @@ common shared if flag(ci) ghc-options: -Werror+ if flag(devel)+ ghc-options: -j default-language: GHC2021 default-extensions:+ DuplicateRecordFields LambdaCase+ NoFieldSelectors+ OverloadedRecordDot OverloadedStrings RecordWildCards TypeFamilies@@ -72,6 +83,7 @@ EVM.Dev, EVM.Expr, EVM.SMT,+ EVM.Solvers, EVM.Exec, EVM.Facts, EVM.Facts.Git,@@ -99,8 +111,10 @@ Paths_hevm autogen-modules: Paths_hevm- ghc-options:- -Wall -Wno-deprecations -Wno-unticked-promoted-constructors -Wno-orphans+ if flag(devel)+ ghc-options: -Wall -Wno-deprecations -Wno-unticked-promoted-constructors -Wno-orphans -j+ else+ ghc-options: -Wall -Wno-deprecations -Wno-unticked-promoted-constructors -Wno-orphans extra-libraries: secp256k1, ff if os(linux)@@ -164,6 +178,8 @@ smt2-parser >= 0.1.0.1, word-wrap >= 0.5 && < 0.6, spool >= 0.1 && < 0.2,+ stm >= 2.5.0 && < 2.6.0,+ spawn >= 0.3 && < 0.4 hs-source-dirs: src @@ -173,8 +189,10 @@ hevm-cli main-is: hevm-cli.hs- ghc-options:- -Wall -threaded -with-rtsopts=-N -Wno-unticked-promoted-constructors -Wno-orphans+ if flag(devel)+ ghc-options: -Wall -threaded -with-rtsopts=-N -Wno-unticked-promoted-constructors -Wno-orphans -j+ else+ ghc-options: -Wall -threaded -with-rtsopts=-N -Wno-unticked-promoted-constructors -Wno-orphans other-modules: Paths_hevm if os(darwin)@@ -213,12 +231,18 @@ text, unordered-containers, vector,- vty+ vty,+ stm,+ spawn +--- Test Helpers ---+ common test-base import: shared- ghc-options:- -Wall -Wno-unticked-promoted-constructors -Wno-orphans+ if flag (devel)+ ghc-options: -Wall -Wno-unticked-promoted-constructors -Wno-orphans -j+ else+ ghc-options: -Wall -Wno-unticked-promoted-constructors -Wno-orphans hs-source-dirs: test extra-libraries:@@ -257,6 +281,9 @@ time, array, vector,+ tasty-bench,+ stm >= 2.5.0,+ spawn >= 0.3, witherable, smt2-parser >= 0.1.0.1 @@ -307,3 +334,30 @@ exitcode-stdio-1.0 main-is: BlockchainTests.hs++--- Benchmarks ---++benchmark bench+ import: shared+ type:+ exitcode-stdio-1.0+ main-is:+ bench.hs+ hs-source-dirs:+ bench+ if os(darwin)+ extra-libraries: c+++ else+ extra-libraries: stdc+++ other-modules:+ Paths_hevm+ autogen-modules:+ Paths_hevm+ build-depends:+ base,+ tasty-bench,+ tasty,+ bytestring,+ text,+ hevm,+ here
src/EVM.hs view
@@ -8,56 +8,53 @@ import Prelude hiding (log, exponent, GT, LT) -import Data.Text (unpack)-import Data.Text.Encoding (decodeUtf8, encodeUtf8) import EVM.ABI-import EVM.Types hiding (IllegalOverflow, Error)-import EVM.Solidity import EVM.Concrete (createAddress, create2Address)-import EVM.Op-import EVM.Expr (readStorage, writeStorage, readByte, readWord, writeWord, writeByte, bufLength, indexWord, litAddr, readBytes, word256At, copySlice)+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 Options.Generic as Options-import qualified EVM.Precompiled-import qualified EVM.Expr as Expr+import EVM.Op+import EVM.Precompiled qualified+import EVM.Solidity+import EVM.Types hiding (IllegalOverflow, Error) import Control.Lens hiding (op, (:<), (|>), (.>)) import Control.Monad.State.Strict hiding (state)--import Data.ByteString (ByteString)-import Data.ByteString.Lazy (fromStrict)-import Data.Map.Strict (Map)-import Data.Set (Set, insert, member, fromList)-import Data.Maybe (fromMaybe, fromJust)-import Data.Sequence (Seq)-import Data.Vector.Storable (Vector)-import Data.Foldable (toList)-import Data.Word (Word8, Word32, Word64)-import Data.Bits (FiniteBits, countLeadingZeros, finiteBitSize)-+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 (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.List (find)--import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LS-import qualified Data.ByteString.Char8 as Char8-import qualified Data.ByteArray as BA-import qualified Data.Map.Strict as Map-import qualified Data.Sequence as Seq-import qualified Data.Tree.Zipper as Zipper-import qualified Data.Vector as V-import qualified Data.Vector.Storable as Vector-import qualified Data.Vector.Storable.Mutable as Vector--import qualified Data.Vector as RegularVector+import Data.Vector qualified as RegularVector+import Data.Vector qualified as V+import Data.Vector.Storable (Vector)+import Data.Vector.Storable qualified as Vector+import Data.Vector.Storable.Mutable qualified as Vector+import Data.Word (Word8, Word32, Word64)+import Options.Generic as Options -import Crypto.Number.ModArithmetic (expFast)-import qualified Crypto.Hash as Crypto import Crypto.Hash (Digest, SHA256, RIPEMD160, digestFromByteString)+import Crypto.Hash qualified as Crypto+import Crypto.Number.ModArithmetic (expFast) import Crypto.PubKey.ECC.ECDSA (signDigestWith, PrivateKey(..), Signature(..))-import Crypto.PubKey.ECC.Types (getCurveByName, CurveName(..), Point(..)) import Crypto.PubKey.ECC.Generate (generateQ)+import Crypto.PubKey.ECC.Types (getCurveByName, CurveName(..), Point(..)) -- * Data types @@ -112,6 +109,7 @@ , _constraints :: [Prop] , _keccakEqs :: [Prop] , _allowFFI :: Bool+ , _overrideCaller :: Maybe (Expr EWord) } deriving (Show) @@ -433,9 +431,9 @@ instance Semigroup Cache where a <> b = Cache- { _fetchedContracts = Map.unionWith unifyCachedContract (view fetchedContracts a) (view fetchedContracts b)- , _fetchedStorage = Map.unionWith unifyCachedStorage (view fetchedStorage a) (view fetchedStorage b)- , _path = mappend (view path a) (view path b)+ { _fetchedContracts = Map.unionWith unifyCachedContract a._fetchedContracts b._fetchedContracts+ , _fetchedStorage = Map.unionWith unifyCachedStorage a._fetchedStorage b._fetchedStorage+ , _path = mappend a._path b._path } unifyCachedStorage :: Map W256 W256 -> Map W256 W256 -> Map W256 W256@@ -464,78 +462,79 @@ currentContract :: VM -> Maybe Contract currentContract vm =- view (env . contracts . at (view (state . codeContract) vm)) vm+ Map.lookup vm._state._codeContract vm._env._contracts -- * Data constructors makeVm :: VMOpts -> VM makeVm o =- let txaccessList = vmoptTxAccessList o- txorigin = vmoptOrigin o- txtoAddr = vmoptAddress o+ let txaccessList = o.vmoptTxAccessList+ txorigin = o.vmoptOrigin+ txtoAddr = o.vmoptAddress initialAccessedAddrs = fromList $ [txorigin, txtoAddr] ++ [1..9] ++ (Map.keys txaccessList) initialAccessedStorageKeys = fromList $ foldMap (uncurry (map . (,))) (Map.toList txaccessList)- touched = if vmoptCreate o then [txorigin] else [txorigin, txtoAddr]+ touched = if o.vmoptCreate then [txorigin] else [txorigin, txtoAddr] in VM { _result = Nothing , _frames = mempty , _tx = TxState- { _gasprice = vmoptGasprice o- , _txgaslimit = vmoptGaslimit o- , _txPriorityFee = vmoptPriorityFee o+ { _gasprice = o.vmoptGasprice+ , _txgaslimit = o.vmoptGaslimit+ , _txPriorityFee = o.vmoptPriorityFee , _origin = txorigin , _toAddr = txtoAddr- , _value = vmoptValue o+ , _value = o.vmoptValue , _substate = SubState mempty touched initialAccessedAddrs initialAccessedStorageKeys mempty --, _accessList = txaccessList- , _isCreate = vmoptCreate o+ , _isCreate = o.vmoptCreate , _txReversion = Map.fromList- [(vmoptAddress o, vmoptContract o)]+ [(o.vmoptAddress , o.vmoptContract )] } , _logs = [] , _traces = Zipper.fromForest [] , _block = Block- { _coinbase = vmoptCoinbase o- , _timestamp = vmoptTimestamp o- , _number = vmoptNumber o- , _prevRandao = vmoptPrevRandao o- , _maxCodeSize = vmoptMaxCodeSize o- , _gaslimit = vmoptBlockGaslimit o- , _baseFee = vmoptBaseFee o- , _schedule = vmoptSchedule o+ { _coinbase = o.vmoptCoinbase+ , _timestamp = o.vmoptTimestamp+ , _number = o.vmoptNumber+ , _prevRandao = o.vmoptPrevRandao+ , _maxCodeSize = o.vmoptMaxCodeSize+ , _gaslimit = o.vmoptBlockGaslimit+ , _baseFee = o.vmoptBaseFee+ , _schedule = o.vmoptSchedule } , _state = FrameState { _pc = 0 , _stack = mempty , _memory = mempty , _memorySize = 0- , _code = view contractcode $ vmoptContract o- , _contract = vmoptAddress o- , _codeContract = vmoptAddress o- , _calldata = fst $ vmoptCalldata o- , _callvalue = vmoptValue o- , _caller = vmoptCaller o- , _gas = vmoptGas o+ , _code = o.vmoptContract._contractcode+ , _contract = o.vmoptAddress+ , _codeContract = o.vmoptAddress+ , _calldata = fst o.vmoptCalldata+ , _callvalue = o.vmoptValue+ , _caller = o.vmoptCaller+ , _gas = o.vmoptGas , _returndata = mempty , _static = False } , _env = Env { _sha3Crack = mempty- , _chainId = vmoptChainId o- , _storage = if vmoptStorageBase o == Concrete then EmptyStore else AbstractStore+ , _chainId = o.vmoptChainId+ , _storage = if o.vmoptStorageBase == Concrete then EmptyStore else AbstractStore , _origStorage = mempty , _contracts = Map.fromList- [(vmoptAddress o, vmoptContract o)]+ [(o.vmoptAddress, o.vmoptContract )] --, _keccakUsed = mempty --, _storageModel = vmoptStorageModel o } , _cache = Cache mempty mempty mempty , _burned = 0- , _constraints = snd $ vmoptCalldata o+ , _constraints = snd o.vmoptCalldata , _keccakEqs = mempty , _iterations = mempty- , _allowFFI = vmoptAllowFFI o+ , _allowFFI = o.vmoptAllowFFI+ , _overrideCaller = Nothing } -- | Initialize empty contract with given code@@ -565,30 +564,25 @@ vm <- get let- -- Convenience function to access parts of the current VM state.- -- Arcane type signature needed to avoid monomorphism restriction.- the :: (b -> VM -> Const a VM) -> ((a -> Const a a) -> b) -> a- the f g = view (f . g) vm- -- Convenient aliases- mem = the state memory- stk = the state stack- self = the state contract- this = fromMaybe (error "internal error: state contract") (preview (ix self) (the env contracts))+ mem = vm._state._memory+ stk = vm._state._stack+ self = vm._state._contract+ this = fromMaybe (error "internal error: state contract") (Map.lookup self vm._env._contracts) - fees@FeeSchedule {..} = the block schedule+ 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 (the state calldata) of+ case bufLength vm._state._calldata of (Lit calldatasize) -> do- copyBytesToMemory (the state calldata) (Lit calldatasize) (Lit 0) (Lit 0)- executePrecompile self (the state gas) 0 calldatasize 0 0 []+ copyBytesToMemory vm._state._calldata (Lit calldatasize) (Lit 0) (Lit 0)+ executePrecompile self vm._state._gas 0 calldatasize 0 0 [] vmx <- get- case view (state.stack) vmx of+ case vmx._state._stack of (x:_) -> case x of Lit (num -> x' :: Integer) -> case x' of 0 -> do@@ -600,32 +594,32 @@ out <- use (state . returndata) finishFrame (FrameReturned out) e -> vmError $- UnexpectedSymbolicArg (view (state . pc) vmx) "precompile returned a symbolic value" [e]+ UnexpectedSymbolicArg vmx._state._pc "precompile returned a symbolic value" [e] _ -> underrun- e -> vmError $ UnexpectedSymbolicArg (the state pc) "cannot call precompiles with symbolic data" [e]+ e -> vmError $ UnexpectedSymbolicArg vm._state._pc "cannot call precompiles with symbolic data" [e] - else if the state pc >= opslen (the state code)+ else if vm._state._pc >= opslen vm._state._code then doStop else do- let ?op = case (the state code) of- InitCode conc _ -> BS.index conc (the state pc)- RuntimeCode (ConcreteRuntimeCode bs) -> BS.index bs (the state pc)+ 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 (error "could not analyze symbolic code") $- unlitByte $ ops V.! the state pc+ unlitByte $ ops V.! vm._state._pc case ?op of -- op: PUSH x | x >= 0x60 && x <= 0x7f -> do let !n = num x - 0x60 + 1- !xs = case the state code of- InitCode conc _ -> Lit $ word $ padRight n $ BS.take n (BS.drop (1 + the state pc) conc)- RuntimeCode (ConcreteRuntimeCode bs) -> Lit $ word $ BS.take n $ BS.drop (1 + the state pc) bs+ !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 + the state pc) 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@@ -667,7 +661,7 @@ forceConcrete2 (xOffset', xSize') "LOG" $ \(xOffset, xSize) -> do let (topics, xs') = splitAt n xs bytes = readMemory xOffset' xSize' vm- logs' = (LogEntry (litAddr self) bytes topics) : (view logs vm)+ logs' = (LogEntry (litAddr self) bytes topics) : vm._logs burn (g_log + g_logdata * (num xSize) + num n * g_logtopic) $ accessMemoryRange fees xOffset xSize $ do traceTopLog logs'@@ -770,33 +764,33 @@ fetchAccount (num x) $ \c -> do next assign (state . stack) xs- push (num $ view balance c)+ push (num c._balance) [] -> underrun -- op: ORIGIN 0x32 -> limitStack 1 . burn g_base $- next >> push (num (the tx origin))+ next >> push (num vm._tx._origin) -- op: CALLER 0x33 -> limitStack 1 . burn g_base $- next >> pushSym (the state caller)+ next >> pushSym vm._state._caller -- op: CALLVALUE 0x34 -> limitStack 1 . burn g_base $- next >> pushSym (the state callvalue)+ next >> pushSym vm._state._callvalue -- op: CALLDATALOAD 0x35 -> stackOp1 (const g_verylow) $- \ind -> Expr.readWord ind (the state calldata)+ \ind -> Expr.readWord ind vm._state._calldata -- op: CALLDATASIZE 0x36 -> limitStack 1 . burn g_base $- next >> pushSym (bufLength (the state calldata))+ next >> pushSym (bufLength vm._state._calldata) -- op: CALLDATACOPY 0x37 ->@@ -808,13 +802,13 @@ accessMemoryRange fees xTo xSize $ do next assign (state . stack) xs- copyBytesToMemory (the state calldata) xSize' xFrom xTo'+ copyBytesToMemory vm._state._calldata xSize' xFrom xTo' _ -> underrun -- op: CODESIZE 0x38 -> limitStack 1 . burn g_base $- next >> pushSym (codelen (the state code))+ next >> pushSym (codelen vm._state._code) -- op: CODECOPY 0x39 ->@@ -830,14 +824,14 @@ accessMemoryRange fees memOffset n $ do next assign (state . stack) xs- copyBytesToMemory (toBuf $ the state code) n' codeOffset memOffset'+ copyBytesToMemory (toBuf vm._state._code) n' codeOffset memOffset' else vmError IllegalOverflow _ -> underrun -- op: GASPRICE 0x3a -> limitStack 1 . burn g_base $- next >> push (the tx gasprice)+ next >> push vm._tx._gasprice -- op: EXTCODESIZE 0x3b ->@@ -857,6 +851,7 @@ _ -> do assign (state . stack) xs pushSym (CodeSize x')+ next [] -> underrun @@ -883,7 +878,7 @@ -- op: RETURNDATASIZE 0x3d -> limitStack 1 . burn g_base $- next >> pushSym (bufLength (the state returndata))+ next >> pushSym (bufLength vm._state._returndata) -- op: RETURNDATACOPY 0x3e ->@@ -896,13 +891,13 @@ assign (state . stack) xs let jump True = vmError EVM.InvalidMemoryAccess- jump False = copyBytesToMemory (the state returndata) xSize' xFrom xTo'+ jump False = copyBytesToMemory vm._state._returndata xSize' xFrom xTo' - case (xFrom, bufLength (the state returndata)) of+ case (xFrom, bufLength vm._state._returndata) of (Lit f, Lit l) -> jump $ l < f + xSize || f + xSize < f _ -> do- let oob = Expr.lt (bufLength $ the state returndata) (Expr.add xFrom xSize')+ 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@@ -927,7 +922,7 @@ -- We adopt the fake block hash scheme of the VMTests, -- so that blockhash(i) is the hash of i as decimal ASCII. stackOp1 (const g_blockhash) $ \case- (Lit i) -> if i + 256 < the block number || i >= the block number+ (Lit i) -> if i + 256 < vm._block._number || i >= vm._block._number then Lit 0 else (num i :: Integer) & show & Char8.pack & keccak' & Lit i -> BlockHash i@@ -935,42 +930,42 @@ -- op: COINBASE 0x41 -> limitStack 1 . burn g_base $- next >> push (num (the block coinbase))+ next >> push (num vm._block._coinbase) -- op: TIMESTAMP 0x42 -> limitStack 1 . burn g_base $- next >> pushSym (the block timestamp)+ next >> pushSym vm._block._timestamp -- op: NUMBER 0x43 -> limitStack 1 . burn g_base $- next >> push (the block number)+ next >> push vm._block._number -- op: PREVRANDAO 0x44 -> do limitStack 1 . burn g_base $- next >> push (the block prevRandao)+ next >> push vm._block._prevRandao -- op: GASLIMIT 0x45 -> limitStack 1 . burn g_base $- next >> push (num $ the block gaslimit)+ next >> push (num vm._block._gaslimit) -- op: CHAINID 0x46 -> limitStack 1 . burn g_base $- next >> push (the env chainId)+ next >> push vm._env._chainId -- op: SELFBALANCE 0x47 -> limitStack 1 . burn g_low $- next >> push (view balance this)+ next >> push this._balance -- op: BASEFEE 0x48 -> limitStack 1 . burn g_base $- next >> push (the block baseFee)+ next >> push vm._block._baseFee -- op: POP 0x50 ->@@ -1034,7 +1029,7 @@ if num availableGas <= g_callstipend then finishFrame (FrameErrored (OutOfGas availableGas (num g_callstipend))) else do- let original = case readStorage (litAddr self) x (ConcreteStore $ the env origStorage) of+ let original = case readStorage (litAddr self) x (ConcreteStore vm._env._origStorage) of Just (Lit v) -> v _ -> 0 let storage_cost = case (maybeLitWord current, maybeLitWord new) of@@ -1107,17 +1102,17 @@ -- op: PC 0x58 -> limitStack 1 . burn g_base $- next >> push (num (the state pc))+ next >> push (num vm._state._pc) -- op: MSIZE 0x59 -> limitStack 1 . burn g_base $- next >> push (num (the state memorySize))+ next >> push (num vm._state._memorySize) -- op: GAS 0x5a -> limitStack 1 . burn g_base $- next >> push (num (the state gas - g_base))+ next >> push (num (vm._state._gas - g_base)) -- op: JUMPDEST 0x5b -> burn g_jumpdest next@@ -1149,7 +1144,7 @@ accessMemoryRange fees xOffset xSize $ do availableGas <- use (state . gas) let- newAddr = createAddress self (view nonce this)+ newAddr = createAddress self this._nonce (cost, gas') = costOfCreate fees availableGas 0 _ <- accessAccountForGas newAddr burn (cost - gas') $ do@@ -1177,8 +1172,9 @@ delegateCall this (num xGas) xTo xTo xValue xInOffset xInSize xOutOffset xOutSize xs $ \callee -> do zoom state $ do assign callvalue (Lit xValue)- assign caller (litAddr self)+ assign caller $ fromMaybe (litAddr self) (vm ^. overrideCaller) assign contract callee+ assign overrideCaller Nothing transfer self callee xValue touchAccount self touchAccount callee@@ -1201,7 +1197,8 @@ delegateCall this (num xGas) xTo (litAddr self) xValue xInOffset xInSize xOutOffset xOutSize xs $ \_ -> do zoom state $ do assign callvalue (Lit xValue)- assign caller (litAddr self)+ assign caller $ fromMaybe (litAddr self) (vm ^. overrideCaller)+ assign overrideCaller Nothing touchAccount self _ -> underrun@@ -1215,10 +1212,10 @@ output = readMemory xOffset' xSize' vm codesize = fromMaybe (error "RETURN: cannot return dynamically sized abstract data") . unlit . bufLength $ output- maxsize = the block maxCodeSize- creation = case view frames vm of- [] -> the tx isCreate- frame:_ -> case view frameContext frame of+ maxsize = vm._block._maxCodeSize+ creation = case vm._frames of+ [] -> vm._tx._isCreate+ frame:_ -> case frame._frameContext of CreationContext {} -> True CallContext {} -> False if creation@@ -1292,9 +1289,10 @@ delegateCall this (num xGas) xTo xTo 0 xInOffset xInSize xOutOffset xOutSize xs $ \callee -> do zoom state $ do assign callvalue (Lit 0)- assign caller (litAddr self)+ assign caller $ fromMaybe (litAddr self) (vm ^. overrideCaller) assign contract callee assign static True+ assign overrideCaller Nothing touchAccount self touchAccount callee _ ->@@ -1308,7 +1306,7 @@ (xTo':_) -> forceConcrete xTo' "SELFDESTRUCT" $ \(num -> xTo) -> do acc <- accessAccountForGas (num xTo) let cost = if acc then 0 else g_cold_account_access- funds = view balance this+ funds = this._balance recipientExists = accountExists xTo vm c_new = if not recipientExists && funds /= 0 then g_selfdestruct_newaccount@@ -1351,20 +1349,20 @@ -> EVM () callChecks this xGas xContext xTo xValue xInOffset xInSize xOutOffset xOutSize xs continue = do vm <- get- let fees = view (block . schedule) vm+ let fees = vm._block._schedule accessMemoryRange fees xInOffset xInSize $ accessMemoryRange fees 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 > num (view balance this)+ if xValue > num this._balance then do assign (state . stack) (Lit 0 : xs) assign (state . returndata) mempty- pushTrace $ ErrorTrace $ BalanceTooLow xValue (view balance this)+ pushTrace $ ErrorTrace $ BalanceTooLow xValue this._balance next- else if length (view frames vm) >= 1024+ else if length vm._frames >= 1024 then do assign (state . stack) (Lit 0 : xs) assign (state . returndata) mempty@@ -1389,18 +1387,20 @@ self <- use (state . contract) stk <- use (state . stack) pc' <- use (state . pc)- case stk of- (x:_) -> case maybeLitWord x of- Just 0 ->- return ()- Just 1 ->- fetchAccount recipient $ \_ -> do-- transfer self recipient xValue- touchAccount self- touchAccount recipient- _ -> vmError $ UnexpectedSymbolicArg pc' "symbolic return value from precompile" [x]- _ -> underrun+ result' <- use result+ case result' of+ Nothing -> case stk of+ (x:_) -> case maybeLitWord x of+ Just 0 ->+ return ()+ Just 1 ->+ fetchAccount recipient $ \_ -> do+ transfer self recipient xValue+ touchAccount self+ touchAccount recipient+ _ -> vmError $ UnexpectedSymbolicArg pc' "unexpected return value from precompile" [x]+ _ -> underrun+ _ -> pure () executePrecompile :: (?op :: Word8)@@ -1410,7 +1410,7 @@ executePrecompile preCompileAddr gasCap inOffset inSize outOffset outSize xs = do vm <- get let input = readMemory (Lit inOffset) (Lit inSize) vm- fees = view (block . schedule) vm+ fees = vm._block._schedule cost = costOfPrecompile fees preCompileAddr input notImplemented = error $ "precompile at address " <> show preCompileAddr <> " not yet implemented" precompileFail = burn (gasCap - cost) $ do@@ -1426,32 +1426,29 @@ case preCompileAddr of -- ECRECOVER 0x1 ->- -- TODO: support symbolic variant- forceConcreteBuf input "ECRECOVER" $ \input' ->- 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+ -- 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 ->+ 0x2 -> forceConcreteBuf input "SHA2-256" $ \input' -> do let- hash = case input of- ConcreteBuf input' -> sha256Buf input'- _ -> WriteWord (Lit 0) (SHA256 input) mempty+ hash = sha256Buf input' sha256Buf x = ConcreteBuf $ BA.convert (Crypto.hash x :: Digest SHA256)- in do- assign (state . stack) (Lit 1 : xs)- assign (state . returndata) hash- copyBytesToMemory hash (Lit outSize) (Lit 0) (Lit outOffset)- next+ assign (state . stack) (Lit 1 : xs)+ assign (state . returndata) hash+ copyBytesToMemory hash (Lit outSize) (Lit 0) (Lit outOffset)+ next -- RIPEMD-160 0x3 ->@@ -1601,7 +1598,7 @@ pushToSequence f x = f %= (Seq.|> x) getCodeLocation :: VM -> CodeLocation-getCodeLocation vm = (view (state . contract) vm, view (state . pc) vm)+getCodeLocation vm = (vm._state._contract, vm._state._pc) branch :: CodeLocation -> Expr EWord -> (Bool -> EVM ()) -> EVM () branch loc cond continue = do@@ -1652,13 +1649,13 @@ Just x -> continue x Nothing ->- if view external c- then- -- check if the slot is cached- use (cache . fetchedContracts . at addr) >>= \case- Nothing -> forceConcrete slot "cannot read symbolic slots via RPC" mkQuery- Just _ -> forceConcrete slot "cannot read symbolic slots via rpc" $- \s -> maybe (mkQuery s) continue (readStorage (litAddr addr) slot store)+ if c._external then+ forceConcrete slot "cannot read symbolic slots via RPC" $ \litSlot -> do+ -- check if the slot is cached+ cachedStore <- use (cache . fetchedStorage)+ case Map.lookup (num addr) cachedStore >>= Map.lookup litSlot of+ Nothing -> mkQuery litSlot+ Just val -> continue (Lit val) else do modifying (env . storage) (writeStorage (litAddr addr) slot (Lit 0)) continue $ Lit 0@@ -1676,19 +1673,19 @@ accountExists :: Addr -> VM -> Bool accountExists addr vm =- case view (env . contracts . at addr) vm of+ case Map.lookup addr vm._env._contracts of Just c -> not (accountEmpty c) Nothing -> False -- EIP 161 accountEmpty :: Contract -> Bool accountEmpty c =- case view contractcode c of+ case c._contractcode of RuntimeCode (ConcreteRuntimeCode "") -> True RuntimeCode (SymbolicRuntimeCode b) -> null b _ -> False- && (view nonce c == 0)- && (view balance c == 0)+ && c._nonce == 0+ && c._balance == 0 -- * How to finalize a transaction finalize :: EVM ()@@ -1731,7 +1728,7 @@ txOrigin <- use (tx . origin) sumRefunds <- (sum . (snd <$>)) <$> (use (tx . substate . refunds)) miner <- use (block . coinbase)- blockReward <- num . r_block <$> (use (block . schedule))+ blockReward <- num . (.r_block) <$> (use (block . schedule)) gasPrice <- use (tx . gasprice) priorityFee <- use (tx . txPriorityFee) gasLimit <- use (tx . txgaslimit)@@ -1813,16 +1810,11 @@ else vmError (OutOfGas available n) ---forceConcreteAddr :: SAddr -> (Addr -> EVM ()) -> EVM ()---forceConcreteAddr n continue = case maybeLitAddr n of- --Nothing -> vmError UnexpectedSymbolicArg- --Just c -> continue c- forceConcrete :: Expr EWord -> String -> (W256 -> EVM ()) -> EVM () forceConcrete n msg continue = case maybeLitWord n of Nothing -> do vm <- get- vmError $ UnexpectedSymbolicArg (view (state . pc) vm) msg [n]+ vmError $ UnexpectedSymbolicArg vm._state._pc msg [n] Just c -> continue c forceConcrete2 :: (Expr EWord, Expr EWord) -> String -> ((W256, W256) -> EVM ()) -> EVM ()@@ -1830,41 +1822,41 @@ (Just c, Just d) -> continue (c, d) _ -> do vm <- get- vmError $ UnexpectedSymbolicArg (view (state . pc) vm) msg [n, m]+ vmError $ UnexpectedSymbolicArg vm._state._pc msg [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- vmError $ UnexpectedSymbolicArg (view (state . pc) vm) msg [k, n, m]+ vmError $ UnexpectedSymbolicArg vm._state._pc msg [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- vmError $ UnexpectedSymbolicArg (view (state . pc) vm) msg [k, l, n, m]+ vmError $ UnexpectedSymbolicArg vm._state._pc msg [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- vmError $ UnexpectedSymbolicArg (view (state . pc) vm) msg [k, l, m, n, o]+ vmError $ UnexpectedSymbolicArg vm._state._pc msg [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- vmError $ UnexpectedSymbolicArg (view (state . pc) vm) msg [k, l, m, n, o, p]+ vmError $ UnexpectedSymbolicArg vm._state._pc msg [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- vmError $ UnexpectedSymbolicArg (view (state . pc) vm) msg [b]+ vmError $ UnexpectedSymbolicArg vm._state._pc msg [b] -- * Substate manipulation refund :: Word64 -> EVM ()@@ -1933,7 +1925,7 @@ abi = readBytes 4 (Lit inOffset) mem input = readMemory (Lit $ inOffset + 4) (Lit $ inSize - 4) vm case maybeLitWord abi of- Nothing -> vmError $ UnexpectedSymbolicArg (view (state . pc) vm) "symbolic cheatcode selector" [abi]+ Nothing -> vmError $ UnexpectedSymbolicArg vm._state._pc "symbolic cheatcode selector" [abi] Just (fromIntegral -> abi') -> case Map.lookup abi' cheatActions of Nothing ->@@ -1951,7 +1943,7 @@ [ action "ffi(string[])" $ \sig outOffset outSize input -> do vm <- get- if view EVM.allowFFI vm then+ if vm._allowFFI then case decodeBuf [AbiArrayDynamicType AbiStringType] input of CAbi valsArr -> case valsArr of [AbiArrayDynamic AbiStringType strsV] ->@@ -2049,6 +2041,11 @@ addr = Lit . W256 . word256 . BS.drop 12 . BS.take 32 . keccakBytes $ pub assign (state . returndata . word256At (Lit 0)) addr assign (state . memory . word256At outOffset) addr+ _ -> vmError (BadCheatCode sig),++ action "prank(address)" $+ \sig _ _ input -> case decodeStaticArgs 0 1 input of+ [addr] -> assign overrideCaller (Just addr) _ -> vmError (BadCheatCode sig) ]@@ -2093,12 +2090,12 @@ , callContextContext = xContext' , callContextOffset = xOutOffset , callContextSize = xOutSize- , callContextCodehash = view codehash target- , callContextReversion = (view (env . contracts) vm0, view (env . storage) vm0)- , callContextSubState = view (tx . substate) vm0+ , callContextCodehash = target._codehash+ , callContextReversion = (vm0._env._contracts, vm0._env._storage)+ , callContextSubState = vm0._tx._substate , callContextAbi = if xInSize >= 4- then case unlit $ readBytes 4 (Lit xInOffset) (view (state . memory) vm0)+ then case unlit $ readBytes 4 (Lit xInOffset) vm0._state._memory of Nothing -> Nothing Just abi -> Just $ num abi else Nothing@@ -2110,7 +2107,7 @@ vm1 <- get pushTo frames $ Frame- { _frameState = (set stack xs) (view state vm1)+ { _frameState = vm1._state { _stack = xs } , _frameContext = newContext } @@ -2121,13 +2118,13 @@ zoom state $ do assign gas (num xGas) assign pc 0- assign code (clearInitCode (view contractcode target))+ 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) (view (state . memory) vm0) mempty)+ assign calldata (copySlice (Lit xInOffset) (Lit 0) (Lit xInSize) vm0._state._memory mempty) continue xTo' @@ -2136,7 +2133,7 @@ -- EIP 684 collision :: Maybe Contract -> Bool collision c' = case c' of- Just c -> (view nonce c /= 0) || case view contractcode c of+ Just c -> c._nonce /= 0 || case c._contractcode of RuntimeCode (ConcreteRuntimeCode "") -> False RuntimeCode (SymbolicRuntimeCode b) -> not $ null b _ -> True@@ -2148,25 +2145,25 @@ create self this xGas' xValue xs newAddr initCode = do vm0 <- get let xGas = num xGas'- if view nonce this == num (maxBound :: Word64)+ if this._nonce == num (maxBound :: Word64) then do assign (state . stack) (Lit 0 : xs) assign (state . returndata) mempty pushTrace $ ErrorTrace NonceOverflow next- else if xValue > view balance this+ else if xValue > this._balance then do assign (state . stack) (Lit 0 : xs) assign (state . returndata) mempty- pushTrace $ ErrorTrace $ BalanceTooLow xValue (view balance this)+ pushTrace $ ErrorTrace $ BalanceTooLow xValue this._balance next- else if length (view frames vm0) >= 1024+ 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 $ view (env . contracts . at newAddr) vm0+ else if collision $ Map.lookup newAddr vm0._env._contracts then burn xGas $ do assign (state . stack) (Lit 0 : xs) assign (state . returndata) mempty@@ -2188,20 +2185,20 @@ pure $ InitCode (BS.pack $ V.toList conc) sym case contract' of Nothing ->- vmError $ UnexpectedSymbolicArg (view (state . pc) vm0) "initcode must have a concrete prefix" []+ vmError $ UnexpectedSymbolicArg vm0._state._pc "initcode must have a concrete prefix" [] Just c -> do let newContract = initialContract c newContext = CreationContext { creationContextAddress = newAddr- , creationContextCodehash = view codehash newContract- , creationContextReversion = view (env . contracts) vm0- , creationContextSubstate = view (tx . substate) vm0+ , creationContextCodehash = newContract._codehash+ , creationContextReversion = vm0._env._contracts+ , creationContextSubstate = vm0._tx._substate } zoom (env . contracts) $ do oldAcc <- use (at newAddr)- let oldBal = maybe 0 (view balance) oldAcc+ let oldBal = maybe 0 (._balance) oldAcc assign (at newAddr) (Just (newContract & balance .~ oldBal)) modifying (ix self . nonce) succ@@ -2223,7 +2220,7 @@ vm1 <- get pushTo frames $ Frame { _frameContext = newContext- , _frameState = (set stack xs) (view state vm1)+ , _frameState = vm1._state { _stack = xs } } assign state $@@ -2241,12 +2238,13 @@ replaceCode target newCode = zoom (env . contracts . at target) $ get >>= \case- Just now -> case (view contractcode now) of+ Just now -> case now._contractcode of InitCode _ _ -> put . Just $- initialContract newCode- & set balance (view balance now)- & set nonce (view nonce now)+ (initialContract newCode)+ { _balance = now._balance+ , _nonce = now._nonce+ } RuntimeCode _ -> error ("internal error: can't replace code of deployed contract " <> show target) Nothing ->@@ -2255,7 +2253,7 @@ replaceCodeOfSelf :: ContractCode -> EVM () replaceCodeOfSelf newCode = do vm <- get- replaceCode (view (state . contract) vm) newCode+ replaceCode vm._state._contract newCode resetState :: EVM () resetState = do@@ -2270,7 +2268,7 @@ vmError e = finishFrame (FrameErrored e) underrun :: EVM ()-underrun = vmError StackUnderrun+underrun = vmError EVM.StackUnderrun -- | A stack frame can be popped in three ways. data FrameResult@@ -2288,7 +2286,7 @@ finishFrame how = do oldVm <- get - case view frames oldVm of+ case oldVm._frames of -- Is the current frame the only one? [] -> do case how of@@ -2308,26 +2306,26 @@ FrameReverted e -> ErrorTrace (EVM.Revert e) FrameReturned output ->- ReturnTrace output (view frameContext nextFrame)+ ReturnTrace output nextFrame._frameContext -- 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 (view frameState nextFrame)+ assign state nextFrame._frameState -- 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 = view (state . gas) oldVm+ 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 error (six cases).- case view frameContext nextFrame of+ case nextFrame._frameContext of -- Were we calling? CallContext _ _ (Lit -> outOffset) (Lit -> outSize) _ _ _ reversion substate' -> do@@ -2377,7 +2375,7 @@ CreationContext _ _ reversion substate' -> do creator <- use (state . contract) let- createe = view (state . contract) oldVm+ createe = oldVm._state._contract revertContracts = assign (env . contracts) reversion' revertSubstate = assign (tx . substate) substate' @@ -2399,7 +2397,7 @@ case Expr.toList output of Nothing -> vmError $ UnexpectedSymbolicArg- (view (state . pc) oldVm)+ oldVm._state._pc "runtime code cannot have an abstract length" [output] Just newCode -> do@@ -2476,19 +2474,18 @@ 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 (view (state . memory) vm) mempty+readMemory offset size vm = copySlice offset (Lit 0) size vm._state._memory mempty -- * Tracing -withTraceLocation- :: (MonadState VM m) => TraceData -> m Trace+withTraceLocation :: TraceData -> EVM Trace withTraceLocation x = do vm <- get let this = fromJust $ currentContract vm pure Trace { _traceData = x , _traceContract = this- , _traceOpIx = fromMaybe 0 $ (view opIxMap this) Vector.!? (view (state . pc) vm)+ , _traceOpIx = fromMaybe 0 $ this._opIxMap Vector.!? vm._state._pc } pushTrace :: TraceData -> EVM ()@@ -2517,9 +2514,9 @@ Just z' -> zipperRootForest (Zipper.nextSpace z') traceForest :: VM -> Forest Trace-traceForest = view (traces . to zipperRootForest)+traceForest vm = zipperRootForest vm._traces -traceTopLog :: (MonadState VM m) => [Expr Log] -> m ()+traceTopLog :: [Expr Log] -> EVM () traceTopLog [] = noop traceTopLog ((LogEntry addr bytes topics) : _) = do trace <- withTraceLocation (EventTrace addr bytes topics)@@ -2666,7 +2663,7 @@ vmOpIx :: VM -> Maybe Int vmOpIx vm = do self <- currentContract vm- (view opIxMap self) Vector.!? (view (state . pc) vm)+ self._opIxMap Vector.!? vm._state._pc opParams :: VM -> Map String (Expr EWord) opParams vm =@@ -2939,8 +2936,8 @@ codeloc :: EVM CodeLocation codeloc = do vm <- get- let self = view (state . contract) vm- loc = view (state . pc) vm+ let self = vm._state._contract+ loc = vm._state._pc pure (self, loc) -- * Emacs setup
src/EVM/CSE.hs view
@@ -40,43 +40,43 @@ -- buffers e@(WriteWord {}) -> do s <- get- case Map.lookup e (bufs s) of+ case Map.lookup e s.bufs of Just v -> pure $ GVar (BufVar v) Nothing -> do let- next = count s- bs' = Map.insert e next (bufs s)+ next = s.count+ bs' = Map.insert e next s.bufs put $ s{bufs=bs', count=next+1} pure $ GVar (BufVar next) e@(WriteByte {}) -> do s <- get- case Map.lookup e (bufs s) of+ case Map.lookup e s.bufs of Just v -> pure $ GVar (BufVar v) Nothing -> do let- next = count s- bs' = Map.insert e next (bufs s)+ next = s.count+ bs' = Map.insert e next s.bufs put $ s{bufs=bs', count=next+1} pure $ GVar (BufVar next) e@(CopySlice {}) -> do s <- get- case Map.lookup e (bufs s) of+ case Map.lookup e s.bufs of Just v -> pure $ GVar (BufVar v) Nothing -> do let- next = count s- bs' = Map.insert e next (bufs s)+ next = s.count+ bs' = Map.insert e next s.bufs put $ s{count=next+1, bufs=bs'} pure $ GVar (BufVar next) -- storage e@(SStore {}) -> do s <- get- case Map.lookup e (stores s) of+ case Map.lookup e s.stores of Just v -> pure $ GVar (StoreVar v) Nothing -> do let- next = count s- ss' = Map.insert e next (stores s)+ next = s.count+ ss' = Map.insert e next s.stores put $ s{count=next+1, stores=ss'} pure $ GVar (StoreVar next) e -> pure e@@ -91,7 +91,7 @@ eliminateExpr :: Expr a -> (Expr a, BufEnv, StoreEnv) eliminateExpr e = let (e', st) = runState (eliminateExpr' e) initState in- (e', invertKeyVal (bufs st), invertKeyVal (stores st))+ (e', invertKeyVal st.bufs, invertKeyVal st.stores) -- | Common subexpression elimination pass for Prop eliminateProp' :: Prop -> State BuilderState Prop@@ -106,4 +106,4 @@ eliminateProps :: [Prop] -> ([Prop], BufEnv, StoreEnv) eliminateProps props = let (props', st) = runState (eliminateProps' props) initState in- (props', invertKeyVal (bufs st), invertKeyVal (stores st))+ (props', invertKeyVal st.bufs, invertKeyVal st.stores)
src/EVM/Dapp.hs view
@@ -1,64 +1,55 @@-{-# Language TemplateHaskell #-}- module EVM.Dapp where -import EVM (Trace, traceContract, traceOpIx, ContractCode(..), Contract(..), codehash, contractcode, RuntimeCode (..))+import EVM (Trace(..), ContractCode(..), Contract(..), RuntimeCode (..)) import EVM.ABI (Event, AbiType, SolError)+import EVM.Concrete import EVM.Debug (srcMapCodePos) import EVM.Solidity import EVM.Types (W256, abiKeccak, keccak', Addr, regexMatches, unlit, unlitByte) -import Data.ByteString (ByteString)-import qualified Data.ByteString as BS+import Control.Arrow ((>>>)) 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.Sequence qualified as Seq import Data.Text (Text, isPrefixOf, pack, unpack) import Data.Text.Encoding (encodeUtf8)-import Data.Map (Map, toList, elems)-import Data.List (sort)-import Data.Maybe (isJust, fromJust, mapMaybe)+import Data.Vector qualified as V import Data.Word (Word32)-import EVM.Concrete -import Control.Arrow ((>>>))-import Control.Lens--import Data.List (find)-import qualified Data.Map as Map-import qualified Data.Vector as V- data DappInfo = DappInfo- { _dappRoot :: FilePath- , _dappSolcByName :: Map Text SolcContract- , _dappSolcByHash :: Map W256 (CodeType, SolcContract)- , _dappSolcByCode :: [(Code, SolcContract)] -- for contracts with `immutable` vars.- , _dappSources :: SourceCache- , _dappUnitTests :: [(Text, [(Test, [AbiType])])]- , _dappAbiMap :: Map Word32 Method- , _dappEventMap :: Map W256 Event- , _dappErrorMap :: Map W256 SolError- , _dappAstIdMap :: Map Int Value- , _dappAstSrcMap :: SrcMap -> Maybe Value+ { root :: FilePath+ , solcByName :: Map Text SolcContract+ , solcByHash :: Map W256 (CodeType, SolcContract)+ , solcByCode :: [(Code, SolcContract)] -- for contracts with `immutable` vars.+ , sources :: SourceCache+ , unitTests :: [(Text, [(Test, [AbiType])])]+ , abiMap :: Map Word32 Method+ , eventMap :: Map W256 Event+ , errorMap :: Map W256 SolError+ , astIdMap :: Map Int Value+ , astSrcMap :: SrcMap -> Maybe Value } -- | bytecode modulo immutables, to identify contracts-data Code =- Code {- raw :: ByteString,- immutableLocations :: [Reference]+data Code = Code+ { raw :: ByteString+ , immutableLocations :: [Reference] } deriving Show data DappContext = DappContext- { _contextInfo :: DappInfo- , _contextEnv :: Map Addr Contract+ { info :: DappInfo+ , env :: Map Addr Contract } data Test = ConcreteTest Text | SymbolicTest Text | InvariantTest Text -makeLenses ''DappInfo-makeLenses ''DappContext- instance Show Test where show t = unpack $ extractSig t @@ -67,31 +58,31 @@ dappInfo root solcByName sources = let solcs = Map.elems solcByName- astIds = astIdMap $ snd <$> toList (view sourceAsts sources)- immutables = filter ((/=) mempty . (view immutableReferences)) solcs+ astIds = astIdMap $ snd <$> Map.toList sources.asts+ immutables = filter ((/=) mempty . (.immutableReferences)) solcs in DappInfo- { _dappRoot = root- , _dappUnitTests = findAllUnitTests solcs- , _dappSources = sources- , _dappSolcByName = solcByName- , _dappSolcByHash =+ { root = root+ , unitTests = findAllUnitTests solcs+ , sources = sources+ , solcByName = solcByName+ , solcByHash = let- f g k = Map.fromList [(view g x, (k, x)) | x <- solcs]+ f g k = Map.fromList [(g x, (k, x)) | x <- solcs] in mappend- (f runtimeCodehash Runtime)- (f creationCodehash Creation)+ (f (.runtimeCodehash) Runtime)+ (f (.creationCodehash) Creation) -- contracts with immutable locations can't be id by hash- , _dappSolcByCode =- [(Code (_runtimeCode x) (concat $ elems $ _immutableReferences x), x) | x <- immutables]+ , solcByCode =+ [(Code x.runtimeCode (concat $ Map.elems x.immutableReferences), x) | x <- immutables] -- Sum up the ABI maps from all the contracts.- , _dappAbiMap = mconcat (map (view abiMap) solcs)- , _dappEventMap = mconcat (map (view eventMap) solcs)- , _dappErrorMap = mconcat (map (view errorMap) solcs)+ , abiMap = mconcat (map (.abiMap) solcs)+ , eventMap = mconcat (map (.eventMap) solcs)+ , errorMap = mconcat (map (.errorMap) solcs) - , _dappAstIdMap = astIds- , _dappAstSrcMap = astSrcMap astIds+ , astIdMap = astIds+ , astSrcMap = astSrcMap astIds } emptyDapp :: DappInfo@@ -123,24 +114,24 @@ findUnitTests :: Text -> ([SolcContract] -> [(Text, [(Test, [AbiType])])]) findUnitTests match = concatMap $ \c ->- case preview (abiMap . ix unitTestMarkerAbi) c of+ case Map.lookup unitTestMarkerAbi c.abiMap of Nothing -> [] Just _ -> let testNames = unitTestMethodsFiltered (regexMatches match) c- in [(view contractName c, testNames) | not (BS.null (view runtimeCode c)) && not (null testNames)]+ in [(c.contractName, testNames) | not (BS.null c.runtimeCode) && not (null testNames)] unitTestMethodsFiltered :: (Text -> Bool) -> (SolcContract -> [(Test, [AbiType])]) unitTestMethodsFiltered matcher c = let- testName method = (view contractName c) <> "." <> (extractSig (fst method))+ testName method = c.contractName <> "." <> (extractSig (fst method)) in filter (matcher . testName) (unitTestMethods c) unitTestMethods :: SolcContract -> [(Test, [AbiType])] unitTestMethods =- view abiMap+ (.abiMap) >>> Map.elems- >>> map (\f -> (mkTest $ view methodSignature f, snd <$> view methodInputs f))+ >>> map (\f -> (mkTest f.methodSignature, snd <$> f.inputs)) >>> filter (isJust . fst) >>> fmap (first fromJust) @@ -150,41 +141,37 @@ extractSig (InvariantTest sig) = sig traceSrcMap :: DappInfo -> Trace -> Maybe SrcMap-traceSrcMap dapp trace =- let- h = view traceContract trace- i = view traceOpIx trace- in srcMap dapp h i+traceSrcMap dapp trace = srcMap dapp trace._traceContract trace._traceOpIx srcMap :: DappInfo -> Contract -> Int -> Maybe SrcMap srcMap dapp contr opIndex = do sol <- findSrc contr dapp- case view contractcode contr of+ case contr._contractcode of (InitCode _ _) ->- preview (creationSrcmap . ix opIndex) sol+ Seq.lookup opIndex sol.creationSrcmap (RuntimeCode _) ->- preview (runtimeSrcmap . ix opIndex) sol+ Seq.lookup opIndex sol.runtimeSrcmap findSrc :: Contract -> DappInfo -> Maybe SolcContract findSrc c dapp = do- hash <- unlit (view codehash c)- case preview (dappSolcByHash . ix hash) dapp of+ hash <- unlit c._codehash+ case Map.lookup hash dapp.solcByHash of Just (_, v) -> Just v- Nothing -> lookupCode (view contractcode c) dapp+ Nothing -> lookupCode c._contractcode dapp lookupCode :: ContractCode -> DappInfo -> Maybe SolcContract lookupCode (InitCode c _) a =- snd <$> preview (dappSolcByHash . ix (keccak' (stripBytecodeMetadata c))) a+ snd <$> Map.lookup (keccak' (stripBytecodeMetadata c)) a.solcByHash lookupCode (RuntimeCode (ConcreteRuntimeCode c)) a =- case snd <$> preview (dappSolcByHash . ix (keccak' (stripBytecodeMetadata c))) a of+ case snd <$> Map.lookup (keccak' (stripBytecodeMetadata c)) a.solcByHash of Just x -> return x- Nothing -> snd <$> find (compareCode c . fst) (view dappSolcByCode a)+ Nothing -> snd <$> find (compareCode c . fst) a.solcByCode lookupCode (RuntimeCode (SymbolicRuntimeCode c)) a = let code = BS.pack $ mapMaybe unlitByte $ V.toList c- in case snd <$> preview (dappSolcByHash . ix (keccak' (stripBytecodeMetadata code))) a of+ in case snd <$> Map.lookup (keccak' (stripBytecodeMetadata code)) a.solcByHash of Just x -> return x- Nothing -> snd <$> find (compareCode code . fst) (view dappSolcByCode a)+ Nothing -> snd <$> find (compareCode code . fst) a.solcByCode compareCode :: ByteString -> Code -> Bool compareCode raw (Code template locs) =@@ -198,7 +185,7 @@ case traceSrcMap dapp trace of Nothing -> Left "<no source map>" Just sm ->- case srcMapCodePos (view dappSources dapp) sm of+ case srcMapCodePos dapp.sources sm of Nothing -> Left "<source not found>" Just (fileName, lineIx) -> Right (fileName <> ":" <> pack (show lineIx))
src/EVM/Debug.hs view
@@ -1,7 +1,7 @@ module EVM.Debug where import EVM (Contract, nonce, balance, bytecode, codehash)-import EVM.Solidity (SrcMap, srcMapFile, srcMapOffset, srcMapLength, SourceCache, sourceFiles)+import EVM.Solidity (SrcMap, srcMapFile, srcMapOffset, srcMapLength, SourceCache(..)) import EVM.Types (Addr) import EVM.Expr (bufLength) @@ -43,12 +43,12 @@ srcMapCodePos :: SourceCache -> SrcMap -> Maybe (Text, Int) srcMapCodePos cache sm =- fmap (second f) $ cache ^? sourceFiles . ix (srcMapFile sm)+ fmap (second f) $ cache.files ^? ix sm.srcMapFile where- f v = ByteString.count 0xa (ByteString.take (srcMapOffset sm - 1) v) + 1+ f v = ByteString.count 0xa (ByteString.take (sm.srcMapOffset - 1) v) + 1 srcMapCode :: SourceCache -> SrcMap -> Maybe ByteString srcMapCode cache sm =- fmap f $ cache ^? sourceFiles . ix (srcMapFile sm)+ fmap f $ cache.files ^? ix sm.srcMapFile where- f (_, v) = ByteString.take (min 80 (srcMapLength sm)) (ByteString.drop (srcMapOffset sm) v)+ f (_, v) = ByteString.take (min 80 sm.srcMapLength) (ByteString.drop sm.srcMapOffset v)
src/EVM/Dev.hs view
@@ -19,6 +19,7 @@ import EVM import EVM.SMT+import EVM.Solvers import EVM.Types import EVM.Expr (numBranches, simplify) import EVM.SymExec
src/EVM/Exec.hs view
@@ -8,12 +8,10 @@ import qualified EVM.FeeSchedule as FeeSchedule import Control.Lens-import Control.Monad.State.Class (MonadState)-import Control.Monad.State.Strict (runState)+import Control.Monad.Trans.State.Strict (get, State) import Data.ByteString (ByteString) import Data.Maybe (isNothing) -import qualified Control.Monad.State.Class as State ethrunAddress :: Addr ethrunAddress = Addr 0x00a329c0648769a73afac7f9381e08fb43dbea72@@ -47,26 +45,27 @@ }) & set (env . contracts . at ethrunAddress) (Just (initialContract (RuntimeCode (ConcreteRuntimeCode "")))) -exec :: MonadState VM m => m VMResult-exec =- use EVM.result >>= \case- Nothing -> State.state (runState exec1) >> exec- Just x -> return x+exec :: State VM VMResult+exec = do+ vm <- get+ case vm._result of+ Nothing -> exec1 >> exec+ Just r -> pure r -run :: MonadState VM m => m VM-run =- use EVM.result >>= \case- Nothing -> State.state (runState exec1) >> run- Just _ -> State.get+run :: State VM VM+run = do+ vm <- get+ case vm._result of+ Nothing -> exec1 >> run+ Just _ -> pure vm -execWhile :: MonadState VM m => (VM -> Bool) -> m Int+execWhile :: (VM -> Bool) -> State VM Int execWhile p = go 0 where go i = do- x <- State.get- if p x && isNothing (view result x)+ vm <- get+ if p vm && isNothing vm._result then do- State.state (runState exec1) go $! (i + 1) else- return i+ pure i
src/EVM/Expr.hs view
@@ -678,6 +678,28 @@ | x == 0 = b | otherwise = 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+ go (Mod o1@(Lit _) o2@(Lit _)) = EVM.Expr.mod o1 o2+ go (SMod o1@(Lit _) o2@(Lit _)) = EVM.Expr.smod o1 o2+ go (Add o1@(Lit _) o2@(Lit _)) = EVM.Expr.add o1 o2+ go (Sub o1@(Lit _) o2@(Lit _)) = EVM.Expr.sub o1 o2++ -- double add/sub.+ -- Notice that everything is done mod 2**256. So for example:+ -- (a-b)+c observes the same arithmetic equalities as we are used to+ -- in infinite integers. In fact, it can be re-written as:+ -- (a+(W256Max-b)+c), which is the same as:+ -- (a+c+(W256Max-b)), which is the same as:+ -- (a+(c-b))+ -- In other words, subtraction is just adding a much larger number.+ -- So 3-1 mod 6 = 3+(6-1) mod 6 = 3+5 mod 6 = 5+3 mod 6 = 2+ go (Sub (Sub orig (Lit x)) (Lit y)) = Sub orig (Lit (y+x))+ go (Sub (Add orig (Lit x)) (Lit y)) = Sub orig (Lit (y-x))+ go (Add (Add orig (Lit x)) (Lit y)) = Add orig (Lit (y+x))+ go (Add (Sub orig (Lit x)) (Lit y)) = Add orig (Lit (y-x))+ -- redundant add / sub go o@(Sub (Add a b) c) | a == c = b
src/EVM/Facts.hs view
@@ -33,7 +33,7 @@ ) where import EVM (VM, Contract, Cache)-import EVM (balance, nonce, storage, bytecode, env, contracts, contract, state, cache, fetchedStorage, fetchedContracts)+import EVM (balance, nonce, storage, bytecode, env, contracts, cache, fetchedStorage, fetchedContracts) import EVM.Types (Addr, W256, Expr(..), num) import EVM.Expr (writeStorage, litAddr) @@ -112,15 +112,15 @@ contractFacts a x store = case view bytecode x of ConcreteBuf b -> storageFacts a store ++- [ BalanceFact a (view balance x)- , NonceFact a (view nonce x)+ [ BalanceFact a x._balance+ , NonceFact a x._nonce , CodeFact a b ] _ -> -- here simply ignore storing the bytecode storageFacts a store ++- [ BalanceFact a (view balance x)- , NonceFact a (view nonce x)+ [ BalanceFact a x._balance+ , NonceFact a x._nonce ] @@ -136,13 +136,13 @@ cacheFacts :: Cache -> Set Fact cacheFacts c = Set.fromList $ do- (k, v) <- Map.toList (view EVM.fetchedContracts c)- contractFacts k v (view EVM.fetchedStorage c)+ (k, v) <- Map.toList c._fetchedContracts+ contractFacts k v c._fetchedStorage vmFacts :: VM -> Set Fact vmFacts vm = Set.fromList $ do- (k, v) <- Map.toList (view (env . contracts) vm)- case view (env . storage) vm of+ (k, v) <- Map.toList vm._env._contracts+ case vm._env._storage of EmptyStore -> contractFacts k v Map.empty ConcreteStore s -> contractFacts k v s _ -> error "cannot serialize an abstract store"@@ -159,7 +159,7 @@ case fact of CodeFact {..} -> flip execState vm $ do assign (env . contracts . at addr) (Just (EVM.initialContract (EVM.RuntimeCode (EVM.ConcreteRuntimeCode blob))))- when (view (state . contract) vm == addr) $ EVM.loadContract addr+ when (vm._state._contract == addr) $ EVM.loadContract addr StorageFact {..} -> vm & over (env . storage) (writeStorage (litAddr addr) (Lit which) (Lit what)) BalanceFact {..} ->@@ -172,9 +172,9 @@ case fact of CodeFact {..} -> flip execState vm $ do assign (cache . fetchedContracts . at addr) (Just (EVM.initialContract (EVM.RuntimeCode (EVM.ConcreteRuntimeCode blob))))- when (view (state . contract) vm == addr) $ EVM.loadContract addr+ when (vm._state._contract == addr) $ EVM.loadContract addr StorageFact {..} -> let- store = view (cache . fetchedStorage) vm+ store = vm._cache._fetchedStorage ctrct = Map.findWithDefault Map.empty (num addr) store in vm & set (cache . fetchedStorage) (Map.insert (num addr) (Map.insert which what ctrct) store)@@ -214,7 +214,7 @@ where mk :: AsASCII a => [ASCII] -> ASCII -> a -> File mk prefix base a =- File (Path (dump (addr fact) : prefix) base)+ File (Path (dump fact.addr : prefix) base) (Data $ dump a) -- This lets us easier pattern match on serialized things.
src/EVM/Fetch.hs view
@@ -8,6 +8,7 @@ import EVM.ABI import EVM.Types (Addr, W256, hexText, Expr(Lit), Expr(..), Prop(..), (.&&), (./=)) import EVM.SMT+import EVM.Solvers import EVM (EVM, Contract, Block, initialContract, nonce, balance, external) import qualified EVM.FeeSchedule as FeeSchedule
src/EVM/Format.hs view
@@ -1,7 +1,6 @@ {-# Language DataKinds #-} {-# Language ImplicitParams #-} - module EVM.Format ( formatExpr , contractNamePart@@ -27,45 +26,39 @@ import Prelude hiding (Word) -import qualified EVM-import EVM.Dapp (DappInfo (..), dappSolcByHash, dappAbiMap, showTraceLocation, dappEventMap, dappErrorMap)-import EVM.Dapp (DappContext (..), contextInfo, contextEnv)-import EVM (VM, cheatCode, traceForest, traceData, Error (..))-import EVM (Trace, TraceData (..), Query (..), FrameContext (..))-import EVM.Types (maybeLitWord, W256 (..), num, word, Expr(..), EType(..))-import EVM.Types (Addr, ByteStringS(..), Error(..))-import EVM.ABI (AbiValue (..), Event (..), AbiType (..), SolError (..))-import EVM.ABI (Indexed (NotIndexed), getAbiSeq)-import EVM.ABI (parseTypeName, formatString)-import EVM.Solidity (SolcContract(..), contractName, abiMap)-import EVM.Solidity (methodOutput, methodSignature, methodName)-import EVM.Hexdump-+import EVM qualified+import EVM (VM, cheatCode, traceForest, Error(..), Trace,+ TraceData(..), Query(..), FrameContext(..))+import EVM.ABI (AbiValue (..), Event (..), AbiType (..), SolError(..),+ Indexed(NotIndexed), getAbiSeq, parseTypeName, formatString)+import EVM.Dapp (DappContext(..), DappInfo(..), showTraceLocation)+import EVM.Expr qualified as Expr+import EVM.Hexdump (prettyHex)+import EVM.Solidity (SolcContract(..), Method(..))+import EVM.Types (maybeLitWord, W256(..), num, word, Expr(..), EType(..), Addr,+ ByteStringS(..), Error(..)) import Control.Arrow ((>>>))-import Control.Lens (view, preview, ix, _2, to, (^?!))+import Control.Lens (preview, ix, _2) import Data.Binary.Get (runGetOrFail)-import Data.Bits (shiftR)+import Data.Bits (shiftR) import Data.ByteString (ByteString)+import Data.ByteString qualified as BS import Data.ByteString.Builder (byteStringHex, toLazyByteString) import Data.ByteString.Lazy (toStrict, fromStrict)+import Data.Char qualified as Char import Data.DoubleWord (signedWord) import Data.Foldable (toList)+import Data.Functor ((<&>))+import Data.Map qualified as Map import Data.Maybe (catMaybes, fromMaybe, fromJust)-import Data.Text (Text, pack, unpack, intercalate)-import Data.Text (dropEnd, splitOn)+import Data.Text (Text, pack, unpack, intercalate, dropEnd, splitOn)+import Data.Text qualified as T import Data.Text.Encoding (decodeUtf8, decodeUtf8') import Data.Tree.View (showTree) import Data.Vector (Vector) import Data.Word (Word32) import Numeric (showHex) -import qualified Data.ByteString as BS-import qualified Data.Char as Char-import qualified Data.Map as Map-import qualified Data.Text as Text-import qualified EVM.Expr as Expr-import qualified Data.Text as T- data Signedness = Signed | Unsigned deriving (Show) @@ -73,7 +66,7 @@ showDec signed (W256 w) | i == num cheatCode = "<hevm cheat address>" | (i :: Integer) == 2 ^ (256 :: Integer) - 1 = "MAX_UINT256"- | otherwise = Text.pack (show (i :: Integer))+ | otherwise = T.pack (show (i :: Integer)) where i = case signed of Signed -> num (signedWord w)@@ -85,18 +78,18 @@ showWordExplanation :: W256 -> DappInfo -> Text showWordExplanation w _ | w > 0xffffffff = showDec Unsigned w showWordExplanation w dapp =- case Map.lookup (fromIntegral w) (view dappAbiMap dapp) of+ case Map.lookup (fromIntegral w) dapp.abiMap of Nothing -> showDec Unsigned w- Just x -> "keccak(\"" <> view methodSignature x <> "\")"+ Just x -> "keccak(\"" <> x.methodSignature <> "\")" humanizeInteger :: (Num a, Integral a, Show a) => a -> Text humanizeInteger =- Text.intercalate ","+ T.intercalate "," . reverse- . map Text.reverse- . Text.chunksOf 3- . Text.reverse- . Text.pack+ . map T.reverse+ . T.chunksOf 3+ . T.reverse+ . T.pack . show prettyIfConcreteWord :: Expr EWord -> Text@@ -109,14 +102,14 @@ showAbiValue (AbiBytesDynamic bs) = formatBytes bs showAbiValue (AbiBytes _ bs) = formatBinary bs showAbiValue (AbiAddress addr) =- let dappinfo = view contextInfo ?context- contracts = view contextEnv ?context- name = case (Map.lookup addr contracts) of+ let dappinfo = ?context.info+ contracts = ?context.env+ name = case Map.lookup addr contracts of Nothing -> "" Just contract ->- let hash = maybeLitWord $ view EVM.codehash contract+ let hash = maybeLitWord contract._codehash in case hash of- Just h -> maybeContractName' (preview (dappSolcByHash . ix h . _2) dappinfo)+ Just h -> maybeContractName' (preview (ix h . _2) dappinfo.solcByHash) Nothing -> "" in name <> "@" <> (pack $ show addr)@@ -147,9 +140,9 @@ showError :: (?context :: DappContext) => Expr Buf -> Text showError (ConcreteBuf bs) =- let dappinfo = view contextInfo ?context+ let dappinfo = ?context.info bs4 = BS.take 4 bs- in case Map.lookup (word bs4) (view dappErrorMap dappinfo) of+ in case Map.lookup (word bs4) dappinfo.errorMap of Just (SolError errName ts) -> errName <> " " <> showCall ts (ConcreteBuf bs) Nothing -> case bs4 of -- Method ID for Error(string)@@ -165,7 +158,7 @@ decodeUtf8' >>> either (const False)- (Text.all (\c-> Char.isPrint c && (not . Char.isControl) c))+ (T.all (\c-> Char.isPrint c && (not . Char.isControl) c)) formatBytes :: ByteString -> Text formatBytes b =@@ -177,7 +170,7 @@ -- a string that came from bytes, displayed with special quotes formatBString :: ByteString -> Text-formatBString b = mconcat [ "«", Text.dropAround (=='"') (pack $ formatString b), "»" ]+formatBString b = mconcat [ "«", T.dropAround (=='"') (pack $ formatString b), "»" ] formatBinary :: ByteString -> Text formatBinary =@@ -199,14 +192,14 @@ showTrace :: DappInfo -> VM -> Trace -> Text showTrace dapp vm trace =- let ?context = DappContext { _contextInfo = dapp, _contextEnv = vm ^?! EVM.env . EVM.contracts }+ let ?context = DappContext { info = dapp, env = vm._env._contracts } in let pos = case showTraceLocation dapp trace of Left x -> " \x1b[1m" <> x <> "\x1b[0m" Right x -> " \x1b[1m(" <> x <> ")\x1b[0m"- fullAbiMap = view dappAbiMap dapp- in case view traceData trace of+ fullAbiMap = dapp.abiMap+ in case trace._traceData of EventTrace _ bytes topics -> let logn = mconcat [ "\x1b[36m"@@ -233,7 +226,7 @@ (t1:_) -> case maybeLitWord t1 of Just topic ->- case Map.lookup (topic) (view dappEventMap dapp) of+ case Map.lookup topic dapp.eventMap of Just (Event name _ types) -> knownTopic name types Nothing ->@@ -255,9 +248,9 @@ Nothing -> "<symbolic>" in- case Map.lookup sig (view dappAbiMap dapp) of+ case Map.lookup sig dapp.abiMap of Just m ->- lognote (view methodSignature m) usr+ lognote m.methodSignature usr Nothing -> logn _ ->@@ -289,7 +282,7 @@ "← " <> case Map.lookup (fromIntegral abi) fullAbiMap of Just m ->- case unzip (view methodOutput m) of+ case unzip m.output of ([], []) -> formatSBinary out (_, ts) ->@@ -305,7 +298,7 @@ t FrameTrace (CreationContext addr (Lit hash) _ _ ) -> -- FIXME: irrefutable pattern "create "- <> maybeContractName (preview (dappSolcByHash . ix hash . _2) dapp)+ <> maybeContractName (preview (ix hash . _2) dapp.solcByHash) <> "@" <> pack (show addr) <> pos FrameTrace (CreationContext addr _ _ _ ) ->@@ -318,7 +311,7 @@ then "call " else "delegatecall " hash' = fromJust $ maybeLitWord hash- in case preview (dappSolcByHash . ix hash' . _2) dapp of+ in case preview (ix hash' . _2) dapp.solcByHash of Nothing -> calltype <> pack (show target)@@ -326,9 +319,9 @@ <> case Map.lookup (fromIntegral (fromMaybe 0x00 abi)) fullAbiMap of Just m -> "\x1b[1m"- <> view methodName m+ <> m.name <> "\x1b[0m"- <> showCall (catMaybes (getAbiTypes (view methodSignature m))) calldata+ <> showCall (catMaybes (getAbiTypes m.methodSignature)) calldata Nothing -> formatSBinary calldata <> pos@@ -336,7 +329,7 @@ Just solc -> calltype <> "\x1b[1m"- <> view (contractName . to contractNamePart) solc+ <> contractNamePart solc.contractName <> "::" <> maybe "[fallback function]" (fromMaybe "[unknown method]" . maybeAbiName solc)@@ -356,20 +349,20 @@ maybeContractName :: Maybe SolcContract -> Text maybeContractName =- maybe "<unknown contract>" (view (contractName . to contractNamePart))+ maybe "<unknown contract>" (contractNamePart . (.contractName)) maybeContractName' :: Maybe SolcContract -> Text maybeContractName' =- maybe "" (view (contractName . to contractNamePart))+ maybe "" (contractNamePart . (.contractName)) maybeAbiName :: SolcContract -> W256 -> Maybe Text-maybeAbiName solc abi = preview (abiMap . ix (fromIntegral abi) . methodSignature) solc+maybeAbiName solc abi = Map.lookup (fromIntegral abi) solc.abiMap <&> (.methodSignature) contractNamePart :: Text -> Text-contractNamePart x = Text.split (== ':') x !! 1+contractNamePart x = T.split (== ':') x !! 1 contractPathPart :: Text -> Text-contractPathPart x = Text.split (== ':') x !! 0+contractPathPart x = T.split (== ':') x !! 0 prettyError :: EVM.Types.Error -> String prettyError= \case@@ -379,6 +372,7 @@ EVM.Types.StackLimitExceeded -> "Stack limit exceeded" EVM.Types.InvalidMemoryAccess -> "Invalid memory access" EVM.Types.BadJumpDestination -> "Bad jump destination"+ EVM.Types.StackUnderrun -> "Stack underrun" TmpErr err -> "Temp error: " <> err
src/EVM/Keccak.hs view
@@ -27,7 +27,7 @@ go = \case e@(Keccak _) -> do s <- get- put $ s{keccaks=Set.insert e (keccaks s)}+ put $ s{keccaks=Set.insert e s.keccaks} pure e e -> pure e @@ -73,7 +73,7 @@ where (_, st) = runState (findKeccakPropsExprs ps bufs stores) initState - injectivity = fmap injProp $ combine (Set.toList (keccaks st))- minValue = fmap minProp (Set.toList (keccaks st))+ injectivity = fmap injProp $ combine (Set.toList st.keccaks)+ minValue = fmap minProp (Set.toList st.keccaks)
src/EVM/Op.hs view
@@ -62,6 +62,7 @@ | OpGaslimit | OpChainid | OpSelfbalance+ | OpBaseFee | OpPop | OpMload | OpMstore@@ -145,6 +146,7 @@ OpGaslimit -> "GASLIMIT" OpChainid -> "CHAINID" OpSelfbalance -> "SELFBALANCE"+ OpBaseFee -> "BASEFEE" OpPop -> "POP" OpMload -> "MLOAD" OpMstore -> "MSTORE"
src/EVM/SMT.hs view
@@ -13,33 +13,28 @@ import Prelude hiding (LT, GT) -import GHC.Natural import Control.Monad-import GHC.IO.Handle (Handle, hFlush, hSetBuffering, BufferMode(..))-import Control.Concurrent.Chan (Chan, newChan, writeChan, readChan)-import Control.Concurrent (forkIO, killThread)-import Data.Char (isSpace) import Data.Containers.ListUtils (nubOrd) import Language.SMT2.Parser (getValueRes, parseCommentFreeFileMsg)-import Language.SMT2.Syntax (SpecConstant(..), GeneralRes(..), Term(..), QualIdentifier(..), Identifier(..), Sort(..), Index(..), VarBinding(..))+import Language.SMT2.Syntax (Symbol, SpecConstant(..), GeneralRes(..), Term(..), QualIdentifier(..), Identifier(..), Sort(..), Index(..), VarBinding(..)) import Data.Word-import Numeric (readHex)+import Numeric (readHex, readBin) import Data.ByteString (ByteString) import qualified Data.ByteString as BS import qualified Data.List as List import Data.List.NonEmpty (NonEmpty((:|)))+import qualified Data.List.NonEmpty as NonEmpty import Data.String.Here-import Data.Maybe+import Data.Maybe (fromJust) import Data.Map (Map) import qualified Data.Map as Map import Data.Text.Lazy (Text) import qualified Data.Text as TS import qualified Data.Text.Lazy as T-import qualified Data.Text.Lazy.IO as T import Data.Text.Lazy.Builder import Data.Bifunctor (second)-import System.Process (createProcess, cleanupProcess, proc, ProcessHandle, std_in, std_out, std_err, StdStream(..))+import Data.Semigroup (Any, Any(..), getAny) import EVM.Types import EVM.Traversals@@ -53,18 +48,20 @@ data CexVars = CexVars { calldataV :: [Text] , buffersV :: [Text]+ , storeReads :: [(Expr EWord, Expr EWord)] -- a list of relevant store reads , blockContextV :: [Text] , txContextV :: [Text] } deriving (Eq, Show) instance Semigroup CexVars where- (CexVars a b c d) <> (CexVars a2 b2 c2 d2) = CexVars (a <> a2) (b <> b2) (c <> c2) (d <> d2)+ (CexVars a b c d e) <> (CexVars a2 b2 c2 d2 e2) = CexVars (a <> a2) (b <> b2) (c <> c2) (d <> d2) (e <> e2) instance Monoid CexVars where mempty = CexVars { calldataV = mempty , buffersV = mempty+ , storeReads = mempty , blockContextV = mempty , txContextV = mempty }@@ -72,13 +69,14 @@ data SMTCex = SMTCex { vars :: Map (Expr EWord) W256 , buffers :: Map (Expr Buf) ByteString+ , store :: Map W256 (Map W256 W256) , blockContext :: Map (Expr EWord) W256 , txContext :: Map (Expr EWord) W256 } deriving (Eq, Show) getVar :: EVM.SMT.SMTCex -> TS.Text -> W256-getVar cex name = fromJust $ Map.lookup (Var name) (vars cex)+getVar cex name = fromJust $ Map.lookup (Var name) cex.vars data SMT2 = SMT2 [Builder] CexVars deriving (Eq, Show)@@ -125,6 +123,7 @@ <> keccakAssumes <> SMT2 [""] mempty <> SMT2 (fmap (\p -> "(assert " <> p <> ")") encs) mempty+ <> SMT2 [] mempty{storeReads = storageReads} where (ps_elim, bufs, stores) = eliminateProps ps@@ -137,11 +136,125 @@ bufVals = Map.elems bufs storeVals = Map.elems stores + storageReads = nubOrd $ concatMap findStorageReads ps+ keccakAssumes = SMT2 ["; keccak assumptions"] mempty <> SMT2 (fmap (\p -> "(assert " <> propToSMT p <> ")") (keccakAssumptions ps_elim bufVals storeVals)) 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]+referencedFrameContextGo = \case+ 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)]+ Balance {} -> error "TODO: BALANCE"+ SelfBalance {} -> error "TODO: SELFBALANCE"+ Gas {} -> error "TODO: GAS"+ _ -> []++referencedFrameContext :: Expr a -> [Builder]+referencedFrameContext expr = nubOrd $ foldExpr referencedFrameContextGo [] expr++referencedFrameContext' :: Prop -> [Builder]+referencedFrameContext' prop = nubOrd $ foldProp referencedFrameContextGo [] prop+++referencedBlockContextGo :: Expr a -> [Builder]+referencedBlockContextGo = \case+ Origin -> ["origin"]+ Coinbase -> ["coinbase"]+ Timestamp -> ["timestamp"]+ BlockNumber -> ["blocknumber"]+ PrevRandao -> ["prevrandao"]+ GasLimit -> ["gaslimit"]+ ChainId -> ["chainid"]+ BaseFee -> ["basefee"]+ _ -> []++referencedBlockContext :: Expr a -> [Builder]+referencedBlockContext expr = nubOrd $ foldExpr referencedBlockContextGo [] expr++referencedBlockContext' :: Prop -> [Builder]+referencedBlockContext' prop = nubOrd $ foldProp referencedBlockContextGo [] prop+++isAbstractStorage :: Expr Storage -> Bool+isAbstractStorage = getAny . foldExpr go (Any False)+ where+ go :: Expr a -> Any+ go AbstractStore = Any True+ go _ = Any False++-- | This function overapproximates the reads from the abstract+-- storage. Potentially, it can return locations that do not read a+-- slot directly from the abstract store but from subsequent writes on+-- 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 []+ where+ go :: Expr a -> [(Expr EWord, Expr EWord)]+ go = \case+ SLoad addr slot storage -> [(addr, slot) | isAbstractStorage storage]+ _ -> []+++declareBufs :: [Builder] -> SMT2+declareBufs names = SMT2 ("; buffers" : fmap declareBuf names <> ("; buffer lengths" : fmap declareLength names)) cexvars+ where+ declareBuf n = "(declare-const " <> n <> " (Array (_ BitVec 256) (_ BitVec 8)))"+ declareLength n = "(define-const " <> n <> "_length" <> " (_ BitVec 256) (bufLength " <> n <> "))"+ cexvars = mempty{buffersV = fmap toLazyText names}+++-- Given a list of 256b VM variable names, create an SMT2 object with the variables declared+declareVars :: [Builder] -> SMT2+declareVars names = SMT2 (["; variables"] <> fmap declare names) cexvars+ where+ declare n = "(declare-const " <> n <> " (_ BitVec 256))"+ cexvars = mempty{calldataV = fmap toLazyText names}+++declareFrameContext :: [Builder] -> SMT2+declareFrameContext names = SMT2 (["; frame context"] <> fmap declare names) cexvars+ where+ declare n = "(declare-const " <> n <> " (_ BitVec 256))"+ cexvars = mempty{txContextV = fmap toLazyText names}+++declareBlockContext :: [Builder] -> SMT2+declareBlockContext names = SMT2 (["; block context"] <> fmap declare names) cexvars+ where+ declare n = "(declare-const " <> n <> " (_ BitVec 256))"+ cexvars = mempty{blockContextV = fmap toLazyText names}++ prelude :: SMT2 prelude = (flip SMT2) mempty $ fmap (fromLazyText . T.drop 2) . T.lines $ [i| ; logic@@ -348,6 +461,7 @@ (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)))@@ -357,145 +471,7 @@ (define-fun sload ((addr Word) (key Word) (storage Storage)) Word (select (select storage addr) key)) |] -declareBufs :: [Builder] -> SMT2-declareBufs names = SMT2 ("; buffers" : fmap declareBuf names <> ("; buffer lengths" : fmap declareLength names)) cexvars- where- declareBuf n = "(declare-const " <> n <> " (Array (_ BitVec 256) (_ BitVec 8)))"- declareLength n = "(define-const " <> n <> "_length" <> " (_ BitVec 256) (bufLength " <> n <> "))"- cexvars = CexVars- { calldataV = mempty- , buffersV = (fmap toLazyText names)- , blockContextV = mempty- , txContextV = mempty- } -referencedBufs :: Expr a -> [Builder]-referencedBufs expr = nubOrd (foldExpr go [] expr)- where- go :: Expr a -> [Builder]- go = \case- AbstractBuf s -> [fromText s]- _ -> []--referencedBufs' :: Prop -> [Builder]-referencedBufs' = \case- PEq a b -> nubOrd $ referencedBufs a <> referencedBufs b- PLT a b -> nubOrd $ referencedBufs a <> referencedBufs b- PGT a b -> nubOrd $ referencedBufs a <> referencedBufs b- PLEq a b -> nubOrd $ referencedBufs a <> referencedBufs b- PGEq a b -> nubOrd $ referencedBufs a <> referencedBufs b- PAnd a b -> nubOrd $ referencedBufs' a <> referencedBufs' b- POr a b -> nubOrd $ referencedBufs' a <> referencedBufs' b- PNeg a -> referencedBufs' a- PBool _ -> []--referencedVars' :: Prop -> [Builder]-referencedVars' = \case- PEq a b -> nubOrd $ referencedVars a <> referencedVars b- PLT a b -> nubOrd $ referencedVars a <> referencedVars b- PGT a b -> nubOrd $ referencedVars a <> referencedVars b- PLEq a b -> nubOrd $ referencedVars a <> referencedVars b- PGEq a b -> nubOrd $ referencedVars a <> referencedVars b- PAnd a b -> nubOrd $ referencedVars' a <> referencedVars' b- POr a b -> nubOrd $ referencedVars' a <> referencedVars' b- PNeg a -> referencedVars' a- PBool _ -> []--referencedFrameContext' :: Prop -> [Builder]-referencedFrameContext' = \case- PEq a b -> nubOrd $ referencedFrameContext a <> referencedFrameContext b- PLT a b -> nubOrd $ referencedFrameContext a <> referencedFrameContext b- PGT a b -> nubOrd $ referencedFrameContext a <> referencedFrameContext b- PLEq a b -> nubOrd $ referencedFrameContext a <> referencedFrameContext b- PGEq a b -> nubOrd $ referencedFrameContext a <> referencedFrameContext b- PAnd a b -> nubOrd $ referencedFrameContext' a <> referencedFrameContext' b- POr a b -> nubOrd $ referencedFrameContext' a <> referencedFrameContext' b- PNeg a -> referencedFrameContext' a- PBool _ -> []--referencedBlockContext' :: Prop -> [Builder]-referencedBlockContext' = \case- PEq a b -> nubOrd $ referencedBlockContext a <> referencedBlockContext b- PLT a b -> nubOrd $ referencedBlockContext a <> referencedBlockContext b- PGT a b -> nubOrd $ referencedBlockContext a <> referencedBlockContext b- PLEq a b -> nubOrd $ referencedBlockContext a <> referencedBlockContext b- PGEq a b -> nubOrd $ referencedBlockContext a <> referencedBlockContext b- PAnd a b -> nubOrd $ referencedBlockContext' a <> referencedBlockContext' b- POr a b -> nubOrd $ referencedBlockContext' a <> referencedBlockContext' b- PNeg a -> referencedBlockContext' a- PBool _ -> []---- Given a list of 256b VM variable names, create an SMT2 object with the variables declared-declareVars :: [Builder] -> SMT2-declareVars names = SMT2 (["; variables"] <> fmap declare names) cexvars- where- declare n = "(declare-const " <> n <> " (_ BitVec 256))"- cexvars = CexVars- { calldataV = fmap toLazyText names- , buffersV = mempty- , blockContextV = mempty- , txContextV = mempty- }--referencedVars :: Expr a -> [Builder]-referencedVars expr = nubOrd (foldExpr go [] expr)- where- go :: Expr a -> [Builder]- go = \case- Var s -> [fromText s]- _ -> []--declareFrameContext :: [Builder] -> SMT2-declareFrameContext names = SMT2 (["; frame context"] <> fmap declare names) cexvars- where- declare n = "(declare-const " <> n <> " (_ BitVec 256))"- cexvars = CexVars- { calldataV = mempty- , buffersV = mempty- , blockContextV = mempty- , txContextV = fmap toLazyText names- }--referencedFrameContext :: Expr a -> [Builder]-referencedFrameContext expr = nubOrd (foldExpr go [] expr)- where- go :: Expr a -> [Builder]- go = \case- 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)]- Balance {} -> error "TODO: BALANCE"- SelfBalance {} -> error "TODO: SELFBALANCE"- Gas {} -> error "TODO: GAS"- _ -> []--declareBlockContext :: [Builder] -> SMT2-declareBlockContext names = SMT2 (["; block context"] <> fmap declare names) cexvars- where- declare n = "(declare-const " <> n <> " (_ BitVec 256))"- cexvars = CexVars- { calldataV = mempty- , buffersV = mempty- , blockContextV = fmap toLazyText names- , txContextV = mempty- }--referencedBlockContext :: Expr a -> [Builder]-referencedBlockContext expr = nubOrd (foldExpr go [] expr)- where- go :: Expr a -> [Builder]- go = \case- Origin -> ["origin"]- Coinbase -> ["coinbase"]- Timestamp -> ["timestamp"]- BlockNumber -> ["blocknumber"]- PrevRandao -> ["prevrandao"]- GasLimit -> ["gaslimit"]- ChainId -> ["chainid"]- BaseFee -> ["basefee"]- _ -> []-- exprToSMT :: Expr a -> Builder exprToSMT = \case Lit w -> fromLazyText $ "(_ bv" <> (T.pack $ show (num w :: Integer)) <> " 256)"@@ -565,6 +541,14 @@ bLift = "(concat (_ bv0 256) " <> bExp <> ")" cLift = "(concat (_ bv0 256) " <> cExp <> ")" in "((_ extract 255 0) (ite (= " <> cExp <> " (_ bv0 256)) (_ bv0 512) (bvurem (bvmul " <> aLift `sp` bLift <> ")" <> cLift <> ")))"+ AddMod a b c ->+ let aExp = exprToSMT a+ bExp = exprToSMT b+ cExp = exprToSMT c+ aLift = "(concat (_ bv0 256) " <> aExp <> ")"+ bLift = "(concat (_ bv0 256) " <> bExp <> ")"+ cLift = "(concat (_ bv0 256) " <> cExp <> ")"+ in "((_ extract 255 0) (ite (= " <> cExp <> " (_ bv0 256)) (_ bv0 512) (bvurem (bvadd " <> aLift `sp` bLift <> ")" <> cLift <> ")))" EqByte a b -> let cond = op2 "=" a b in "(ite " <> cond `sp` one `sp` zero <> ")"@@ -684,71 +668,70 @@ "(" <> op <> " " <> aenc <> " " <> benc <> ")" --- ** Execution ** ------------------------------------------------------------------------------- +-- ** Helpers ** --------------------------------------------------------------------------------- --- | Supported solvers-data Solver- = Z3- | CVC5- | Bitwuzla- | Custom Text -instance Show Solver where- show Z3 = "z3"- show CVC5 = "cvc5"- show Bitwuzla = "bitwuzla"- show (Custom s) = T.unpack s+-- | Stores a region of src into dst+copySlice :: Expr EWord -> Expr EWord -> Expr EWord -> Builder -> Builder -> Builder+copySlice srcOffset dstOffset size@(Lit _) src dst+ | size == (Lit 0) = dst+ | otherwise =+ let size' = (sub size (Lit 1))+ encDstOff = exprToSMT (add dstOffset size')+ encSrcOff = exprToSMT (add srcOffset size')+ child = copySlice srcOffset dstOffset size' src dst in+ "(store " <> child `sp` encDstOff `sp` "(select " <> src `sp` encSrcOff <> "))"+copySlice _ _ _ _ _ = error "TODO: implement copySlice with a symbolically sized region" +-- | Unrolls an exponentiation into a series of multiplications+expandExp :: Expr EWord -> W256 -> Builder+expandExp base expnt+ | expnt == 1 = exprToSMT base+ | otherwise =+ let b = exprToSMT base+ n = expandExp base (expnt - 1) in+ "(bvmul " <> b `sp` n <> ")" --- | A running solver instance-data SolverInstance = SolverInstance- { _type :: Solver- , _stdin :: Handle- , _stdout :: Handle- , _stderr :: Handle- , _process :: ProcessHandle- }+-- | Concatenates a list of bytes into a larger bitvector+concatBytes :: [Expr Byte] -> Builder+concatBytes bytes =+ let bytesRev = reverse bytes+ a2 = exprToSMT (head bytesRev) in+ foldl wrap a2 $ tail bytesRev+ where+ wrap inner byte =+ let byteSMT = exprToSMT byte in+ "(concat " <> byteSMT `sp` inner <> ")" --- | A channel representing a group of solvers-newtype SolverGroup = SolverGroup (Chan Task)+-- | Concatenates a list of bytes into a larger bitvector+writeBytes :: ByteString -> Expr Buf -> Builder+writeBytes bytes buf = snd $ BS.foldl' wrap (0, exprToSMT buf) bytes+ where+ -- we don't need to store zeros if the base buffer is empty+ skipZeros = buf == mempty+ wrap :: (Int, Builder) -> Word8 -> (Int, Builder)+ wrap (idx, inner) byte =+ if skipZeros && byte == 0+ then (idx + 1, inner)+ else let+ byteSMT = exprToSMT (LitByte byte)+ idxSMT = exprToSMT . Lit . num $ idx+ in (idx + 1, "(store " <> inner `sp` idxSMT `sp` byteSMT <> ")") --- | A script to be executed, a list of models to be extracted in the case of a sat result, and a channel where the result should be written-data Task = Task- { script :: SMT2- , resultChan :: Chan CheckSatResult- }+encodeConcreteStore :: Map W256 (Map W256 W256) -> Builder+encodeConcreteStore s = foldl encodeWrite "emptyStore" writes+ 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)+ encKey = exprToSMT (Lit key)+ encVal = exprToSMT (Lit val)+ in "(sstore " <> encAddr `sp` encKey `sp` encVal `sp` prev <> ")" --- | The result of a call to (check-sat)-data CheckSatResult- = Sat SMTCex- | Unsat- | Unknown- | Error TS.Text- deriving (Show, Eq) -isSat :: CheckSatResult -> Bool-isSat (Sat _) = True-isSat _ = False--isErr :: CheckSatResult -> Bool-isErr (Error _) = True-isErr _ = False--isUnsat :: CheckSatResult -> Bool-isUnsat Unsat = True-isUnsat _ = False--checkSat :: SolverGroup -> SMT2 -> IO CheckSatResult-checkSat (SolverGroup taskQueue) script = do- -- prepare result channel- resChan <- newChan-- -- send task to solver group- writeChan taskQueue (Task script resChan)-- -- collect result- readChan resChan+-- ** Cex parsing ** -------------------------------------------------------------------------------- parseW256 :: SpecConstant -> W256 parseW256 = parseSC@@ -759,6 +742,11 @@ parseW8 :: SpecConstant -> Word8 parseW8 = parseSC +parseSC :: (Num a, Eq a) => SpecConstant -> a+parseSC (SCHexadecimal a) = fst . head . Numeric.readHex . T.unpack . T.fromStrict $ a+parseSC (SCBinary a) = fst . head . Numeric.readBin . T.unpack . T.fromStrict $ a+parseSC sc = error $ "Internal Error: cannot parse: " <> show sc+ parseErr :: (Show a) => a -> b parseErr res = error $ "Internal Error: cannot parse solver response: " <> show res @@ -783,12 +771,12 @@ ('a':'d':'d':'r':'e':'s':'s':'_':frame) -> Address (read frame) t -> error $ "Internal Error: cannot parse " <> t <> " into an Expr" -getVars :: (TS.Text -> Expr EWord) -> SolverInstance -> [TS.Text] -> IO (Map (Expr EWord) W256)-getVars parseFn inst names = Map.mapKeys parseFn <$> foldM getOne 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 <- getValue inst (T.fromStrict name)+ raw <- getVal (T.fromStrict name) let parsed = case parseCommentFreeFileMsg getValueRes (T.toStrict raw) of Right (ResSpecific (valParsed :| [])) -> valParsed@@ -803,12 +791,12 @@ r -> parseErr r pure $ Map.insert name val acc -getBufs :: SolverInstance -> [TS.Text] -> IO (Map (Expr Buf) ByteString)-getBufs inst names = foldM getBuf mempty names+getBufs :: (Text -> IO Text) -> [TS.Text] -> IO (Map (Expr Buf) ByteString)+getBufs getVal names = foldM getBuf mempty names where getLength :: TS.Text -> IO Int getLength name = do- val <- getValue inst (T.fromStrict name <> "_length")+ val <- getVal (T.fromStrict name <> "_length") len <- case parseCommentFreeFileMsg getValueRes (T.toStrict val) of Right (ResSpecific (parsed :| [])) -> case parsed of (TermQualIdentifier (Unqualified (IdSymbol symbol)), (TermSpecConstant sc))@@ -833,7 +821,7 @@ -- this buffer and then use that to produce a shorter counterexample (by -- replicating the constant byte up to the length). len <- getLength name- val <- getValue inst (T.fromStrict name)+ val <- getVal (T.fromStrict name) buf <- case parseCommentFreeFileMsg getValueRes (T.toStrict val) of Right (ResSpecific (valParsed :| [])) -> case valParsed of (TermQualIdentifier (Unqualified (IdSymbol symbol)), term)@@ -886,213 +874,91 @@ <> " in environment mapping" p -> parseErr p --parseSC :: (Num a, Eq a) => SpecConstant -> a-parseSC (SCHexadecimal a) = fst . head . Numeric.readHex . T.unpack . T.fromStrict $ a-parseSC sc = error $ "Internal Error: cannot parse: " <> show sc--withSolvers :: Solver -> Natural -> Maybe Natural -> (SolverGroup -> IO a) -> IO a-withSolvers solver count timeout cont = do- -- spawn solvers- instances <- mapM (const $ spawnSolver solver timeout) [1..count]-- -- spawn orchestration thread- taskQueue <- newChan- availableInstances <- newChan- forM_ instances (writeChan availableInstances)- orchestrateId <- forkIO $ orchestrate taskQueue availableInstances+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 error "Internal Error: solver did not return model for requested value"+ r -> parseErr r - -- run continuation with task queue- res <- cont (SolverGroup taskQueue)+ -- then create a map by adding only the locations that are read by the program+ foldM (\m (addr, slot) -> do+ addr' <- queryValue addr+ slot' <- queryValue slot+ pure $ addElem addr' slot' m fun) Map.empty sreads - -- cleanup and return results- mapM_ stopSolver instances- killThread orchestrateId- pure res where- orchestrate queue avail = do- task <- readChan queue- inst <- readChan avail- _ <- forkIO $ runTask task inst avail- orchestrate queue avail - runTask (Task (SMT2 cmds cexvars) r) inst availableInstances = do- -- reset solver and send all lines of provided script- out <- sendScript inst (SMT2 ("(reset)" : cmds) cexvars)- 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" -> do- calldatamodels <- getVars parseVar inst (fmap T.toStrict $ calldataV cexvars)- buffermodels <- getBufs inst (fmap T.toStrict $ buffersV cexvars)- blockctxmodels <- getVars parseBlockCtx inst (fmap T.toStrict $ blockContextV cexvars)- txctxmodels <- getVars parseFrameCtx inst (fmap T.toStrict $ txContextV cexvars)- pure $ Sat $ SMTCex- { vars = calldatamodels- , buffers = buffermodels- , blockContext = blockctxmodels- , txContext = txctxmodels- }- "unsat" -> pure Unsat- "timeout" -> pure Unknown- "unknown" -> pure Unknown- _ -> pure . Error $ T.toStrict $ "Unable to parse solver output: " <> sat- writeChan r res+ 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 - -- put the instance back in the list of available instances- writeChan availableInstances inst --- | Arguments used when spawing a solver instance-solverArgs :: Solver -> Maybe (Natural) -> [Text]-solverArgs solver timeout = case solver of- Bitwuzla -> error "TODO: Bitwuzla args"- Z3 ->- [ "-in" ]- CVC5 ->- [ "--lang=smt"- , "--no-interactive"- , "--produce-models"- , "--tlimit-per=" <> T.pack (show (1000 * fromMaybe 10 timeout))- ]- Custom _ -> []+ queryValue :: Expr EWord -> IO W256+ queryValue (Lit w) = pure w+ queryValue w = do+ let expr = toLazyText $ exprToSMT w+ raw <- getVal expr+ case parseCommentFreeFileMsg getValueRes (T.toStrict raw) of+ Right (ResSpecific (valParsed :| [])) ->+ case valParsed of+ (_, TermSpecConstant sc) -> pure $ parseW256 sc+ _ -> error "Internal Error: cannot parse model for storage index"+ r -> parseErr r --- | Spawns a solver instance, and sets the various global config options that we use for our queries-spawnSolver :: Solver -> Maybe (Natural) -> IO SolverInstance-spawnSolver solver timeout = do- let cmd = (proc (show solver) (fmap T.unpack $ solverArgs solver timeout)) { std_in = CreatePipe, std_out = CreatePipe, std_err = CreatePipe }- (Just stdin, Just stdout, Just stderr, process) <- createProcess cmd- hSetBuffering stdin (BlockBuffering (Just 1000000))- let solverInstance = SolverInstance solver stdin stdout stderr process- case timeout of- Nothing -> pure solverInstance- Just t -> case solver of- CVC5 -> pure solverInstance- _ -> do- _ <- sendLine' solverInstance $ "(set-option :timeout " <> T.pack (show t) <> ")"- pure solverInstance --- | Cleanly shutdown a running solver instnace-stopSolver :: SolverInstance -> IO ()-stopSolver (SolverInstance _ stdin stdout stderr process) = cleanupProcess (Just stdin, Just stdout, Just stderr, process) --- | 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 (T.unlines $ fmap toLazyText cmds)- pure $ Right()---- | 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- -- trim leading whitespace- let cmd' = T.dropWhile isSpace cmd- case T.unpack cmd' of- "" -> pure "success" -- ignore blank lines- ';' : _ -> pure "success" -- ignore comments- _ -> sendLine inst cmd'---- | Sends a string to the solver and appends a newline, returns the first available line from the output buffer-sendLine :: SolverInstance -> Text -> IO Text-sendLine (SolverInstance _ stdin stdout _ _) cmd = do- T.hPutStr stdin (T.append cmd "\n")- hFlush stdin- T.hGetLine stdout---- | Sends a string to the solver and appends a newline, doesn't return stdout-sendLine' :: SolverInstance -> Text -> IO ()-sendLine' (SolverInstance _ stdin _ _ _) cmd = do- T.hPutStr stdin (T.append cmd "\n")- hFlush stdin---- | Returns a string representation of the model for the requested variable-getValue :: SolverInstance -> Text -> IO Text-getValue (SolverInstance _ stdin stdout _ _) var = do- T.hPutStr stdin (T.append (T.append "(get-value (" var) "))\n")- hFlush stdin- fmap (T.unlines . reverse) (readSExpr stdout)+-- | 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)+interpretNDArray interp env = \case+ -- variable reference+ TermQualIdentifier (Unqualified (IdSymbol s)) ->+ case Map.lookup s env of+ Just t -> interpretNDArray interp env t+ Nothing -> error "Internal error: unknown identifier, cannot parse array"+ -- (let (x t') t)+ TermLet (VarBinding x t' :| []) t -> interpretNDArray interp (Map.insert x t' env) t+ TermLet (VarBinding x t' :| lets) t -> interpretNDArray interp (Map.insert x t' env) (TermLet (NonEmpty.fromList lets) t)+ -- (as const (Array (_ BitVec 256) (_ BitVec 256))) SpecConstant+ TermApplication asconst (val :| []) | isArrConst asconst ->+ \_ -> interp env val+ -- (store arr ind val)+ TermApplication store (arr :| [TermSpecConstant ind, val]) | isStore store ->+ \x -> if x == parseW256 ind then interp env val else interpretNDArray interp env arr x+ t -> error $ "Internal error: cannot parse array value. Unexpected term: " <> (show t) --- | Reads lines from h until we have a balanced sexpr-readSExpr :: Handle -> IO [Text]-readSExpr h = go 0 0 [] where- go 0 0 _ = do- line <- T.hGetLine h- let ls = T.length $ T.filter (== '(') line- rs = T.length $ T.filter (== ')') line- if ls == rs- then pure [line]- else go ls rs [line]- go ls rs prev = do- line <- T.hGetLine h- let ls' = T.length $ T.filter (== '(') line- rs' = T.length $ T.filter (== ')') line- if (ls + ls') == (rs + rs')- then pure $ line : prev- else go (ls + ls') (rs + rs') (line : prev)--+ isArrConst :: QualIdentifier -> Bool+ isArrConst = \case+ Qualified (IdSymbol "const") (SortParameter (IdSymbol "Array") _) -> True+ _ -> False --- ** Helpers ** ---------------------------------------------------------------------------------+ isStore :: QualIdentifier -> Bool+ isStore t = t == Unqualified (IdSymbol "store") --- | Stores a region of src into dst-copySlice :: Expr EWord -> Expr EWord -> Expr EWord -> Builder -> Builder -> Builder-copySlice srcOffset dstOffset size@(Lit _) src dst- | size == (Lit 0) = dst- | otherwise =- let size' = (sub size (Lit 1))- encDstOff = exprToSMT (add dstOffset size')- encSrcOff = exprToSMT (add srcOffset size')- child = copySlice srcOffset dstOffset size' src dst in- "(store " <> child `sp` encDstOff `sp` "(select " <> src `sp` encSrcOff <> "))"-copySlice _ _ _ _ _ = error "TODO: implement copySlice with a symbolically sized region"---- | Unrolls an exponentiation into a series of multiplications-expandExp :: Expr EWord -> W256 -> Builder-expandExp base expnt- | expnt == 1 = exprToSMT base- | otherwise =- let b = exprToSMT base- n = expandExp base (expnt - 1) in- "(bvmul " <> b `sp` n <> ")"---- | Concatenates a list of bytes into a larger bitvector-concatBytes :: [Expr Byte] -> Builder-concatBytes bytes =- let bytesRev = reverse bytes- a2 = exprToSMT (head bytesRev) in- foldl wrap a2 $ tail bytesRev- where- wrap inner byte =- let byteSMT = exprToSMT byte in- "(concat " <> byteSMT `sp` inner <> ")"---- | Concatenates a list of bytes into a larger bitvector-writeBytes :: ByteString -> Expr Buf -> Builder-writeBytes bytes buf = snd $ BS.foldl' wrap (0, exprToSMT buf) bytes+-- | Interpret an 1-dimensional array as a function+interpret1DArray :: (Map Symbol Term) -> Term -> (W256 -> W256)+interpret1DArray = interpretNDArray interpretW256 where- -- we don't need to store zeros if the base buffer is empty- skipZeros = buf == mempty- wrap :: (Int, Builder) -> Word8 -> (Int, Builder)- wrap (idx, inner) byte =- if skipZeros && byte == 0- then (idx + 1, inner)- else let- byteSMT = exprToSMT (LitByte byte)- idxSMT = exprToSMT . Lit . num $ idx- in (idx + 1, "(store " <> inner `sp` idxSMT `sp` byteSMT <> ")")+ interpretW256 :: (Map Symbol Term) -> Term -> W256+ interpretW256 _ (TermSpecConstant val) = parseW256 val+ interpretW256 env (TermQualIdentifier (Unqualified (IdSymbol s))) =+ case Map.lookup s env of+ Just t -> interpretW256 env t+ Nothing -> error "Internal error: unknown identifier, cannot parse array"+ interpretW256 _ t = error $ "Internal error: cannot parse array value. Unexpected term: " <> (show t) -encodeConcreteStore :: Map W256 (Map W256 W256) -> Builder-encodeConcreteStore s = foldl encodeWrite "emptyStore" writes- 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)- encKey = exprToSMT (Lit key)- encVal = exprToSMT (Lit val)- in "(sstore " <> encAddr `sp` encKey `sp` encVal `sp` prev <> ")"+-- | 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,7 +1,6 @@ {-# Language DeriveAnyClass #-} {-# Language DataKinds #-} {-# Language StrictData #-}-{-# Language TemplateHaskell #-} {-# Language QuasiQuotes #-} module EVM.Solidity@@ -21,33 +20,12 @@ , SlotType (..) , Reference(..) , Mutability(..)- , methodName- , methodSignature- , methodInputs- , methodOutput- , methodMutability- , abiMap- , eventMap- , errorMap- , storageLayout- , contractName- , constructorInputs- , creationCode , functionAbi , makeSrcMaps , readSolc , readJSON , readStdJSON , readCombinedJSON- , runtimeCode- , runtimeCodehash- , creationCodehash- , runtimeSrcmap- , creationSrcmap- , sourceFiles- , sourceLines- , sourceAsts- , immutableReferences , stripBytecodeMetadata , stripBytecodeMetadataSym , signature@@ -66,49 +44,48 @@ import EVM.Types import Control.Applicative+import Control.Lens hiding (Indexed, (.=)) import Control.Monad-import Control.Lens hiding (Indexed, (.=))-import qualified Data.String.Here as Here import Data.Aeson hiding (json) import Data.Aeson.Types import Data.Aeson.Lens-import qualified Data.Aeson.KeyMap as KeyMap+import Data.Aeson.Key qualified as Key+import Data.Aeson.KeyMap qualified as KeyMap import Data.Scientific-import Data.ByteString (ByteString)+import Data.ByteString (ByteString)+import Data.ByteString qualified as BS+import Data.ByteString.Base16 qualified as BS16 import Data.ByteString.Lazy (toStrict)-import Data.Char (isDigit)+import Data.Char (isDigit) import Data.Foldable-import Data.Map.Strict (Map)+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.HashMap.Strict qualified as HMap+import Data.List (sort, isPrefixOf, isInfixOf, elemIndex, tails, findIndex)+import Data.List.NonEmpty (NonEmpty)+import Data.List.NonEmpty qualified as NonEmpty import Data.Maybe-import Data.Word (Word8, Word32)-import Data.List.NonEmpty (NonEmpty) import Data.Semigroup-import Data.Sequence (Seq)-import Data.Text (Text, pack, intercalate)-import Data.Text.Encoding (encodeUtf8, decodeUtf8)-import Data.Text.IO (readFile, writeFile)-import Data.Vector (Vector)-import GHC.Generics (Generic)-import Prelude hiding (readFile, writeFile)-import System.IO hiding (readFile, writeFile)+import Data.Sequence (Seq)+import Data.String.Here qualified as Here+import Data.Text (Text, pack, intercalate)+import Data.Text qualified as Text+import Data.Text.Encoding (encodeUtf8, decodeUtf8)+import Data.Text.IO (readFile, writeFile)+import Data.Vector (Vector)+import Data.Vector qualified as Vector+import Data.Word (Word8, Word32)+import GHC.Generics (Generic)+import Prelude hiding (readFile, writeFile)+import System.IO hiding (readFile, writeFile) import System.IO.Temp import System.Process-import Text.Read (readMaybe)--import qualified Data.Aeson.Key as Key-import qualified Data.ByteString as BS-import qualified Data.ByteString.Base16 as BS16-import qualified Data.HashMap.Strict as HMap-import qualified Data.Map.Strict as Map-import qualified Data.Text as Text-import qualified Data.Vector as Vector-import qualified Data.List.NonEmpty as NonEmpty-import Data.List (sort, isPrefixOf, isInfixOf, elemIndex, tails, findIndex)+import Text.Read (readMaybe) -data StorageItem = StorageItem {- _type :: SlotType,- _offset :: Int,- _slot :: Int+data StorageItem = StorageItem+ { slotType :: SlotType+ , offset :: Int+ , slot :: Int } deriving (Show, Eq) data SlotType@@ -143,27 +120,27 @@ readsPrec _ s = [(StorageValue $ fromMaybe (error "could not parse storage item") (parseTypeName mempty (pack s)),"")] data SolcContract = SolcContract- { _runtimeCodehash :: W256- , _creationCodehash :: W256- , _runtimeCode :: ByteString- , _creationCode :: ByteString- , _contractName :: Text- , _constructorInputs :: [(Text, AbiType)]- , _abiMap :: Map Word32 Method- , _eventMap :: Map W256 Event- , _errorMap :: Map W256 SolError- , _immutableReferences :: Map W256 [Reference]- , _storageLayout :: Maybe (Map Text StorageItem)- , _runtimeSrcmap :: Seq SrcMap- , _creationSrcmap :: Seq SrcMap+ { runtimeCodehash :: W256+ , creationCodehash :: W256+ , runtimeCode :: ByteString+ , creationCode :: ByteString+ , contractName :: Text+ , constructorInputs :: [(Text, AbiType)]+ , abiMap :: Map Word32 Method+ , eventMap :: Map W256 Event+ , errorMap :: Map W256 SolError+ , immutableReferences :: Map W256 [Reference]+ , storageLayout :: Maybe (Map Text StorageItem)+ , runtimeSrcmap :: Seq SrcMap+ , creationSrcmap :: Seq SrcMap } deriving (Show, Eq, Generic) data Method = Method- { _methodOutput :: [(Text, AbiType)]- , _methodInputs :: [(Text, AbiType)]- , _methodName :: Text- , _methodSignature :: Text- , _methodMutability :: Mutability+ { output :: [(Text, AbiType)]+ , inputs :: [(Text, AbiType)]+ , name :: Text+ , methodSignature :: Text+ , mutability :: Mutability } deriving (Show, Eq, Ord, Generic) data Mutability@@ -174,14 +151,14 @@ deriving (Show, Eq, Ord, Generic) data SourceCache = SourceCache- { _sourceFiles :: [(Text, ByteString)]- , _sourceLines :: [(Vector ByteString)]- , _sourceAsts :: Map Text Value+ { files :: [(Text, ByteString)]+ , lines :: [(Vector ByteString)]+ , asts :: Map Text Value } deriving (Show, Eq, Generic) data Reference = Reference- { _refStart :: Int,- _refLength :: Int+ { start :: Int,+ length :: Int } deriving (Show, Eq) instance FromJSON Reference where@@ -220,10 +197,6 @@ data CodeType = Creation | Runtime deriving (Show, Eq, Ord) -makeLenses ''SolcContract-makeLenses ''SourceCache-makeLenses ''Method- -- Obscure but efficient parser for the Solidity sourcemap format. makeSrcMaps :: Text -> Maybe (Seq SrcMap) makeSrcMaps = (\case (_, Fe, _) -> Nothing; x -> Just (done x))@@ -278,9 +251,9 @@ f (fp, Nothing) = BS.readFile $ Text.unpack fp xs <- mapM f paths return $! SourceCache- { _sourceFiles = zip (fst <$> paths) xs- , _sourceLines = map (Vector.fromList . BS.split 0xa) xs- , _sourceAsts = asts+ { files = zip (fst <$> paths) xs+ , lines = map (Vector.fromList . BS.split 0xa) xs+ , asts = asts } lineSubrange ::@@ -324,19 +297,19 @@ solidity contract src = do (json, path) <- solidity' src let (sol, _, _) = fromJust $ readJSON json- return (sol ^? ix (path <> ":" <> contract) . creationCode)+ pure $ Map.lookup (path <> ":" <> contract) sol <&> (.creationCode) solcRuntime :: Text -> Text -> IO (Maybe ByteString) solcRuntime contract src = do (json, path) <- solidity' src let (sol, _, _) = fromJust $ readJSON json- return (sol ^? ix (path <> ":" <> contract) . runtimeCode)+ pure $ Map.lookup (path <> ":" <> contract) sol <&> (.runtimeCode) functionAbi :: Text -> IO Method functionAbi f = do (json, path) <- solidity' ("contract ABI { function " <> f <> " public {}}") let (sol, _, _) = fromJust $ readJSON json- case Map.toList $ sol ^?! ix (path <> ":ABI") . abiMap of+ case Map.toList $ (fromJust (Map.lookup (path <> ":ABI") sol)).abiMap of [(_,b)] -> return b _ -> error "hevm internal error: unexpected abi format" @@ -365,24 +338,24 @@ Just v -> v -- solc >= 0.8 Nothing -> (x ^?! key "abi" . _String) ^?! _Array -- solc < 0.8 in SolcContract {- _runtimeCode = theRuntimeCode,- _creationCode = theCreationCode,- _runtimeCodehash = keccak' (stripBytecodeMetadata theRuntimeCode),- _creationCodehash = keccak' (stripBytecodeMetadata theCreationCode),- _runtimeSrcmap = force "internal error: srcmap-runtime" (makeSrcMaps (x ^?! key "srcmap-runtime" . _String)),- _creationSrcmap = force "internal error: srcmap" (makeSrcMaps (x ^?! key "srcmap" . _String)),- _contractName = s,- _constructorInputs = mkConstructor abis,- _abiMap = mkAbiMap abis,- _eventMap = mkEventMap abis,- _errorMap = mkErrorMap abis,- _storageLayout = mkStorageLayout $ x ^? key "storage-layout",- _immutableReferences = mempty -- TODO: deprecate combined-json+ runtimeCode = theRuntimeCode,+ creationCode = theCreationCode,+ runtimeCodehash = keccak' (stripBytecodeMetadata theRuntimeCode),+ creationCodehash = keccak' (stripBytecodeMetadata theCreationCode),+ runtimeSrcmap = force "internal error: srcmap-runtime" (makeSrcMaps (x ^?! key "srcmap-runtime" . _String)),+ creationSrcmap = force "internal error: srcmap" (makeSrcMaps (x ^?! key "srcmap" . _String)),+ contractName = s,+ constructorInputs = mkConstructor abis,+ abiMap = mkAbiMap abis,+ eventMap = mkEventMap abis,+ errorMap = mkErrorMap abis,+ storageLayout = mkStorageLayout $ x ^? key "storage-layout",+ immutableReferences = mempty -- TODO: deprecate combined-json } readStdJSON :: Text -> Maybe (Map Text SolcContract, Map Text Value, [(Text, Maybe ByteString)]) readStdJSON json = do- contracts <- KeyMap.toHashMapText <$> json ^? key "contracts" ._Object+ contracts <- KeyMap.toHashMapText <$> json ^? key "contracts" . _Object -- TODO: support the general case of "urls" and "content" in the standard json sources <- KeyMap.toHashMapText <$> json ^? key "sources" . _Object let asts = force "JSON lacks abstract syntax trees." . preview (key "ast") <$> sources@@ -408,19 +381,19 @@ abis = force ("abi key not found in " <> show x) $ toList <$> x ^? key "abi" . _Array in (s <> ":" <> c, (SolcContract {- _runtimeCode = theRuntimeCode,- _creationCode = theCreationCode,- _runtimeCodehash = keccak' (stripBytecodeMetadata theRuntimeCode),- _creationCodehash = keccak' (stripBytecodeMetadata theCreationCode),- _runtimeSrcmap = force "internal error: srcmap-runtime" (makeSrcMaps (runtime ^?! key "sourceMap" . _String)),- _creationSrcmap = force "internal error: srcmap" (makeSrcMaps (creation ^?! key "sourceMap" . _String)),- _contractName = s <> ":" <> c,- _constructorInputs = mkConstructor abis,- _abiMap = mkAbiMap abis,- _eventMap = mkEventMap abis,- _errorMap = mkErrorMap abis,- _storageLayout = mkStorageLayout $ x ^? key "storageLayout",- _immutableReferences = fromMaybe mempty $+ runtimeCode = theRuntimeCode,+ creationCode = theCreationCode,+ runtimeCodehash = keccak' (stripBytecodeMetadata theRuntimeCode),+ creationCodehash = keccak' (stripBytecodeMetadata theCreationCode),+ runtimeSrcmap = force "internal error: srcmap-runtime" (makeSrcMaps (runtime ^?! key "sourceMap" . _String)),+ creationSrcmap = force "internal error: srcmap" (makeSrcMaps (creation ^?! key "sourceMap" . _String)),+ contractName = s <> ":" <> c,+ constructorInputs = mkConstructor abis,+ abiMap = mkAbiMap abis,+ eventMap = mkEventMap abis,+ errorMap = mkErrorMap abis,+ storageLayout = mkStorageLayout $ x ^? key "storageLayout",+ immutableReferences = fromMaybe mempty $ do x' <- runtime ^? key "immutableReferences" case fromJSON x' of Success a -> return a@@ -433,13 +406,13 @@ relevant = filter (\y -> "function" == y ^?! key "type" . _String) abis f abi = (abiKeccak (encodeUtf8 (signature abi)),- Method { _methodName = abi ^?! key "name" . _String- , _methodSignature = signature abi- , _methodInputs = map parseMethodInput+ Method { name = abi ^?! key "name" . _String+ , methodSignature = signature abi+ , inputs = map parseMethodInput (toList (abi ^?! key "inputs" . _Array))- , _methodOutput = map parseMethodInput+ , output = map parseMethodInput (toList (abi ^?! key "outputs" . _Array))- , _methodMutability = parseMutability+ , mutability = parseMutability (abi ^?! key "stateMutability" . _String) }) in f <$> relevant@@ -474,7 +447,7 @@ ( stripKeccak $ keccak' (encodeUtf8 (signature abi)) , SolError (abi ^?! key "name" . _String)- (map (\y -> ( force "internal error: type" (parseTypeName' y)))+ (map (force "internal error: type" . parseTypeName') (toList $ abi ^?! key "inputs" . _Array)) ) in f <$> relevant
+ src/EVM/Solvers.hs view
@@ -0,0 +1,239 @@+{-# Language DataKinds #-}+{-# Language GADTs #-}+{-# Language PolyKinds #-}+{-# Language ScopedTypeVariables #-}+{-# Language TypeApplications #-}+{-# Language QuasiQuotes #-}++{- |+ Module: EVM.Solvers+ Description: Solver orchestration+-}+module EVM.Solvers where++import Prelude hiding (LT, GT)++import GHC.Natural+import Control.Monad+import GHC.IO.Handle (Handle, hFlush, hSetBuffering, BufferMode(..))+import Control.Concurrent.Chan (Chan, newChan, writeChan, readChan)+import Control.Concurrent (forkIO, killThread)+import Data.Char (isSpace)++import Data.Maybe (fromMaybe)+import Data.Text.Lazy (Text)+import qualified Data.Text as TS+import qualified Data.Text.Lazy as T+import qualified Data.Text.Lazy.IO as T+import Data.Text.Lazy.Builder+import System.Process (createProcess, cleanupProcess, proc, ProcessHandle, std_in, std_out, std_err, StdStream(..))++import EVM.SMT++-- | Supported solvers+data Solver+ = Z3+ | CVC5+ | Bitwuzla+ | Custom Text++instance Show Solver where+ show Z3 = "z3"+ show CVC5 = "cvc5"+ show Bitwuzla = "bitwuzla"+ show (Custom s) = T.unpack s+++-- | A running solver instance+data SolverInstance = SolverInstance+ { _type :: Solver+ , _stdin :: Handle+ , _stdout :: Handle+ , _stderr :: Handle+ , _process :: ProcessHandle+ }++-- | A channel representing a group of solvers+newtype SolverGroup = SolverGroup (Chan Task)++-- | A script to be executed, a list of models to be extracted in the case of a sat result, and a channel where the result should be written+data Task = Task+ { script :: SMT2+ , resultChan :: Chan CheckSatResult+ }++-- | The result of a call to (check-sat)+data CheckSatResult+ = Sat SMTCex+ | Unsat+ | Unknown+ | Error TS.Text+ deriving (Show, Eq)++isSat :: CheckSatResult -> Bool+isSat (Sat _) = True+isSat _ = False++isErr :: CheckSatResult -> Bool+isErr (Error _) = True+isErr _ = False++isUnsat :: CheckSatResult -> Bool+isUnsat Unsat = True+isUnsat _ = False++checkSat :: SolverGroup -> SMT2 -> IO CheckSatResult+checkSat (SolverGroup taskQueue) script = do+ -- prepare result channel+ resChan <- newChan++ -- send task to solver group+ writeChan taskQueue (Task script resChan)++ -- collect result+ readChan resChan++withSolvers :: Solver -> Natural -> Maybe Natural -> (SolverGroup -> IO a) -> IO a+withSolvers solver count timeout cont = do+ -- spawn solvers+ instances <- mapM (const $ spawnSolver solver timeout) [1..count]++ -- spawn orchestration thread+ taskQueue <- newChan+ availableInstances <- newChan+ forM_ instances (writeChan availableInstances)+ orchestrateId <- forkIO $ orchestrate taskQueue availableInstances++ -- run continuation with task queue+ res <- cont (SolverGroup taskQueue)++ -- cleanup and return results+ mapM_ stopSolver instances+ killThread orchestrateId+ pure res+ where+ orchestrate queue avail = do+ task <- readChan queue+ inst <- readChan avail+ _ <- forkIO $ runTask task inst avail+ orchestrate queue avail++ runTask (Task (SMT2 cmds cexvars) r) inst availableInstances = do+ -- reset solver and send all lines of provided script+ out <- sendScript inst (SMT2 ("(reset)" : cmds) cexvars)+ 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" -> do+ calldatamodels <- getVars parseVar (getValue inst) (fmap T.toStrict cexvars.calldataV)+ buffermodels <- getBufs (getValue inst) (fmap T.toStrict cexvars.buffersV)+ storagemodels <- getStore (getValue inst) cexvars.storeReads+ blockctxmodels <- getVars parseBlockCtx (getValue inst) (fmap T.toStrict cexvars.blockContextV)+ txctxmodels <- getVars parseFrameCtx (getValue inst) (fmap T.toStrict cexvars.txContextV)+ pure $ Sat $ SMTCex+ { vars = calldatamodels+ , buffers = buffermodels+ , store = storagemodels+ , blockContext = blockctxmodels+ , txContext = txctxmodels+ }+ "unsat" -> pure Unsat+ "timeout" -> pure Unknown+ "unknown" -> pure Unknown+ _ -> pure . Error $ T.toStrict $ "Unable to parse solver output: " <> sat+ writeChan r res++ -- put the instance back in the list of available instances+ writeChan availableInstances inst++-- | Arguments used when spawing a solver instance+solverArgs :: Solver -> Maybe (Natural) -> [Text]+solverArgs solver timeout = case solver of+ Bitwuzla -> error "TODO: Bitwuzla args"+ Z3 ->+ [ "-in" ]+ CVC5 ->+ [ "--lang=smt"+ , "--no-interactive"+ , "--produce-models"+ , "--tlimit-per=" <> T.pack (show (1000 * fromMaybe 10 timeout))+ ]+ Custom _ -> []++-- | Spawns a solver instance, and sets the various global config options that we use for our queries+spawnSolver :: Solver -> Maybe (Natural) -> IO SolverInstance+spawnSolver solver timeout = do+ let cmd = (proc (show solver) (fmap T.unpack $ solverArgs solver timeout)) { std_in = CreatePipe, std_out = CreatePipe, std_err = CreatePipe }+ (Just stdin, Just stdout, Just stderr, process) <- createProcess cmd+ hSetBuffering stdin (BlockBuffering (Just 1000000))+ let solverInstance = SolverInstance solver stdin stdout stderr process+ case timeout of+ Nothing -> pure solverInstance+ Just t -> case solver of+ CVC5 -> pure solverInstance+ _ -> do+ _ <- sendLine' solverInstance $ "(set-option :timeout " <> T.pack (show t) <> ")"+ pure solverInstance++-- | Cleanly shutdown a running solver instnace+stopSolver :: SolverInstance -> IO ()+stopSolver (SolverInstance _ stdin stdout stderr process) = cleanupProcess (Just stdin, Just stdout, Just stderr, process)++-- | 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 (T.unlines $ fmap toLazyText cmds)+ pure $ Right()++-- | 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+ -- trim leading whitespace+ let cmd' = T.dropWhile isSpace cmd+ case T.unpack cmd' of+ "" -> pure "success" -- ignore blank lines+ ';' : _ -> pure "success" -- ignore comments+ _ -> sendLine inst cmd'++-- | Sends a string to the solver and appends a newline, returns the first available line from the output buffer+sendLine :: SolverInstance -> Text -> IO Text+sendLine (SolverInstance _ stdin stdout _ _) cmd = do+ T.hPutStr stdin (T.append cmd "\n")+ hFlush stdin+ T.hGetLine stdout++-- | Sends a string to the solver and appends a newline, doesn't return stdout+sendLine' :: SolverInstance -> Text -> IO ()+sendLine' (SolverInstance _ stdin _ _ _) cmd = do+ T.hPutStr stdin (T.append cmd "\n")+ hFlush stdin++-- | Returns a string representation of the model for the requested variable+getValue :: SolverInstance -> Text -> IO Text+getValue (SolverInstance _ stdin stdout _ _) var = do+ T.hPutStr stdin (T.append (T.append "(get-value (" var) "))\n")+ hFlush stdin+ fmap (T.unlines . reverse) (readSExpr stdout)++-- | Reads lines from h until we have a balanced sexpr+readSExpr :: Handle -> IO [Text]+readSExpr h = go 0 0 []+ where+ go 0 0 _ = do+ line <- T.hGetLine h+ let ls = T.length $ T.filter (== '(') line+ rs = T.length $ T.filter (== ')') line+ if ls == rs+ then pure [line]+ else go ls rs [line]+ go ls rs prev = do+ line <- T.hGetLine h+ let ls' = T.length $ T.filter (== '(') line+ rs' = T.length $ T.filter (== ')') line+ if (ls + ls') == (rs + rs')+ then pure $ line : prev+ else go (ls + ls') (rs + rs') (line : prev)
src/EVM/Stepper.hs view
@@ -99,7 +99,7 @@ runFully :: Stepper EVM.VM runFully = do vm <- run- case EVM._result vm of+ case vm._result of Nothing -> error "should not occur" Just (VMFailure (Query q)) -> wait q >> runFully@@ -133,9 +133,9 @@ eval (action :>>= k) = case action of Exec ->- EVM.Exec.exec >>= interpret fetcher . k+ (State.state . runState) EVM.Exec.exec >>= interpret fetcher . k Run ->- EVM.Exec.run >>= interpret fetcher . k+ (State.state . runState) EVM.Exec.run >>= interpret fetcher . k Wait q -> do m <- liftIO (fetcher q) State.state (runState m) >> interpret fetcher (k ())
src/EVM/StorageLayout.hs view
@@ -2,22 +2,19 @@ -- Figures out the layout of storage slots for Solidity contracts. -import EVM.Dapp (DappInfo, dappAstSrcMap, dappAstIdMap)+import EVM.Dapp (DappInfo(..)) import EVM.Solidity (SolcContract, creationSrcmap, SlotType(..)) import EVM.ABI (AbiType (..), parseTypeName) +import Control.Lens import Data.Aeson (Value (..)) import Data.Aeson.Lens--import Control.Lens--import Data.Text (Text, unpack, pack, words)- import Data.Foldable (toList)+import Data.List.NonEmpty qualified as NonEmpty+import Data.Map qualified as Map import Data.Maybe (fromMaybe, isJust)-import qualified Data.List.NonEmpty as NonEmpty--import qualified Data.Sequence as Seq+import Data.Sequence qualified as Seq+import Data.Text (Text, unpack, pack, words) import Prelude hiding (words) @@ -32,9 +29,9 @@ 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 (view creationSrcmap solc) of+ case Seq.viewl solc.creationSrcmap of firstSrcMap Seq.:< _ ->- (view dappAstSrcMap dapp) firstSrcMap+ dapp.astSrcMap firstSrcMap _ -> Nothing @@ -57,7 +54,7 @@ (\case Number i -> fromMaybe (error "malformed AST JSON") $ storageVariablesForContract =<<- preview (dappAstIdMap . ix (floor i)) dapp+ Map.lookup (floor i) dapp.astIdMap _ -> error "malformed AST JSON")
src/EVM/SymExec.hs view
@@ -1,16 +1,19 @@+{-# Language TupleSections #-} {-# Language DataKinds #-} module EVM.SymExec where import Prelude hiding (Word) +import Data.Tuple (swap) import Control.Lens hiding (pre)-import EVM hiding (Query, Revert, push)+import EVM hiding (Query, Revert, push, bytecode, cache) import qualified EVM import EVM.Exec import qualified EVM.Fetch as Fetch import EVM.ABI import EVM.SMT+import EVM.Solvers import EVM.Traversals import qualified EVM.Expr as Expr import EVM.Stepper (Stepper)@@ -23,11 +26,9 @@ import Data.DoubleWord (Word256) import Control.Concurrent.Async import Data.Maybe-import Data.List (foldl')-import Data.Tuple (swap)+import Data.List (foldl', sortBy) import Data.ByteString (ByteString)-import Data.List (find)-import Data.ByteString (null, pack)+import qualified Data.ByteString as BS import qualified Control.Monad.State.Class as State import Data.Bifunctor (first, second) import Data.Text (Text)@@ -37,20 +38,31 @@ import qualified Data.Text.IO as T import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy.IO as TL-import EVM.Format (formatExpr, indent, formatBinary)+import EVM.Format (formatExpr)+import Data.Set (Set, isSubsetOf, size)+import qualified Data.Set as Set+import Control.Concurrent.STM (atomically, TVar, readTVarIO, readTVar, newTVarIO, writeTVar)+import Control.Concurrent.Spawn+import GHC.Conc (getNumProcessors)+import EVM.Format (indent, formatBinary) data ProofResult a b c = Qed a | Cex b | Timeout c deriving (Show, Eq) type VerifyResult = ProofResult () (Expr End, SMTCex) (Expr End)-type EquivalenceResult = ProofResult ([VM], [VM]) VM ()+type EquivResult = ProofResult () (SMTCex) () +isTimeout :: ProofResult a b c -> Bool+isTimeout (Timeout _) = True+isTimeout _ = False++isCex :: ProofResult a b c -> Bool+isCex (Cex _) = True+isCex _ = False+ isQed :: ProofResult a b c -> Bool isQed (Qed _) = True isQed _ = False -containsA :: Eq a => Eq b => Eq c => ProofResult a b c -> [(d , e, ProofResult a b c)] -> Bool-containsA a lst = isJust $ Data.List.find (\(_, _, c) -> c == a) lst- data VeriOpts = VeriOpts { simp :: Bool , debug :: Bool@@ -165,7 +177,7 @@ abstractVM typesignature concreteArgs contractCode maybepre storagemodel = finalVm where (calldata', calldataProps) = case typesignature of- Nothing -> (AbstractBuf "txdata", [])+ Nothing -> (AbstractBuf "txdata", [Expr.bufLength (AbstractBuf "txdata") .< (Lit (2 ^ (64 :: Integer)))]) Just (name, typs) -> symCalldata name typs concreteArgs (AbstractBuf "txdata") store = case storagemodel of SymbolicS -> AbstractStore@@ -232,9 +244,9 @@ eval (action Operational.:>>= k) = case action of Stepper.Exec ->- exec >>= interpret fetcher maxIter askSmtIters . k+ (State.state . runState) exec >>= interpret fetcher maxIter askSmtIters . k Stepper.Run ->- run >>= interpret fetcher maxIter askSmtIters . k+ (State.state . runState) run >>= interpret fetcher maxIter askSmtIters . k Stepper.IOAct q -> mapStateT liftIO q >>= interpret fetcher maxIter askSmtIters . k Stepper.Ask (EVM.PleaseChoosePath cond continue) -> do@@ -277,9 +289,9 @@ maxIterationsReached _ Nothing = Nothing maxIterationsReached vm (Just maxIter) = let codelocation = getCodeLocation vm- iters = view (iterations . at codelocation . non 0) vm+ iters = view (at codelocation . non 0) vm._iterations in if num maxIter <= iters- then view (cache . path . at (codelocation, iters - 1)) vm+ then Map.lookup (codelocation, iters - 1) vm._cache._path else Nothing @@ -333,7 +345,7 @@ pruneDeadPaths :: [VM] -> [VM] pruneDeadPaths =- filter $ \vm -> case view result vm of+ filter $ \vm -> case vm._result of Just (VMFailure DeadPath) -> False _ -> True @@ -341,10 +353,10 @@ runExpr :: Stepper.Stepper (Expr End) runExpr = do vm <- Stepper.runFully- let asserts = view keccakEqs vm- pure $ case view result vm of+ let asserts = vm._keccakEqs+ pure $ case vm._result of Nothing -> error "Internal Error: vm in intermediate state after call to runFully"- Just (VMSuccess buf) -> Return asserts buf (view (env . EVM.storage) vm)+ Just (VMSuccess buf) -> Return asserts buf vm._env._storage Just (VMFailure e) -> case e of UnrecognizedOpcode _ -> Failure asserts Invalid SelfDestruction -> Failure asserts SelfDestruct@@ -353,6 +365,7 @@ EVM.Revert buf -> EVM.Types.Revert asserts buf EVM.InvalidMemoryAccess -> Failure asserts EVM.Types.InvalidMemoryAccess EVM.BadJumpDestination -> Failure asserts EVM.Types.BadJumpDestination+ EVM.StackUnderrun -> Failure asserts EVM.Types.StackUnderrun e' -> Failure asserts $ EVM.Types.TmpErr (show e') -- | Converts a given top level expr into a list of final states and the associated path conditions for each state@@ -457,12 +470,12 @@ verify solvers opts preState maybepost = do putStrLn "Exploring contract" - exprInter <- evalStateT (interpret (Fetch.oracle solvers (rpcInfo opts)) (maxIter opts) (askSmtIters opts) runExpr) preState- when (debug opts) $ T.writeFile "unsimplified.expr" (formatExpr exprInter)+ exprInter <- evalStateT (interpret (Fetch.oracle solvers opts.rpcInfo) opts.maxIter opts.askSmtIters runExpr) preState+ when opts.debug $ T.writeFile "unsimplified.expr" (formatExpr exprInter) putStrLn "Simplifying expression"- expr <- if (simp opts) then (pure $ Expr.simplify exprInter) else pure exprInter- when (debug opts) $ T.writeFile "simplified.expr" (formatExpr expr)+ expr <- if opts.simp then (pure $ Expr.simplify exprInter) else pure exprInter+ when opts.debug $ T.writeFile "simplified.expr" (formatExpr expr) putStrLn $ "Explored contract (" <> show (Expr.numBranches expr) <> " branches)" @@ -475,11 +488,11 @@ \(_, leaf) -> case evalProp (post preState leaf) of PBool True -> False _ -> True- assumes = view constraints preState+ assumes = preState._constraints withQueries = fmap (\(pcs, leaf) -> (assertProps (PNeg (post preState leaf) : assumes <> extractProps leaf <> pcs), leaf)) canViolate putStrLn $ "Checking for reachability of " <> show (length withQueries) <> " potential property violation(s)" - when (debug opts) $ forM_ (zip [(1 :: Int)..] withQueries) $ \(idx, (q, leaf)) -> do+ when opts.debug $ forM_ (zip [(1 :: Int)..] withQueries) $ \(idx, (q, leaf)) -> do TL.writeFile ("query-" <> show idx <> ".smt2") ("; " <> (TL.pack $ show leaf) <> "\n\n" <> formatSMT2 q <> "\n\n(check-sat)")@@ -494,75 +507,138 @@ toVRes :: (CheckSatResult, Expr End) -> VerifyResult toVRes (res, leaf) = case res of Sat model -> Cex (leaf, model)- EVM.SMT.Unknown -> Timeout leaf+ EVM.Solvers.Unknown -> Timeout leaf Unsat -> Qed () Error e -> error $ "Internal Error: solver responded with error: " <> show e +type UnsatCache = TVar [Set Prop]+ -- | Compares two contract runtimes for trace equivalence by running two VMs and comparing the end states.-equivalenceCheck :: SolverGroup -> ByteString -> ByteString -> VeriOpts -> Maybe (Text, [AbiType]) -> IO [(Maybe SMTCex, Prop, ProofResult () () ())]+--+-- We do this by asking the solver to find a common input for each pair of endstates that satisfies the path+-- conditions for both sides and produces a differing output. If we can find such an input, then we have a clear+-- equivalence break, and since we run this check for every pair of end states, the check is exhaustive.+equivalenceCheck :: SolverGroup -> ByteString -> ByteString -> VeriOpts -> Maybe (Text, [AbiType]) -> IO [EquivResult] equivalenceCheck solvers bytecodeA bytecodeB opts signature' = do- let- bytecodeA' = if Data.ByteString.null bytecodeA then Data.ByteString.pack [0] else bytecodeA- bytecodeB' = if Data.ByteString.null bytecodeB then Data.ByteString.pack [0] else bytecodeB- preStateA = abstractVM signature' [] bytecodeA' Nothing SymbolicS- preStateB = abstractVM signature' [] bytecodeB' Nothing SymbolicS+ case bytecodeA == bytecodeB of+ True -> do+ putStrLn "bytecodeA and bytecodeB are identical"+ pure [Qed ()]+ False -> do+ branchesA <- getBranches bytecodeA+ branchesB <- getBranches bytecodeB+ let allPairs = [(a,b) | a <- branchesA, b <- branchesB]+ putStrLn $ "Found " <> (show $ length allPairs) <> " total pairs of endstates" - aExpr <- evalStateT (interpret (Fetch.oracle solvers Nothing) (maxIter opts) (askSmtIters opts) runExpr) preStateA- bExpr <- evalStateT (interpret (Fetch.oracle solvers Nothing) (maxIter opts) (askSmtIters opts) runExpr) preStateB- aExprSimp <- if (simp opts) then (pure $ Expr.simplify aExpr) else pure aExpr- bExprSimp <- if (simp opts) then (pure $ Expr.simplify bExpr) else pure bExpr- when (debug opts) $ putStrLn $ "num of aExprSimp endstates:" <> (show $ length $ flattenExpr aExprSimp) <> "\nnum of bExprSimp endstates:" <> (show $ length $ flattenExpr bExprSimp)+ when opts.debug $ putStrLn+ $ "endstates in bytecodeA: " <> (show $ length branchesA)+ <> "\nendstates in bytecodeB: " <> (show $ length branchesB) - -- Check each pair of end states for equality:- let- differingEndStates = uncurry distinct <$> [(a,b) | a <- flattenExpr aExprSimp, b <- flattenExpr bExprSimp]- distinct :: ([Prop], Expr End) -> ([Prop], Expr End) -> Prop- distinct (aProps, aEnd) (bProps, bEnd) =- let- differingResults = case (aEnd, bEnd) of- (Return _ aOut aStore, Return _ bOut bStore) ->- if aOut == bOut && aStore == bStore then PBool False- else aStore ./= bStore .|| aOut ./= bOut- (Return {}, _) -> PBool True- (_, Return {}) -> PBool True- (Revert _ a, Revert _ b) -> if a==b then PBool False else a ./= b- (Revert _ _, _) -> PBool True- (_, Revert _ _) -> PBool True- (Failure _ erra, Failure _ errb) -> if erra==errb then PBool False else PBool True- (GVar _, _) -> error "Expressions cannot contain global vars"- (_ , GVar _) -> error "Expressions cannot contain global vars"- (Failure _ (TmpErr s), _) -> error $ "Unhandled error: " <> s- (_, Failure _ (TmpErr s)) -> error $ "Unhandled error: " <> s- (ITE _ _ _, _ ) -> error "Expressions must be flattened"- (_, ITE _ _ _) -> error "Expressions must be flattened"+ let differingEndStates = sortBySize (mapMaybe (uncurry distinct) allPairs)+ putStrLn $ "Asking the SMT solver for " <> (show $ length differingEndStates) <> " pairs"+ when opts.debug $ forM_ (zip differingEndStates [(1::Integer)..]) (\(x, i) ->+ T.writeFile ("prop-checked-" <> show i) (T.pack $ show x)) - -- if the SMT solver can find a common input that satisfies BOTH sets of path conditions- -- AND the output differs, then we are in trouble. We do this for _every_ pair of paths, which- -- makes this exhaustive- in- if differingResults == PBool False- then PBool False- else (foldl PAnd (PBool True) aProps) .&& (foldl PAnd (PBool True) bProps) .&& differingResults- -- If there exists a pair of end states where this is not the case,- -- the following constraint is satisfiable+ knownUnsat <- newTVarIO []+ procs <- getNumProcessors+ results <- checkAll differingEndStates knownUnsat procs - let diffEndStFilt = filter (/= PBool False) differingEndStates- putStrLn $ "Equivalence checking " <> (show $ length diffEndStFilt) <> " combinations"- when (debug opts) $ forM_ (zip diffEndStFilt [1 :: Integer ..]) (\(x, i) -> T.writeFile ("prop-checked-" <> show i) (T.pack $ show x))- results <- flip mapConcurrently (zip diffEndStFilt [1 :: Integer ..]) $ \(prop, i) -> do- let assertedProps = assertProps [prop]- let filename = "eq-check-" <> show i <> ".smt2"- when (debug opts) $ T.writeFile (filename) $ (TL.toStrict $ formatSMT2 assertedProps) <> "\n(check-sat)"- res <- case prop of- PBool False -> pure Unsat- _ -> checkSat solvers assertedProps- case res of- Sat a -> return (Just a, prop, Cex ())- Unsat -> return (Nothing, prop, Qed ())- EVM.SMT.Unknown -> return (Nothing, prop, Timeout ())- Error txt -> error $ "Error while running solver: `" <> T.unpack txt <> "` SMT file was: `" <> filename <> "`"- return $ filter (\(_, _, res) -> res /= Qed ()) results+ let useful = foldr (\(_, b) n -> if b then n+1 else n) (0::Integer) results+ putStrLn $ "Reuse of previous queries was Useful in " <> (show useful) <> " cases"+ case all isQed . fmap fst $ results of+ True -> pure [Qed ()]+ False -> pure $ filter (/= Qed ()) . fmap fst $ results+ where+ -- we order the sets by size because this gives us more cache hits when+ -- running our queries later on (since we rely on a subset check)+ sortBySize :: [Set a] -> [Set a]+ sortBySize = sortBy (\a b -> if size a > size b then Prelude.LT else Prelude.GT) + -- returns True if a is a subset of any of the sets in b+ subsetAny :: Set Prop -> [Set Prop] -> Bool+ subsetAny a b = foldr (\bp acc -> acc || isSubsetOf a bp) False b++ -- decompiles the given bytecode into a list of branches+ getBranches :: ByteString -> IO [([Prop], Expr End)]+ getBranches bs = do+ let+ bytecode = if BS.null bs then BS.pack [0] else bs+ prestate = abstractVM signature' [] bytecode Nothing SymbolicS+ expr <- evalStateT (interpret (Fetch.oracle solvers Nothing) opts.maxIter opts.askSmtIters runExpr) prestate+ let simpl = if opts.simp then (Expr.simplify expr) else expr+ pure $ flattenExpr simpl++ -- checks for satisfiability of all the props in the provided set. skips+ -- the solver if we can determine unsatisfiability from the cache already+ -- the last element of the returned tuple indicates whether the cache was+ -- used or not+ check :: UnsatCache -> Set Prop -> IO (EquivResult, Bool)+ check knownUnsat props = do+ let smt = assertProps $ Set.toList props+ ku <- readTVarIO knownUnsat+ res <- if subsetAny props ku+ then pure (True, Unsat)+ else (fmap ((False),) (checkSat solvers smt))+ case res of+ (_, Sat x) -> pure (Cex x, False)+ (quick, Unsat) -> case quick of+ True -> pure (Qed (), quick)+ False -> do+ -- nb: we might end up with duplicates here due to a+ -- potential race, but it doesn't matter for correctness+ atomically $ readTVar knownUnsat >>= writeTVar knownUnsat . (props :)+ pure (Qed (), False)+ (_, EVM.Solvers.Unknown) -> pure (Timeout (), False)+ (_, Error txt) -> error $ "Error while running solver: `" <> T.unpack txt -- <> "` SMT file was: `" <> filename <> "`"++ -- 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+ -- mapConcurrently which would spawn as many threads as there are jobs, and+ -- run them in a random order. We ordered them correctly, though so that'd be bad+ checkAll :: [(Set Prop)] -> UnsatCache -> Int -> IO [(EquivResult, Bool)]+ checkAll input cache numproc = do+ wrap <- pool numproc+ parMapIO (wrap . (check cache)) input++ -- Takes two branches and returns a set of props that will need to be+ -- satisfied for the two branches to violate the equivalence check. i.e.+ -- for a given pair of branches, equivalence is violated if there exists an+ -- input that satisfies the branch conditions from both sides and produces+ -- a differing result in each branch+ distinct :: ([Prop], Expr End) -> ([Prop], Expr End) -> Maybe (Set Prop)+ distinct (aProps, aEnd) (bProps, bEnd) =+ let+ differingResults = case (aEnd, bEnd) of+ (Return _ aOut aStore, Return _ bOut bStore) ->+ if aOut == bOut && aStore == bStore+ then PBool False+ else aStore ./= bStore .|| aOut ./= bOut+ (Return {}, _) -> PBool True+ (_, Return {}) -> PBool True+ (Revert _ a, Revert _ b) -> if a == b then PBool False else a ./= b+ (Revert _ _, _) -> PBool True+ (_, Revert _ _) -> PBool True+ (Failure _ (TmpErr s), _) -> error $ "Unhandled error: " <> s+ (_, Failure _ (TmpErr s)) -> error $ "Unhandled error: " <> s+ (Failure _ erra, Failure _ errb) -> if erra==errb then PBool False else PBool True+ (ITE _ _ _, _) -> error "Expressions must be flattened"+ (_, ITE _ _ _) -> error "Expressions must be flattened"+ (a, b) -> if a == b+ then PBool False+ else error $ "Internal Error: Unimplemented. Left: " <> show a <> " Right: " <> show b+ in case differingResults of+ -- if the end states are the same, then they can never produce a+ -- different result under any circumstances+ PBool False -> Nothing+ -- if we can statically determine that the end states differ, then we+ -- ask the solver to find us inputs that satisfy both sets of branch+ -- conditions+ PBool True -> Just . Set.fromList $ aProps <> bProps+ -- 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 : aProps <> bProps+ both' :: (a -> b) -> (a, a) -> (b, b) both' f (x, y) = (f x, f y) @@ -580,7 +656,7 @@ case res of Unsat -> pure () -- ignore unreachable branches Error e -> error $ "Internal error: smt solver returned an error: " <> show e- EVM.SMT.Unknown -> do+ EVM.Solvers.Unknown -> do putStrLn "--- Branch ---" putStrLn "" putStrLn "Unable to produce a model for the following end state:"@@ -601,11 +677,12 @@ formatCex :: Expr Buf -> SMTCex -> Text-formatCex cd m@(SMTCex _ _ blockContext txContext) = T.unlines $+formatCex cd m@(SMTCex _ _ store blockContext txContext) = T.unlines $ [ "Calldata:" , indent 2 cd' , "" ]+ <> storeCex <> txCtx <> blockCtx where@@ -618,6 +695,15 @@ -- payable functions). cd' = prettyBuf $ Expr.simplify $ subModel m cd + storeCex :: [Text]+ storeCex+ | Map.null store = []+ | otherwise =+ [ "Storage:"+ , indent 2 $ T.unlines $ Map.foldrWithKey (\key val acc -> ("Addr " <> (T.pack $ show key) <> ": " <> (T.pack $ show (Map.toList val))) : acc) mempty store+ , ""+ ]+ txCtx :: [Text] txCtx | Map.null txContext = []@@ -663,7 +749,7 @@ -- | Takes a buffer 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 (buffers c) . subVars (vars c) . subVars (blockContext c) . subVars (txContext c) $ expr+subModel c expr = subBufs c.buffers . subVars c.vars . subStore c.store . subVars c.blockContext . subVars c.txContext $ expr where subVars model b = Map.foldlWithKey subVar b model subVar :: Expr a -> Expr EWord -> W256 -> Expr a@@ -685,4 +771,12 @@ a@(AbstractBuf _) -> if a == var then ConcreteBuf val else a+ e -> e++ subStore :: Map W256 (Map W256 W256) -> Expr a -> Expr a+ subStore m b = mapExpr go b+ where+ go :: Expr a -> Expr a+ go = \case+ AbstractStore -> ConcreteStore m e -> e
src/EVM/TTY.hs view
@@ -1,6 +1,7 @@ {-# Language TemplateHaskell #-} {-# Language ImplicitParams #-} {-# Language DataKinds #-}+ module EVM.TTY where import Prelude hiding (lookup, Word)@@ -14,52 +15,44 @@ import EVM.ABI (abiTypeSolidity, decodeAbiValue, AbiType(..), emptyAbi) import EVM.SymExec (maxIterationsReached, symCalldata) import EVM.Expr (simplify)-import EVM.Dapp (DappInfo, dappInfo, Test, extractSig, Test(..), srcMap)-import EVM.Dapp (dappUnitTests, unitTestMethods, dappSolcByName, dappSolcByHash, dappSources)-import EVM.Dapp (dappAstSrcMap)+import EVM.Dapp (DappInfo(..), dappInfo, Test, extractSig, Test(..), srcMap, unitTestMethods) import EVM.Debug-import EVM.Format (showWordExact, showWordExplanation)-import EVM.Format (contractNamePart, contractPathPart, showTraceTree, prettyIfConcreteWord, formatExpr)+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.SMT (SolverGroup)+import EVM.Solvers (SolverGroup) import EVM.Op import EVM.Solidity hiding (storageLayout) import EVM.Types hiding (padRight) import EVM.UnitTest-import EVM.StorageLayout-import Text.Wrap- import EVM.Stepper (Stepper)-import qualified EVM.Stepper as Stepper-import qualified EVM.Fetch as Fetch-import qualified Control.Monad.Operational as Operational--import EVM.Fetch (Fetcher)+import EVM.Stepper qualified as Stepper+import EVM.StorageLayout+import EVM.TTYCenteredList qualified as Centered import Control.Lens hiding (List)+import Control.Monad.Operational qualified as Operational import Control.Monad.State.Strict hiding (state)- import Data.Aeson.Lens 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.List (sort, find)+import Data.Vector qualified as Vec+import Data.Vector.Storable qualified as SVec import Data.Version (showVersion)--import qualified Data.ByteString as BS-import qualified Data.Text as T-import qualified Data.Map as Map-import qualified Data.Text as Text-import qualified Data.Vector as Vec-import qualified Data.Vector.Storable as SVec-import qualified Graphics.Vty as V-import qualified System.Console.Haskeline as Readline--import qualified EVM.TTYCenteredList as Centered--import qualified Paths_hevm as Paths+import Graphics.Vty qualified as V+import System.Console.Haskeline qualified as Readline+import Paths_hevm qualified as Paths+import Text.Wrap data Name = AbiPane@@ -222,7 +215,7 @@ isUnitTestContract :: Text -> DappInfo -> Bool isUnitTestContract name dapp =- elem name (map fst (view dappUnitTests dapp))+ elem name (map fst dapp.unitTests) mkVty :: IO V.Vty mkVty = do@@ -258,7 +251,7 @@ v <- mkVty ui2 <- customMain v mkVty Nothing (app opts) (ViewVm ui0) case ui2 of- ViewVm ui -> return (view uiVm ui)+ ViewVm ui -> return ui._uiVm _ -> error "internal error: customMain returned prematurely" @@ -303,7 +296,7 @@ (Vec.fromList (concatMap (debuggableTests opts)- (view dappUnitTests dapp)))+ dapp.unitTests)) 1 , _testPickerDapp = dapp , _testOpts = opts@@ -325,7 +318,7 @@ (Continue steps, ui') -> put (ViewVm (ui' & set uiStepper steps)) where- m = interpret mode (view uiStepper ui)+ m = interpret mode ui._uiStepper nxt = runStateT m ui backstepUntil@@ -334,21 +327,21 @@ => (UiVmState -> Pred VM) -> EventM n UiState () backstepUntil p = get >>= \case ViewVm s ->- case view uiStep s of+ case s._uiStep 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) (view uiSnapshots s1)+ snapshots' = Data.Map.filter (p s1 . fst) s1._uiSnapshots case lookupLT n snapshots' of -- If no such vm exists, go to the beginning Nothing -> let- (step', (vm', stepper')) = fromJust $ lookupLT (n - 1) (view uiSnapshots s)+ (step', (vm', stepper')) = fromJust $ lookupLT (n - 1) s._uiSnapshots s2 = s1 & set uiVm vm'- & set (uiVm . cache) (view (uiVm . cache) s1)+ & set (uiVm . cache) s1._uiVm._cache & set uiStep step' & set uiStepper stepper' in takeStep s2 (Step 0)@@ -357,7 +350,7 @@ let s2 = s1 & set uiVm vm'- & set (uiVm . cache) (view (uiVm . cache) s1)+ & set (uiVm . cache) s1._uiVm._cache & set uiStep step' & set uiStepper stepper' in takeStep s2 (StepUntil (not . p s1))@@ -368,7 +361,7 @@ ,?maxIter :: Maybe Integer) => UiVmState -> IO UiVmState backstep s =- case view uiStep s of+ case s._uiStep of -- We're already at the first step; ignore command. 0 -> pure s -- To step backwards, we revert to the previous snapshot@@ -378,10 +371,10 @@ -- any blocking queries, and also the memory view. n -> let- (step, (vm, stepper)) = fromJust $ lookupLT n (view uiSnapshots s)+ (step, (vm, stepper)) = fromJust $ lookupLT n s._uiSnapshots s1 = s & set uiVm vm- & set (uiVm . cache) (view (uiVm . cache) s)+ & set (uiVm . cache) s._uiVm._cache & set uiStep step & set uiStepper stepper stepsToTake = n - step - 1@@ -419,14 +412,14 @@ appEvent (VtyEvent (V.EvKey V.KEsc [])) = get >>= \case ViewVm s -> do let opts = s ^. uiTestOpts- dapp' = dapp opts- tests = concatMap (debuggableTests opts) (dapp' ^. dappUnitTests)+ dapp = opts.dapp+ tests = concatMap (debuggableTests opts) dapp.unitTests case tests of [] -> halt ts -> put $ ViewPicker $ UiTestPickerState { _testPickerList = list TestPickerPane (Vec.fromList ts) 1- , _testPickerDapp = dapp'+ , _testPickerDapp = dapp , _testOpts = opts } ViewHelp s -> put (ViewVm s)@@ -441,15 +434,15 @@ { _browserContractList = list BrowserPane- (Vec.fromList (Map.toList (view (uiVm . env . contracts) s)))+ (Vec.fromList (Map.toList s._uiVm._env._contracts)) 2 , _browserVm = s } ViewPicker s ->- case listSelectedElement (view testPickerList s) of+ case listSelectedElement s._testPickerList of Nothing -> error "nothing selected" Just (_, x) -> do- let initVm = initialUiVmStateForTest (view testOpts s) x+ let initVm = initialUiVmStateForTest s._testOpts x put (ViewVm initVm) _ -> pure () @@ -514,10 +507,10 @@ -- We keep the current cache so we don't have to redo -- any blocking queries. let- (vm, stepper) = fromJust (Map.lookup 0 (view uiSnapshots s))+ (vm, stepper) = fromJust (Map.lookup 0 s._uiSnapshots) s' = s & set uiVm vm- & set (uiVm . cache) (view (uiVm . cache) s)+ & set (uiVm . cache) s._uiVm._cache & set uiStep 0 & set uiStepper stepper @@ -527,7 +520,7 @@ -- Vm Overview: p - backstep appEvent (VtyEvent (V.EvKey (V.KChar 'p') [])) = get >>= \case ViewVm s ->- case view uiStep s of+ case s._uiStep of 0 -> -- We're already at the first step; ignore command. pure ()@@ -538,10 +531,10 @@ -- 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 (view uiSnapshots s)+ (step, (vm, stepper)) = fromJust $ lookupLT n s._uiSnapshots s1 = s & set uiVm vm -- set the vm to the one from the snapshot- & set (uiVm . cache) (view (uiVm . cache) s) -- persist the cache+ & set (uiVm . cache) s._uiVm._cache -- persist the cache & set uiStep step & set uiStepper stepper stepsToTake = n - step - 1@@ -562,7 +555,7 @@ ViewVm s -> case view (uiVm . result) s of Just (VMFailure (Choose (PleaseChoosePath _ contin))) ->- takeStep (s & set uiStepper (Stepper.evm (contin True) >> (view uiStepper s)))+ takeStep (s & set uiStepper (Stepper.evm (contin True) >> s._uiStepper)) (Step 1) _ -> pure () _ -> pure ()@@ -570,9 +563,9 @@ -- Vm Overview: 1 - choose jump appEvent (VtyEvent (V.EvKey (V.KChar '1') [])) = get >>= \case ViewVm s ->- case view (uiVm . result) s of+ case s._uiVm._result of Just (VMFailure (Choose (PleaseChoosePath _ contin))) ->- takeStep (s & set uiStepper (Stepper.evm (contin False) >> (view uiStepper s)))+ takeStep (s & set uiStepper (Stepper.evm (contin False) >> s._uiStepper)) (Step 1) _ -> pure () _ -> pure ()@@ -616,7 +609,7 @@ SymbolicTest _ -> symCalldata theTestName types [] (AbstractBuf "txdata") _ -> (error "unreachable", error "unreachable") (test, types) = fromJust $ find (\(test',_) -> extractSig test' == theTestName) $ unitTestMethods testContract- testContract = fromJust $ view (dappSolcByName . at theContractName) dapp+ testContract = fromJust $ Map.lookup theContractName dapp.solcByName vm0 = initialUnitTestVm opts testContract script = do@@ -697,7 +690,7 @@ withHighlight selected $ txt " Debug " <+> txt (contractNamePart x) <+> txt "::" <+> txt y) True- (view testPickerList ui)+ ui._testPickerList ] drawVmBrowser :: UiBrowserState -> [UiWidget]@@ -708,48 +701,47 @@ renderList (\selected (k, c') -> withHighlight selected . txt . mconcat $- [ fromMaybe "<unknown contract>" . flip preview dapp' $- ( dappSolcByHash . ix (maybeHash c')- . _2 . contractName )+ [ fromMaybe "<unknown contract>" $+ Map.lookup (maybeHash c') dapp.solcByHash <&> (.contractName) . snd , "\n" , " ", pack (show k) ]) True- (view browserContractList ui)- , case flip preview dapp' (dappSolcByHash . ix (maybeHash c) . _2) of+ ui._browserContractList+ , case snd <$> Map.lookup (maybeHash c) dapp.solcByHash of Nothing -> hBox [ borderWithLabel (txt "Contract information") . padBottom Max . padRight Max $ vBox- [ txt ("Codehash: " <> pack (show (view codehash c)))- , txt ("Nonce: " <> showWordExact (view nonce c))- , txt ("Balance: " <> showWordExact (view balance c))+ [ 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 (view contractName sol))- , txt "File: " <+> txt (contractPathPart (view contractName sol))+ [ txt "Name: " <+> txt (contractNamePart sol.contractName)+ , txt "File: " <+> txt (contractPathPart sol.contractName) , txt " " , txt "Constructor inputs:"- , vBox . flip map (view constructorInputs sol) $+ , vBox . flip map sol.constructorInputs $ \(name, abiType) -> txt (" " <> name <> ": " <> abiTypeSolidity abiType) , txt "Public methods:"- , vBox . flip map (sort (Map.elems (view abiMap sol))) $- \method -> txt (" " <> view methodSignature method)+ , 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))+ (map txt (storageLayout dapp sol)) ] ] ] where- dapp' = dapp (view (browserVm . uiTestOpts) ui)- (_, (_, c)) = fromJust $ listSelectedElement (view browserContractList ui)+ dapp = ui._browserVm._uiTestOpts.dapp+ (_, (_, c)) = fromJust $ listSelectedElement ui._browserContractList -- currentContract = view (dappSolcByHash . ix ) dapp- maybeHash ch = fromJust (error "Internal error: cannot find concrete codehash for partially symbolic code") (maybeLitWord (view codehash ch))+ maybeHash ch = fromJust (error "Internal error: cannot find concrete codehash for partially symbolic code") (maybeLitWord ch._codehash) drawVm :: UiVmState -> [UiWidget] drawVm ui =@@ -813,35 +805,35 @@ :: UiVmState -> Pred VM isNewTraceAdded ui vm = let- currentTraceTree = length <$> traceForest (view uiVm ui)+ currentTraceTree = length <$> traceForest ui._uiVm newTraceTree = length <$> traceForest vm in currentTraceTree /= newTraceTree isNextSourcePosition :: UiVmState -> Pred VM isNextSourcePosition ui vm =- let dapp' = dapp (view uiTestOpts ui)- initialPosition = currentSrcMap dapp' (view uiVm ui)- in currentSrcMap dapp' vm /= initialPosition+ let dapp = ui._uiTestOpts.dapp+ initialPosition = currentSrcMap dapp ui._uiVm+ in currentSrcMap dapp vm /= initialPosition isNextSourcePositionWithoutEntering :: UiVmState -> Pred VM isNextSourcePositionWithoutEntering ui vm = let- dapp' = dapp (view uiTestOpts ui)- vm0 = view uiVm ui- initialPosition = currentSrcMap dapp' vm0- initialHeight = length (view frames vm0)+ dapp = ui._uiTestOpts.dapp+ vm0 = ui._uiVm+ initialPosition = currentSrcMap dapp vm0+ initialHeight = length vm0._frames in- case currentSrcMap dapp' vm of+ case currentSrcMap dapp vm of Nothing -> False Just here -> let moved = Just here /= initialPosition- deeper = length (view frames vm) > initialHeight+ deeper = length vm._frames > initialHeight boring =- case srcMapCode (view dappSources dapp') here of+ case srcMapCode dapp.sources here of Just bs -> BS.isPrefixOf "contract " bs Nothing ->@@ -850,20 +842,20 @@ moved && not deeper && not boring isExecutionHalted :: UiVmState -> Pred VM-isExecutionHalted _ vm = isJust (view result vm)+isExecutionHalted _ vm = isJust vm._result currentSrcMap :: DappInfo -> VM -> Maybe SrcMap currentSrcMap dapp vm = do this <- currentContract vm- i <- (view opIxMap this) SVec.!? (view (state . pc) vm)+ i <- this._opIxMap SVec.!? vm._state._pc srcMap dapp this i drawStackPane :: UiVmState -> UiWidget drawStackPane ui = let- gasText = showWordExact (num $ view (uiVm . state . gas) ui)+ gasText = showWordExact (num ui._uiVm._state._gas) labelText = txt ("Gas available: " <> gasText <> "; stack:")- stackList = list StackPane (Vec.fromList $ zip [(1 :: Int)..] (fmap simplify $ view (uiVm . state . stack) ui)) 2+ stackList = list StackPane (Vec.fromList $ zip [(1 :: Int)..] (simplify <$> ui._uiVm._state._stack)) 2 in hBorderWithLabel labelText <=> renderList (\_ (i, w) ->@@ -872,14 +864,14 @@ <+> ourWrap (Text.unpack $ prettyIfConcreteWord w) , dim (txt (" " <> case unlit w of Nothing -> ""- Just u -> showWordExplanation u $ dapp (view uiTestOpts ui)))+ Just u -> showWordExplanation u ui._uiTestOpts.dapp)) ]) False stackList message :: VM -> String message vm =- case view result vm of+ case vm._result of Just (VMSuccess (ConcreteBuf msg)) -> "VMSuccess: " <> (show $ ByteStringS msg) Just (VMSuccess (msg)) ->@@ -889,13 +881,13 @@ Just (VMFailure err) -> "VMFailure: " <> show err Nothing ->- "Executing EVM code in " <> show (view (state . contract) vm)+ "Executing EVM code in " <> show vm._state._contract drawBytecodePane :: UiVmState -> UiWidget drawBytecodePane ui = let- vm = view uiVm ui+ vm = ui._uiVm move = maybe id listMoveTo $ vmOpIx vm in hBorderWithLabel (str $ message vm) <=>@@ -905,7 +897,7 @@ else withDefAttr boldAttr (opWidget x)) False (move $ list BytecodePane- (maybe mempty (view codeOps) (currentContract vm))+ (maybe mempty (._codeOps) (currentContract vm)) 1) @@ -922,31 +914,31 @@ drawTracePane :: UiVmState -> UiWidget drawTracePane s =- let vm = view uiVm s- dapp' = dapp (view uiTestOpts s)+ let vm = s._uiVm+ dapp = s._uiTestOpts.dapp traceList = list TracePane (Vec.fromList . Text.lines- . showTraceTree dapp'+ . showTraceTree dapp $ vm) 1 - in case view uiShowMemory s of+ in case s._uiShowMemory of True -> viewport TracePane Vertical $ hBorderWithLabel (txt "Calldata")- <=> ourWrap (prettyIfConcrete (view (state . calldata) vm))+ <=> ourWrap (prettyIfConcrete vm._state._calldata) <=> hBorderWithLabel (txt "Returndata")- <=> ourWrap (prettyIfConcrete (view (state . returndata) vm))+ <=> ourWrap (prettyIfConcrete vm._state._returndata) <=> hBorderWithLabel (txt "Output")- <=> ourWrap (maybe "" show (view result vm))+ <=> ourWrap (maybe "" show vm._result) <=> hBorderWithLabel (txt "Cache")- <=> ourWrap (show (view (cache . path) vm))+ <=> ourWrap (show vm._cache._path) <=> hBorderWithLabel (txt "Path Conditions")- <=> (ourWrap $ show $ view constraints vm)+ <=> (ourWrap $ show $ vm._constraints) <=> hBorderWithLabel (txt "Memory")- <=> (ourWrap (prettyIfConcrete (view (state . memory) vm)))+ <=> (ourWrap (prettyIfConcrete vm._state._memory)) False -> hBorderWithLabel (txt "Trace") <=> renderList@@ -965,45 +957,40 @@ } solidityList :: VM -> DappInfo -> List Name (Int, ByteString)-solidityList vm dapp' =+solidityList vm dapp = list SolidityPane- (case currentSrcMap dapp' vm of+ (case currentSrcMap dapp vm of Nothing -> mempty Just x ->- view (dappSources- . sourceLines- . ix (srcMapFile x)+ view (+ ix x.srcMapFile . to (Vec.imap (,)))- dapp')+ dapp.sources.lines) 1 drawSolidityPane :: UiVmState -> UiWidget drawSolidityPane ui =- let dapp' = dapp (view uiTestOpts ui)- dappSrcs = view dappSources dapp'- vm = view uiVm ui- in case currentSrcMap dapp' vm of+ let dapp = ui._uiTestOpts.dapp+ dappSrcs = dapp.sources+ vm = ui._uiVm+ in case currentSrcMap dapp vm of Nothing -> padBottom Max (hBorderWithLabel (txt "<no source map>")) Just sm -> let- rows = (_sourceLines dappSrcs) !! srcMapFile sm- subrange = lineSubrange rows (srcMapOffset sm, srcMapLength sm)+ rows = dappSrcs.lines !! sm.srcMapFile+ subrange = lineSubrange rows (sm.srcMapOffset, sm.srcMapLength) fileName :: Maybe Text- fileName = preview (dappSources . sourceFiles . ix (srcMapFile sm) . _1) dapp'+ fileName = preview (ix sm.srcMapFile . _1) dapp.sources.files lineNo :: Maybe Int- lineNo = maybe Nothing (\a -> Just (a - 1))- (snd <$>- (srcMapCodePos- (view dappSources dapp')- sm))+ lineNo = ((\a -> Just (a - 1)) . snd) =<< srcMapCodePos dapp.sources sm in vBox [ hBorderWithLabel $ txt (fromMaybe "<unknown>" fileName)- <+> str (":" ++ show lineNo)+ <+> str (":" ++ (maybe "?" show lineNo)) -- Show the AST node type if present <+> txt (" (" <> fromMaybe "?"- ((view dappAstSrcMap dapp') sm+ (dapp.astSrcMap sm >>= preview (key "name" . _String)) <> ")") , Centered.renderList (\_ (i, line) ->@@ -1021,7 +1008,7 @@ ]) False ((maybe id listMoveTo lineNo)- (solidityList vm dapp'))+ (solidityList vm dapp)) ] ifTallEnough :: Int -> Widget n -> Widget n -> Widget n
src/EVM/Transaction.hs view
@@ -52,94 +52,94 @@ -- | utility function for getting a more useful representation of accesslistentries -- duplicates only matter for gas computation txAccessMap :: Transaction -> Map Addr [W256]-txAccessMap tx = ((Map.fromListWith (++)) . makeTups) $ txAccessList tx- where makeTups = map (\ale -> (accessAddress ale, accessStorageKeys ale))+txAccessMap tx = ((Map.fromListWith (++)) . makeTups) tx.txAccessList+ where makeTups = map (\ale -> (ale.accessAddress , ale.accessStorageKeys )) ecrec :: W256 -> W256 -> W256 -> W256 -> Maybe Addr ecrec v r s e = num . word <$> EVM.Precompiled.execute 1 input 32 where input = BS.concat (word256Bytes <$> [e, v, r, s]) sender :: Int -> Transaction -> Maybe Addr-sender chainId tx = ecrec v' (txR tx) (txS tx) hash+sender chainId tx = ecrec v' tx.txR tx.txS hash where hash = keccak' (signingData chainId tx)- v = txV tx+ v = tx.txV v' = if v == 27 || v == 28 then v else 27 + v signingData :: Int -> Transaction -> ByteString signingData chainId tx =- case txType tx of+ case tx.txType of LegacyTransaction -> if v == (chainId * 2 + 35) || v == (chainId * 2 + 36) then eip155Data else normalData AccessListTransaction -> eip2930Data EIP1559Transaction -> eip1559Data- where v = fromIntegral (txV tx)- to' = case txToAddr tx of+ where v = fromIntegral tx.txV+ to' = case tx.txToAddr of Just a -> BS $ word160Bytes a Nothing -> BS mempty- maxFee = fromJust $ txMaxFeePerGas tx- maxPrio = fromJust $ txMaxPriorityFeeGas tx- gasPrice = fromJust $ txGasPrice tx- accessList = txAccessList tx+ maxFee = fromJust tx.txMaxFeePerGas+ maxPrio = fromJust tx.txMaxPriorityFeeGas+ gasPrice = fromJust tx.txGasPrice+ accessList = tx.txAccessList rlpAccessList = EVM.RLP.List $ map (\accessEntry ->- EVM.RLP.List [BS $ word160Bytes (accessAddress accessEntry),- EVM.RLP.List $ map rlpWordFull $ accessStorageKeys accessEntry]+ EVM.RLP.List [BS $ word160Bytes accessEntry.accessAddress,+ EVM.RLP.List $ map rlpWordFull accessEntry.accessStorageKeys] ) accessList- normalData = rlpList [rlpWord256 (txNonce tx),+ normalData = rlpList [rlpWord256 tx.txNonce, rlpWord256 gasPrice,- rlpWord256 (num $ txGasLimit tx),+ rlpWord256 (num tx.txGasLimit), to',- rlpWord256 (txValue tx),- BS (txData tx)]- eip155Data = rlpList [rlpWord256 (txNonce tx),+ rlpWord256 tx.txValue,+ BS tx.txData]+ eip155Data = rlpList [rlpWord256 tx.txNonce, rlpWord256 gasPrice,- rlpWord256 (num $ txGasLimit tx),+ rlpWord256 (num tx.txGasLimit), to',- rlpWord256 (txValue tx),- BS (txData tx),+ rlpWord256 tx.txValue,+ BS tx.txData, rlpWord256 (fromIntegral chainId), rlpWord256 0x0, rlpWord256 0x0] eip1559Data = cons 0x02 $ rlpList [ rlpWord256 (fromIntegral chainId),- rlpWord256 (txNonce tx),+ rlpWord256 tx.txNonce, rlpWord256 maxPrio, rlpWord256 maxFee,- rlpWord256 (num $ txGasLimit tx),+ rlpWord256 (num tx.txGasLimit), to',- rlpWord256 (txValue tx),- BS (txData tx),+ rlpWord256 tx.txValue,+ BS tx.txData, rlpAccessList] eip2930Data = cons 0x01 $ rlpList [ rlpWord256 (fromIntegral chainId),- rlpWord256 (txNonce tx),+ rlpWord256 tx.txNonce, rlpWord256 gasPrice,- rlpWord256 (num $ txGasLimit tx),+ rlpWord256 (num tx.txGasLimit), to',- rlpWord256 (txValue tx),- BS (txData tx),+ rlpWord256 tx.txValue,+ BS tx.txData, rlpAccessList] accessListPrice :: FeeSchedule Word64 -> [AccessListEntry] -> Word64 accessListPrice fs al = sum (map (\ale ->- g_access_list_address fs +- (g_access_list_storage_key fs * (fromIntegral . length) (accessStorageKeys ale)))+ fs.g_access_list_address ++ (fs.g_access_list_storage_key * (fromIntegral . length) ale.accessStorageKeys)) al) txGasCost :: FeeSchedule Word64 -> Transaction -> Word64 txGasCost fs tx =- let calldata = txData tx+ let calldata = tx.txData zeroBytes = BS.count 0 calldata nonZeroBytes = BS.length calldata - zeroBytes- baseCost = g_transaction fs- + (if isNothing (txToAddr tx) then g_txcreate fs else 0)- + (accessListPrice fs $ txAccessList tx)- zeroCost = g_txdatazero fs- nonZeroCost = g_txdatanonzero fs+ baseCost = fs.g_transaction+ + (if isNothing tx.txToAddr then fs.g_txcreate else 0)+ + (accessListPrice fs tx.txAccessList )+ zeroCost = fs.g_txdatazero+ nonZeroCost = fs.g_txdatanonzero in baseCost + zeroCost * (fromIntegral zeroBytes) + nonZeroCost * (fromIntegral nonZeroBytes) instance FromJSON AccessListEntry where@@ -200,16 +200,16 @@ -- and pay receiving address initTx :: EVM.VM -> EVM.VM initTx vm = let- toAddr = view (EVM.state . EVM.contract) vm- origin = view (EVM.tx . EVM.origin) vm- gasPrice = view (EVM.tx . EVM.gasprice) vm- gasLimit = view (EVM.tx . EVM.txgaslimit) vm- coinbase = view (EVM.block . EVM.coinbase) vm- value = view (EVM.state . EVM.callvalue) vm- toContract = initialContract (view (EVM.state . EVM.code) vm)- preState = setupTx origin coinbase gasPrice gasLimit $ view (EVM.env . EVM.contracts) vm+ toAddr = vm._state._contract+ origin = vm._tx._origin+ gasPrice = vm._tx._gasprice+ gasLimit = vm._tx._txgaslimit+ coinbase = vm._block._coinbase+ value = vm._state._callvalue+ toContract = initialContract vm._state._code+ preState = setupTx origin coinbase gasPrice gasLimit vm._env._contracts oldBalance = view (accountAt toAddr . balance) preState- creation = view (EVM.tx . EVM.isCreate) vm+ creation = vm._tx._isCreate initState = (case unlit value of Just v -> ((Map.adjust (over balance (subtract v))) origin) . (Map.adjust (over balance (+ v))) toAddr
src/EVM/Types.hs view
@@ -5,7 +5,7 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE DefaultSignatures #-} -{-# OPTIONS_GHC -Wno-error=inline-rule-shadowing #-}+{-# OPTIONS_GHC -Wno-inline-rule-shadowing #-} module EVM.Types where @@ -16,7 +16,7 @@ import Data.Map (Map) import Data.Bifunctor (first) import Data.Char-import Data.List (isPrefixOf)+import Data.List (isPrefixOf, foldl') import Data.ByteString (ByteString) import Data.ByteString.Base16 as BS16 import Data.ByteString.Builder (byteStringHex, toLazyByteString)@@ -124,6 +124,7 @@ | StackLimitExceeded | InvalidMemoryAccess | BadJumpDestination+ | StackUnderrun | SelfDestruct | TmpErr String deriving (Show, Eq, Ord)@@ -410,6 +411,12 @@ (./=) :: (Typeable a) => Expr a -> Expr a -> Prop x ./= y = PNeg (PEq x y) +pand :: [Prop] -> Prop+pand = foldl' PAnd (PBool True)++por :: [Prop] -> Prop+por = foldl' POr (PBool False)+ instance Eq Prop where PBool a == PBool b = a == b PEq (a :: Expr x) (b :: Expr x) == PEq (c :: Expr y) (d :: Expr y)@@ -639,7 +646,7 @@ word256Bytes x = BS.pack [byteAt x (31 - i) | i <- [0..31]] word160Bytes :: Addr -> ByteString-word160Bytes x = BS.pack [byteAt (addressWord160 x) (19 - i) | i <- [0..19]]+word160Bytes x = BS.pack [byteAt x.addressWord160 (19 - i) | i <- [0..19]] newtype Nibble = Nibble Word8 deriving ( Num, Integral, Real, Ord, Enum, Eq, Bounded, Generic)
src/EVM/UnitTest.hs view
@@ -9,65 +9,57 @@ 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 qualified as Expr+import EVM.Facts qualified as Facts+import EVM.Facts.Git qualified as Git+import EVM.FeeSchedule qualified as FeeSchedule+import EVM.Fetch qualified as Fetch import EVM.Format import EVM.Solidity-import qualified EVM.SymExec as SymExec+import EVM.SymExec qualified as SymExec import EVM.SymExec (defaultVeriOpts, symCalldata, verify, isQed, extractCex, runExpr, subModel, VeriOpts) import EVM.Types hiding (Failure) import EVM.Transaction (initTx) import EVM.RLP-import qualified EVM.Facts as Facts-import qualified EVM.Facts.Git as Git-import qualified EVM.Fetch as Fetch-import qualified EVM.Expr as Expr--import qualified EVM.FeeSchedule as FeeSchedule- import EVM.Stepper (Stepper, interpret)-import qualified EVM.Stepper as Stepper-import qualified Control.Monad.Operational as Operational+import EVM.Stepper qualified as Stepper +import Control.Monad.Operational qualified as Operational import Control.Lens hiding (Indexed, elements, List, passing)-import Control.Monad.State.Strict hiding (state)-import qualified Control.Monad.State.Strict as State- import Control.Monad.Par.Class (spawn_)+import Control.Monad.Par.Class qualified as Par import Control.Monad.Par.IO (runParIO)--import qualified Data.ByteString.Lazy as BSLazy-import Data.Binary.Get (runGet)-import Data.ByteString (ByteString)-import Data.Decimal (DecimalRaw(..))-import Data.Either (isRight)-import Data.Foldable (toList)-import Data.Map (Map)-import Data.Maybe (fromMaybe, catMaybes, fromJust, isJust, fromMaybe, mapMaybe, isNothing)-import Data.Text (isPrefixOf, stripSuffix, intercalate, Text, pack, unpack)-import Data.Word (Word32, Word64)-import Data.Text.Encoding (encodeUtf8)-import System.Environment (lookupEnv)-import System.IO (hFlush, stdout)-import GHC.Natural--import qualified Control.Monad.Par.Class as Par-import qualified Data.ByteString as BS-import qualified Data.Map as Map-import qualified Data.Text as Text-import qualified Data.Text.IO as Text-+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.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 qualified Data.MultiSet as MultiSet-+import Data.MultiSet qualified as MultiSet import Data.Set (Set)-import qualified Data.Set as Set-+import Data.Set qualified as Set+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 qualified Data.Vector as Vector-+import Data.Vector qualified as Vector+import Data.Word (Word32, Word64)+import GHC.Natural+import System.Environment (lookupEnv)+import System.IO (hFlush, stdout) import Test.QuickCheck hiding (verbose) data UnitTestOptions = UnitTestOptions@@ -127,10 +119,10 @@ -- | Generate VeriOpts from UnitTestOptions makeVeriOpts :: UnitTestOptions -> VeriOpts makeVeriOpts opts =- defaultVeriOpts { SymExec.debug = smtDebug opts- , SymExec.maxIter = maxIter opts- , SymExec.askSmtIters = askSmtIters opts- , SymExec.rpcInfo = rpcInfo opts+ defaultVeriOpts { SymExec.debug = opts.smtDebug+ , SymExec.maxIter = opts.maxIter+ , SymExec.askSmtIters = opts.askSmtIters+ , SymExec.rpcInfo = opts.rpcInfo } -- | Top level CLI endpoint for dapp-test@@ -139,7 +131,7 @@ out <- liftIO $ readSolc solcFile case out of Just (contractMap, _) -> do- let unitTests = findUnitTests (EVM.UnitTest.match opts) $ Map.elems contractMap+ let unitTests = findUnitTests opts.match $ Map.elems contractMap results <- concatMapM (runUnitTestContract opts contractMap) unitTests let (passing, vms) = unzip results case cache' of@@ -148,13 +140,11 @@ Just path -> -- merge all of the post-vm caches and save into the state let- evmcache = mconcat [view EVM.cache vm | vm <- vms]+ evmcache = mconcat [vm._cache | vm <- vms] in liftIO $ Git.saveFacts (Git.RepoAt path) (Facts.cacheFacts evmcache) - if and passing- then return True- else return False+ return $ and passing Nothing -> error ("Failed to read Solidity JSON for `" ++ solcFile ++ "'") @@ -164,7 +154,7 @@ initializeUnitTest :: UnitTestOptions -> SolcContract -> Stepper () initializeUnitTest UnitTestOptions { .. } theContract = do - let addr = testAddress testParams+ let addr = testParams.testAddress Stepper.evm $ do -- Maybe modify the initial VM, e.g. to load library code@@ -177,10 +167,10 @@ Stepper.evm $ do -- Give a balance to the test target- env . contracts . ix addr . balance += testBalanceCreate testParams+ env . contracts . ix addr . balance += testParams.testBalanceCreate -- call setUp(), if it exists, to initialize the test contract- let theAbi = view abiMap theContract+ let theAbi = theContract.abiMap setUp = abiKeccak (encodeUtf8 "setUp()") when (isJust (Map.lookup setUp theAbi)) $ do@@ -218,11 +208,11 @@ Stepper.evm $ do cs <- use (env . contracts) abiCall testParams (Right bs)- let (Method _ inputs sig _ _) = fromMaybe (error "unknown abi call") $ Map.lookup (num $ word $ BS.take 4 bs) (view dappAbiMap dapp)+ let (Method _ inputs sig _ _) = fromMaybe (error "unknown abi call") $ Map.lookup (num $ word $ BS.take 4 bs) dapp.abiMap types = snd <$> inputs let ?context = DappContext dapp cs- this <- fromMaybe (error "unknown target") <$> (use (env . contracts . at (testAddress testParams)))- let name = maybe "" (contractNamePart . view contractName) $ lookupCode (view contractcode this) dapp+ this <- fromMaybe (error "unknown target") <$> (use (env . contracts . at testParams.testAddress))+ 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@@ -266,10 +256,10 @@ } deriving (Show) instance Eq OpLocation where- (==) (OpLocation a b) (OpLocation a' b') = b == b' && view contractcode a == view contractcode a'+ (==) (OpLocation a b) (OpLocation a' b') = b == b' && a._contractcode == a'._contractcode instance Ord OpLocation where- compare (OpLocation a b) (OpLocation a' b') = compare (view contractcode a, b) (view contractcode a', b')+ 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@@ -295,7 +285,7 @@ -- 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 view result vm0 of+ case vm0._result of Nothing -> do vm1 <- zoom _1 (State.state (runState exec1) >> get) zoom _2 (modify (MultiSet.insert (currentOpLocation vm1)))@@ -341,7 +331,7 @@ coverageReport dapp cov = let sources :: SourceCache- sources = view dappSources dapp+ sources = dapp.sources allPositions :: Set (Text, Int) allPositions =@@ -349,9 +339,9 @@ . mapMaybe (srcMapCodePos sources) . toList $ mconcat- ( view dappSolcByName dapp+ ( dapp.solcByName & Map.elems- & map (\x -> view runtimeSrcmap x <> view creationSrcmap x)+ & map (\x -> x.runtimeSrcmap <> x.creationSrcmap) ) ) @@ -362,8 +352,8 @@ linesByName = Map.fromList $ zipWith (\(name, _) lines' -> (name, lines'))- (view sourceFiles sources)- (view sourceLines sources)+ sources.files+ sources.lines f :: Text -> Vector ByteString -> Vector (Int, ByteString) f name =@@ -388,7 +378,7 @@ opts@(UnitTestOptions {..}) contractMap _ (name, testNames) = do -- Look for the wanted contract by name from the Solidity info- case preview (ix name) contractMap of+ case Map.lookup name contractMap of Nothing -> -- Fail if there's no such contract error $ "Contract " ++ unpack name ++ " not found"@@ -432,7 +422,7 @@ ++ unpack name -- Look for the wanted contract by name from the Solidity info- case preview (ix name) contractMap of+ case Map.lookup name contractMap of Nothing -> -- Fail if there's no such contract error $ "Contract " ++ unpack name ++ " not found"@@ -446,7 +436,7 @@ (Stepper.enter name >> initializeUnitTest opts theContract)) vm0 - case view result vm1 of+ case vm1._result of Nothing -> error "internal error: setUp() did not end with a result" Just (VMFailure _) -> liftIO $ do Text.putStrLn "\x1b[31m[BAIL]\x1b[0m setUp() "@@ -461,7 +451,7 @@ runCache (results, vm) (test, types) = do (t, r, vm') <- runTest opts vm (test, types) liftIO $ Text.putStrLn t- let vmCached = vm & set cache (view cache vm')+ let vmCached = vm { _cache = vm'._cache } pure (((r, vm'): results), vmCached) -- Run all the test cases and print their status updates,@@ -527,31 +517,33 @@ Nothing -> Stepper.evmIO $ do vm <- get- let cs = view (env . contracts) vm- noCode c = case view contractcode c of+ let cs = vm._env._contracts+ noCode c = case c._contractcode of RuntimeCode (ConcreteRuntimeCode "") -> True RuntimeCode (SymbolicRuntimeCode c') -> null c' _ -> False- mutable m = view methodMutability m `elem` [NonPayable, Payable]+ mutable m = m.mutability `elem` [NonPayable, Payable] knownAbis :: Map Addr SolcContract knownAbis = -- exclude contracts without code- Map.filter (not . BS.null . view runtimeCode) $+ Map.filter (not . BS.null . (.runtimeCode)) $ -- exclude contracts without state changing functions- Map.filter (not . null . Map.filter mutable . view abiMap) $+ Map.filter (not . null . Map.filter mutable . (.abiMap)) $ -- exclude testing abis- Map.filter (isNothing . preview (abiMap . ix unitTestMarkerAbi)) $+ Map.filter (isNothing . preview (ix unitTestMarkerAbi) . (.abiMap)) $ -- pick all contracts with known compiler artifacts- fmap fromJust (Map.filter isJust $ Map.fromList [(addr, lookupCode (view contractcode c) dapp) | (addr, c) <- Map.toList cs])+ fmap fromJust (Map.filter isJust $ Map.fromList [(addr, lookupCode c._contractcode dapp) | (addr, c) <- Map.toList cs]) selected = [(addr,- fromMaybe (error ("no src found for: " <> show addr)) $ lookupCode (view contractcode (fromMaybe (error $ "contract not found: " <> show addr) $ Map.lookup addr cs)) dapp)+ fromMaybe (error ("no src found for: " <> show addr)) $+ lookupCode (fromMaybe (error $ "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 $ view abiMap solcInfo)+ (_, (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@@ -567,7 +559,7 @@ let cd = abiMethod (sig <> "(" <> intercalate "," ((pack . show) <$> types) <> ")") args -- increment timestamp with random amount timepassed <- num <$> generate (arbitrarySizedNatural :: Gen Word32)- let ts = fromMaybe (error "symbolic timestamp not supported here") $ maybeLitWord $ view (block . timestamp) vm+ let ts = fromMaybe (error "symbolic timestamp not supported here") $ maybeLitWord vm._block._timestamp return (caller', target, cd, num ts + timepassed) let opts' = opts { testParams = testParams {testAddress = target, testCaller = caller', testTimestamp = timestamp'}} thisCallRLP = List [BS $ word160Bytes caller', BS $ word160Bytes target, BS cd, BS $ word256Bytes timestamp']@@ -593,7 +585,7 @@ getTargetContracts UnitTestOptions{..} = do vm <- Stepper.evm get let contract' = fromJust $ currentContract vm- theAbi = view abiMap $ fromJust $ lookupCode (view contractcode contract') dapp+ theAbi = (fromJust $ lookupCode contract'._contractcode dapp).abiMap setUp = abiKeccak (encodeUtf8 "targetContracts()") case Map.lookup setUp theAbi of Nothing -> return []@@ -653,7 +645,7 @@ (EVM.Stepper.interpret (Fetch.oracle solvers rpcInfo) (checkFailures opts testName bailed)) vm' if success then- let gasSpent = num (testGasCall testParams) - view (state . gas) vm'+ let gasSpent = num testParams.testGasCall - vm'._state._gas gasText = pack $ show (fromIntegral gasSpent :: Integer) in pure@@ -721,7 +713,7 @@ symRun opts@UnitTestOptions{..} vm testName types = do let cd = symCalldata testName types [] (AbstractBuf "txdata") shouldFail = "proveFail" `isPrefixOf` testName- testContract = view (state . contract) vm+ testContract = vm._state._contract -- 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()@@ -764,7 +756,7 @@ , intercalate "\n" $ indentLines 2 . mkMsg <$> failures' ] where- ctx = DappContext { _contextInfo = dapp, _contextEnv = mempty }+ ctx = DappContext { info = dapp, env = mempty } showRes = \case Return _ _ _ -> if "proveFail" `isPrefixOf` testName then "Successful execution"@@ -835,7 +827,7 @@ passOutput :: VM -> UnitTestOptions -> Text -> Text passOutput vm UnitTestOptions { .. } testName =- let ?context = DappContext { _contextInfo = dapp, _contextEnv = vm ^?! EVM.env . EVM.contracts }+ let ?context = DappContext { info = dapp, env = vm._env._contracts } in let v = fromMaybe 0 verbose in if (v > 1) then mconcat@@ -843,7 +835,7 @@ , fromMaybe "" (stripSuffix "()" testName) , "\n" , if (v > 2) then indentLines 2 (showTraceTree dapp vm) else ""- , indentLines 2 (formatTestLogs (view dappEventMap dapp) (view logs vm))+ , indentLines 2 (formatTestLogs dapp.eventMap vm._logs) , "\n" ] else ""@@ -851,7 +843,7 @@ -- TODO failOutput :: VM -> UnitTestOptions -> Text -> Text failOutput vm UnitTestOptions { .. } testName =- let ?context = DappContext { _contextInfo = dapp, _contextEnv = vm ^?! EVM.env . EVM.contracts}+ let ?context = DappContext { info = dapp, env = vm._env._contracts} in mconcat [ "Failure: " , fromMaybe "" (stripSuffix "()" testName)@@ -859,7 +851,7 @@ , case verbose of Just _ -> indentLines 2 (showTraceTree dapp vm) _ -> ""- , indentLines 2 (formatTestLogs (view dappEventMap dapp) (view logs vm))+ , indentLines 2 (formatTestLogs dapp.eventMap vm._logs) , "\n" ] @@ -955,7 +947,7 @@ assign (state . caller) (litAddr testCaller) assign (state . gas) testGasCall origin' <- fromMaybe (initialContract (RuntimeCode (ConcreteRuntimeCode ""))) <$> use (env . contracts . at testOrigin)- let originBal = view balance origin'+ let originBal = origin'._balance when (originBal < testGasprice * (num testGasCall)) $ error "insufficient balance for gas cost" vm <- get put $ initTx vm@@ -965,7 +957,7 @@ let TestVMParams {..} = testParams vm = makeVm $ VMOpts- { vmoptContract = initialContract (InitCode (view creationCode theContract) mempty)+ { vmoptContract = initialContract (InitCode theContract.creationCode mempty) , vmoptCalldata = mempty , vmoptValue = Lit 0 , vmoptAddress = testAddress
test/BlockchainTests.hs view
@@ -13,7 +13,7 @@ import EVM.FeeSchedule qualified import EVM.Fetch qualified import EVM.Stepper qualified-import EVM.SMT (withSolvers, Solver(Z3))+import EVM.Solvers (withSolvers, Solver(Z3)) import EVM.Transaction import EVM.TTY qualified as TTY import EVM.Types@@ -104,11 +104,7 @@ -- CI has issues with some heaver tests, disable in bulk ciIgnoredFiles :: [String]-ciIgnoredFiles =- [ "BlockchainTests/GeneralStateTests/VMTests/vmPerformance"- , "BlockchainTests/GeneralStateTests/stQuadraticComplexityTest"- , "BlockchainTests/GeneralStateTests/stStaticCall"- ]+ciIgnoredFiles = [] commonProblematicTests :: Map String (TestTree -> TestTree) commonProblematicTests = Map.fromList@@ -186,8 +182,8 @@ , ("bad-storage", not okData || okMoney || okNonce || okCode) , ("bad-code", not okCode || okMoney || okNonce || okData) ])- check = checkContracts x- expected = testExpectation x+ check = x.checkContracts+ expected = x.testExpectation actual = Map.map (,mempty) $ view (EVM.env . EVM.contracts) vm -- . to (fmap (clearZeroStorage.clearOrigStorage))) vm printStorage = show -- TODO: fixme @@ -203,7 +199,7 @@ checkExpectation :: Bool -> Case -> EVM.VM -> IO (Maybe String) checkExpectation diff x vm = do- let expectation = testExpectation x+ let expectation = x.testExpectation (okState, b2, b3, b4, b5) = checkExpectedContracts vm expectation if okState then pure Nothing@@ -260,7 +256,7 @@ clearCode :: (EVM.Contract, Storage) -> (EVM.Contract, Storage) clearCode (c, s) = (set contractcode (EVM.RuntimeCode (EVM.ConcreteRuntimeCode "")) c, s) -newtype ContractWithStorage = ContractWithStorage { unContractWithStorage :: (EVM.Contract, Storage) }+newtype ContractWithStorage = ContractWithStorage (EVM.Contract, Storage) instance FromJSON ContractWithStorage where parseJSON (JSON.Object v) = do@@ -302,10 +298,11 @@ parseContracts :: Which -> JSON.Object -> JSON.Parser (Map Addr (EVM.Contract, Storage)) parseContracts w v =- (Map.map unContractWithStorage) <$> (v .: which >>= parseJSON)+ (Map.map unwrap) <$> (v .: which >>= parseJSON) where which = case w of Pre -> "pre" Post -> "postState"+ unwrap (ContractWithStorage x) = x parseBCSuite :: Lazy.ByteString -> Either String (Map String Case)@@ -343,7 +340,7 @@ fromBlockchainCase :: BlockchainCase -> Either BlockchainError Case fromBlockchainCase (BlockchainCase blocks preState postState network) = case (blocks, network) of- ([block], "London") -> case blockTxs block of+ ([block], "London") -> case block.blockTxs of [tx] -> fromBlockchainCase' block tx preState postState [] -> Left NoTxs _ -> Left TooManyTxs@@ -354,7 +351,7 @@ -> Map Addr (EVM.Contract, Storage) -> Map Addr (EVM.Contract, Storage) -> Either BlockchainError Case fromBlockchainCase' block tx preState postState =- let isCreate = isNothing (txToAddr tx) in+ let isCreate = isNothing tx.txToAddr in case (sender 1 tx, checkTx tx block preState) of (Nothing, _) -> Left SignatureUnverified (_, Nothing) -> Left (if isCreate then FailedCreate else InvalidTx)@@ -362,21 +359,21 @@ (EVM.VMOpts { vmoptContract = EVM.initialContract theCode , vmoptCalldata = (cd, [])- , vmoptValue = Lit (txValue tx)+ , vmoptValue = Lit tx.txValue , vmoptAddress = toAddr , vmoptCaller = litAddr origin , vmoptStorageBase = Concrete , vmoptOrigin = origin- , vmoptGas = txGasLimit tx - fromIntegral (txGasCost feeSchedule tx)- , vmoptBaseFee = blockBaseFee block- , vmoptPriorityFee = priorityFee tx (blockBaseFee block)- , vmoptGaslimit = txGasLimit tx- , vmoptNumber = blockNumber block- , vmoptTimestamp = Lit $ blockTimestamp block- , vmoptCoinbase = blockCoinbase block- , vmoptPrevRandao = blockDifficulty block+ , vmoptGas = tx.txGasLimit - fromIntegral (txGasCost feeSchedule tx)+ , vmoptBaseFee = block.blockBaseFee+ , vmoptPriorityFee = priorityFee tx block.blockBaseFee+ , vmoptGaslimit = tx.txGasLimit+ , vmoptNumber = block.blockNumber+ , vmoptTimestamp = Lit block.blockTimestamp+ , vmoptCoinbase = block.blockCoinbase+ , vmoptPrevRandao = block.blockDifficulty , vmoptMaxCodeSize = 24576- , vmoptBlockGaslimit = blockGasLimit block+ , vmoptBlockGaslimit = block.blockGasLimit , vmoptGasprice = effectiveGasPrice , vmoptSchedule = feeSchedule , vmoptChainId = 1@@ -387,38 +384,38 @@ checkState postState where- toAddr = fromMaybe (EVM.createAddress origin senderNonce) (txToAddr tx)+ toAddr = fromMaybe (EVM.createAddress origin senderNonce) tx.txToAddr senderNonce = view (accountAt origin . nonce) (Map.map fst preState) feeSchedule = EVM.FeeSchedule.berlin toCode = Map.lookup toAddr preState theCode = if isCreate- then EVM.InitCode (txData tx) mempty+ then EVM.InitCode tx.txData mempty else maybe (EVM.RuntimeCode (EVM.ConcreteRuntimeCode "")) (view contractcode . fst) toCode- effectiveGasPrice = effectiveprice tx (blockBaseFee block)+ effectiveGasPrice = effectiveprice tx block.blockBaseFee cd = if isCreate then mempty- else ConcreteBuf $ txData tx+ else ConcreteBuf tx.txData effectiveprice :: Transaction -> W256 -> W256 effectiveprice tx baseFee = priorityFee tx baseFee + baseFee priorityFee :: Transaction -> W256 -> W256 priorityFee tx baseFee = let- (txPrioMax, txMaxFee) = case txType tx of+ (txPrioMax, txMaxFee) = case tx.txType of EIP1559Transaction ->- let maxPrio = fromJust $ txMaxPriorityFeeGas tx- maxFee = fromJust $ txMaxFeePerGas tx+ let maxPrio = fromJust tx.txMaxPriorityFeeGas+ maxFee = fromJust tx.txMaxFeePerGas in (maxPrio, maxFee) _ ->- let gasPrice = fromJust $ txGasPrice tx+ let gasPrice = fromJust tx.txGasPrice in (gasPrice, gasPrice) in min txPrioMax (txMaxFee - baseFee) maxBaseFee :: Transaction -> W256 maxBaseFee tx =- case txType tx of- EIP1559Transaction -> fromJust $ txMaxFeePerGas tx- _ -> fromJust $ txGasPrice tx+ case tx.txType of+ EIP1559Transaction -> fromJust tx.txMaxFeePerGas+ _ -> fromJust tx.txGasPrice validateTx :: Transaction -> Block -> Map Addr (EVM.Contract, Storage) -> Maybe () validateTx tx block cs = do@@ -426,9 +423,9 @@ origin <- sender 1 tx originBalance <- (view balance) <$> view (at origin) cs' originNonce <- (view nonce) <$> view (at origin) cs'- let gasDeposit = (effectiveprice tx (blockBaseFee block)) * (num $ txGasLimit tx)- if gasDeposit + (txValue tx) <= originBalance- && txNonce tx == originNonce && blockBaseFee block <= maxBaseFee tx+ let gasDeposit = (effectiveprice tx block.blockBaseFee) * (num tx.txGasLimit)+ if gasDeposit + tx.txValue <= originBalance+ && tx.txNonce == originNonce && block.blockBaseFee <= maxBaseFee tx then Just () else Nothing @@ -436,9 +433,9 @@ checkTx tx block prestate = do origin <- sender 1 tx validateTx tx block prestate- let isCreate = isNothing (txToAddr tx)+ let isCreate = isNothing tx.txToAddr senderNonce = view (accountAt origin . nonce) (Map.map fst prestate)- toAddr = fromMaybe (EVM.createAddress origin senderNonce) (txToAddr tx)+ toAddr = fromMaybe (EVM.createAddress origin senderNonce) tx.txToAddr prevCode = view (accountAt toAddr . contractcode) (Map.map fst prestate) prevNonce = view (accountAt toAddr . nonce) (Map.map fst prestate) if isCreate && ((case prevCode of {EVM.RuntimeCode (EVM.ConcreteRuntimeCode b) -> not (BS.null b); _ -> True}) || (prevNonce /= 0))@@ -449,10 +446,10 @@ vmForCase :: Case -> EVM.VM vmForCase x = let- a = checkContracts x+ a = x.checkContracts cs = Map.map fst a st = Map.mapKeys num $ Map.map snd a- vm = EVM.makeVm (testVmOpts x)+ vm = EVM.makeVm x.testVmOpts & set (EVM.env . EVM.contracts) cs & set (EVM.env . EVM.storage) (ConcreteStore st) & set (EVM.env . EVM.origStorage) st
test/EVM/TestUtils.hs view
@@ -13,7 +13,7 @@ import System.Process (readProcess) import EVM.Solidity-import EVM.SMT+import EVM.Solvers import EVM.Dapp import EVM.UnitTest import EVM.Fetch (RpcInfo)
test/contracts/pass/cheatCodes.sol view
@@ -10,12 +10,19 @@ 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 CheatCodes is DSTest { address store = address(new HasStorage()); Hevm hevm = Hevm(HEVM_ADDRESS);@@ -85,5 +92,13 @@ (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)); } }
test/rpc.hs view
@@ -16,6 +16,7 @@ import EVM import EVM.ABI import EVM.SMT+import EVM.Solvers import EVM.Fetch import EVM.SymExec import EVM.TestUtils
test/test.hs view
@@ -43,7 +43,7 @@ import Data.Binary.Put (runPut) import Data.Binary.Get (runGetOrFail) -import EVM hiding (Query, allowFFI)+import EVM import EVM.SymExec import EVM.ABI import EVM.Exec@@ -53,11 +53,14 @@ import EVM.Solidity import EVM.Types import EVM.Traversals+import EVM.Concrete (createAddress) import EVM.SMT hiding (one)+import EVM.Solvers import qualified EVM.Expr as Expr import qualified Data.Text as T import Data.List (isSubsequenceOf) import EVM.TestUtils+import GHC.Conc (getNumProcessors) main :: IO () main = defaultMain tests@@ -87,6 +90,29 @@ (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 (VMFailure (Query (PleaseFetchContract _addr continue))) ->+ execState (continue dummyContract) vm1+ _ -> error "unexpected result"+ -- then it should fetch the slow+ vm3 = case vm2._result of+ Just (VMFailure (Query (PleaseFetchSlot _addr _slot continue))) ->+ execState (continue 1337) vm2+ _ -> error "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) ] -- These tests fuzz the simplifier by generating a random expression, -- applying some simplification rules, and then using the smt encoding to@@ -149,6 +175,10 @@ 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@@ -426,7 +456,7 @@ (json, path') <- solidity' srccode let (solc', _, _) = fromJust $ readJSON json initCode :: ByteString- initCode = fromJust $ solc' ^? ix (path' <> ":A") . creationCode+ initCode = (fromJust $ solc' ^? ix (path' <> ":A")).creationCode -- add constructor arguments assertEqual "constructor args screwed up metadata stripping" (stripBytecodeMetadata (initCode <> encodeAbiValue (AbiUInt 256 1))) (stripBytecodeMetadata initCode) ]@@ -649,7 +679,6 @@ putStrLn "Require works as expected" , testCase "ITE-with-bitwise-AND" $ do- --- using ignore to suppress huge output Just c <- solcRuntime "C" [i| contract C {@@ -670,7 +699,6 @@ putStrLn "expected counterexample found" , testCase "ITE-with-bitwise-OR" $ do- --- using ignore to suppress huge output Just c <- solcRuntime "C" [i| contract C {@@ -988,6 +1016,29 @@ (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just ("fun(uint256,uint256)", [AbiUIntType 256, AbiUIntType 256])) [] defaultVeriOpts putStrLn "XOR works as expected" ,+ testCase "opcode-addmod-no-overflow" $ do+ Just c <- solcRuntime "MyContract"+ [i|+ contract MyContract {+ function fun(uint8 a, uint8 b, uint8 c) external pure {+ require(a < 4);+ require(b < 4);+ require(c < 4);+ uint16 r1;+ uint16 r2;+ uint16 g2;+ assembly {+ r1 := add(a,b)+ r2 := mod(r1, c)+ g2 := addmod (a, b, c)+ }+ assert (r2 == g2);+ }+ }+ |]+ (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just ("fun(uint8,uint8,uint8)", [AbiUIntType 8, AbiUIntType 8, AbiUIntType 8])) [] defaultVeriOpts+ putStrLn "ADDMOD is fine on NON overflow values"+ , testCase "opcode-mulmod-no-overflow" $ do Just c <- solcRuntime "MyContract" [i|@@ -1589,8 +1640,7 @@ (res, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c Nothing [] defaultVeriOpts putStrLn $ "successfully explored: " <> show (Expr.numBranches res) <> " paths" ,- ignoreTest $ testCase "keccak soundness" $ do- --- using ignore to suppress huge output+ testCase "keccak soundness" $ do Just c <- solcRuntime "C" [i| contract C {@@ -1625,17 +1675,26 @@ aAddr = Addr 0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B Just c <- solcRuntime "C" code' Just a <- solcRuntime "A" code'- (_, [Cex _]) <- withSolvers Z3 1 Nothing $ \s -> do+ (_, [Cex (_, cex)]) <- withSolvers Z3 1 Nothing $ \s -> do let vm0 = abstractVM (Just ("call_A()", [])) [] c Nothing SymbolicS let vm = vm0 & set (state . callvalue) (Lit 0) & over (env . contracts) (Map.insert aAddr (initialContract (RuntimeCode (ConcreteRuntimeCode a))))- -- NOTE: this used to as follows, but there is no _storage field in Contract record- -- (Map.insert aAddr (initialContract (RuntimeCode $ ConcreteBuffer a) &- -- set EVM.storage (EVM.Symbolic [] store))) verify s defaultVeriOpts vm (Just $ checkAssertions defaultPanicCodes)- putStrLn "found counterexample:"++ let storeCex = cex.store+ addrC = W256 $ num $ 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"@@ -1713,6 +1772,69 @@ (_, [Qed _]) <- withSolvers Z3 1 Nothing $ \s -> checkAssert s defaultPanicCodes c (Just ("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 ("fun(uint256)", [AbiUIntType 256])) [] defaultVeriOpts+ let addr = W256 $ num $ 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 ("fun(uint256)", [AbiUIntType 256])) [] defaultVeriOpts+ let addr = W256 $ num $ 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 ("fun(uint256)", [AbiUIntType 256])) [] defaultVeriOpts ConcreteS 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" [@@ -1739,7 +1861,7 @@ |] withSolvers Z3 3 Nothing $ \s -> do a <- equivalenceCheck s aPrgm bPrgm defaultVeriOpts Nothing- assertBool "Must have a difference" (not (null a))+ assertBool "Must have a difference" (any isCex a) , testCase "eq-sol-exp-qed" $ do Just aPrgm <- solcRuntime "C"@@ -1764,7 +1886,7 @@ |] withSolvers Z3 3 Nothing $ \s -> do a <- equivalenceCheck s aPrgm bPrgm defaultVeriOpts Nothing- assertEqual "Must have no difference" [] a+ assertEqual "Must have no difference" [Qed ()] a return () , testCase "eq-sol-exp-cex" $ do@@ -1792,70 +1914,31 @@ withSolvers Z3 3 Nothing $ \s -> do let myVeriOpts = VeriOpts{ simp = True, debug = False, maxIter = Just 2, askSmtIters = Just 2, rpcInfo = Nothing} a <- equivalenceCheck s aPrgm bPrgm myVeriOpts Nothing- assertEqual "Must be different" (containsA (Cex ()) a) True+ assertEqual "Must be different" (any isCex a) True return () , testCase "eq-all-yul-optimization-tests" $ do let myVeriOpts = VeriOpts{ simp = True, debug = False, maxIter = Just 5, askSmtIters = Just 20, rpcInfo = Nothing } ignoredTests = [- "controlFlowSimplifier/terminating_for_nested.yul"- , "controlFlowSimplifier/terminating_for_nested_reversed.yul"- -- unbounded loop --- , "commonSubexpressionEliminator/branches_for.yul"- , "commonSubexpressionEliminator/loop.yul"- , "conditionalSimplifier/clear_after_if_continue.yul"+ "commonSubexpressionEliminator/branches_for.yul" , "conditionalSimplifier/no_opt_if_break_is_not_last.yul"- , "conditionalUnsimplifier/clear_after_if_continue.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/devcon_example.yul"- , "fullSuite/loopInvariantCodeMotion.yul" , "fullSuite/no_move_loop_orig.yul"- , "loadResolver/loop.yul" , "loopInvariantCodeMotion/multi.yul"- , "loopInvariantCodeMotion/recursive.yul"- , "loopInvariantCodeMotion/simple.yul"- , "redundantAssignEliminator/for_branch.yul"- , "redundantAssignEliminator/for_break.yul"- , "redundantAssignEliminator/for_continue.yul"- , "redundantAssignEliminator/for_decl_inside_break_continue.yul"- , "redundantAssignEliminator/for_deep_noremove.yul" , "redundantAssignEliminator/for_deep_simple.yul"- , "redundantAssignEliminator/for_multi_break.yul"- , "redundantAssignEliminator/for_nested.yul"- , "redundantAssignEliminator/for_rerun.yul"- , "redundantAssignEliminator/for_stmnts_after_break_continue.yul"- , "rematerialiser/branches_for1.yul"- , "rematerialiser/branches_for2.yul"- , "rematerialiser/for_break.yul"- , "rematerialiser/for_continue.yul"- , "rematerialiser/for_continue_2.yul"- , "rematerialiser/for_continue_with_assignment_in_post.yul"- , "rematerialiser/no_remat_in_loop.yul"- , "ssaTransform/for_reassign_body.yul"- , "ssaTransform/for_reassign_init.yul"- , "ssaTransform/for_reassign_post.yul"- , "ssaTransform/for_simple.yul"- , "loopInvariantCodeMotion/nonMovable.yul"- , "unusedAssignEliminator/for_rerun.yul"- , "unusedAssignEliminator/for_continue_3.yul"+ , "unusedAssignEliminator/for_deep_noremove.yul" , "unusedAssignEliminator/for_deep_simple.yul" , "ssaTransform/for_def_in_init.yul"- , "rematerialiser/many_refs_small_cost_loop.yul"+ , "loopInvariantCodeMotion/simple_state.yul"+ , "loopInvariantCodeMotion/simple.yul"+ , "loopInvariantCodeMotion/recursive.yul"+ , "loopInvariantCodeMotion/no_move_staticall_returndatasize.yul" , "loopInvariantCodeMotion/no_move_state_loop.yul"- , "loopInvariantCodeMotion/dependOnVarInLoop.yul"- , "forLoopInitRewriter/empty_pre.yul"- , "loadResolver/keccak_crash.yul"- , "blockFlattener/for_stmt.yul" -- symb input can loop it forever- , "unusedAssignEliminator/for.yul" -- not infinite, just 2**256-3 , "loopInvariantCodeMotion/no_move_state.yul" -- not infinite, but rollaround on a large int- , "loopInvariantCodeMotion/non-ssavar.yul" -- same as above- , "forLoopInitRewriter/complex_pre.yul"- , "rematerialiser/some_refs_small_cost_loop.yul" -- not infinite but 100 long- , "forLoopInitRewriter/simple.yul" , "loopInvariantCodeMotion/no_move_loop.yul" -- unexpected symbolic arg --@@ -1905,11 +1988,15 @@ , "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"@@ -1924,7 +2011,6 @@ , "commonSubexpressionEliminator/object_access.yul" , "expressionSplitter/object_access.yul" , "fullSuite/stack_compressor_msize.yul"- , "varNameCleaner/function_names.yul" -- stack too deep -- , "fullSuite/abi2.yul"@@ -2021,20 +2107,16 @@ , "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- , "fullSuite/clear_after_if_continue.yul"- , "reasoningBasedSimplifier/smod.yul" , "reasoningBasedSimplifier/mulmod.yul" -- TODO check what's wrong with these!- , "unusedStoreEliminator/create_inside_function.yul"- , "fullSimplify/not_applied_removes_non_constant_and_not_movable.yul" -- create bug?- , "unusedStoreEliminator/create.yul" -- create bug?- , "fullSuite/extcodelength.yul" -- extcodecopy bug?- , "loadResolver/keccak_short.yul" -- keccak bug- , "reasoningBasedSimplifier/signed_division.yul" -- ACTUAL bug, SDIV I think?+ , "loadResolver/keccak_short.yul" -- ACTUAL bug -- keccak+ , "reasoningBasedSimplifier/signed_division.yul" -- ACTUAL bug, SDIV ] solcRepo <- fromMaybe (error "cannot find solidity repo") <$> (lookupEnv "HEVM_SOLIDITY_REPO")@@ -2053,9 +2135,8 @@ False -> recursiveList ax (a:b) recursiveList [] b = pure b files <- recursiveList fullpaths []- -- let filesFiltered = filter (\file -> not $ any (\filt -> Data.List.isSubsequenceOf filt 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@@ -2083,7 +2164,7 @@ filteredBSym = symbolicMem [ replaceAll "" $ x *=~[re|^//|] | x <- onlyAfter [re|^// step:|] unfiltered, not $ x =~ [re|^$|] ] start <- getCurrentTime putStrLn $ "Checking file: " <> f- when (debug myVeriOpts) $ do+ when myVeriOpts.debug $ do putStrLn "-------------Original Below-----------------" mapM_ putStrLn unfiltered putStrLn "------------- Filtered A + Symb below-----------------"@@ -2093,14 +2174,17 @@ putStrLn "------------- END -----------------" Just aPrgm <- yul "" $ T.pack $ unlines filteredASym Just bPrgm <- yul "" $ T.pack $ unlines filteredBSym- withSolvers CVC5 6 (Just 3) $ \s -> do+ procs <- getNumProcessors+ withSolvers CVC5 (num procs) (Just 100) $ \s -> do res <- equivalenceCheck s aPrgm bPrgm myVeriOpts Nothing end <- getCurrentTime- case containsA (Cex()) res of+ case any isCex res of False -> do print $ "OK. Took " <> (show $ diffUTCTime end start) <> " seconds"- let timeouts = filter (\(_, _, c) -> c == EVM.SymExec.Timeout()) res- unless (null timeouts) $ putStrLn $ "But " <> (show $ length timeouts) <> " timeout(s) occurred"+ let timeouts = filter isTimeout res+ unless (null timeouts) $ do+ putStrLn $ "But " <> (show $ length timeouts) <> " timeout(s) occurred"+ error "Encountered timeouts, error" True -> do putStrLn $ "Not OK: " <> show f <> " Got: " <> show res error "Was NOT equivalent, error"@@ -2123,7 +2207,7 @@ print res pure $ case res of Unsat -> True- EVM.SMT.Unknown -> True+ EVM.Solvers.Unknown -> True Sat _ -> False Error _ -> False @@ -2132,7 +2216,7 @@ runSimpleVM x ins = case loadVM x of Nothing -> Nothing Just vm -> let calldata' = (ConcreteBuf ins)- in case runState (assign (state.calldata) calldata' >> exec) vm of+ in case runState (assign (state . calldata) calldata' >> exec) vm of (VMSuccess (ConcreteBuf bs), _) -> Just bs _ -> Nothing @@ -2429,6 +2513,46 @@ 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)