Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update hevm to 0.50.3 #942

Merged
merged 4 commits into from
Feb 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
pkgs.haskellPackages.callCabal2nix "hevm" (pkgs.fetchFromGitHub {
owner = "ethereum";
repo = "hevm";
rev = "2f498916e8d46966baa17472b16e5894b1adfc06";
sha256 = "sha256-i+4vIA/z+iuR79bqXOW8zw/pQZGAVjs9nsukd1WEBlc=";
rev = "release/0.50.3";
sha256 = "sha256-n+Y3Y8UGuRxMxzJjGB+/1QBaVLaCttOO2r3cb2PQCVM=";
}) { secp256k1 = pkgs.secp256k1; });

echidna = with pkgs; lib.pipe
Expand Down
16 changes: 8 additions & 8 deletions lib/Echidna/Events.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ import Data.Text (pack, Text)
import Data.Map qualified as M
import Data.Maybe (listToMaybe, fromJust)
import Data.Vector (fromList)
import Control.Lens

import EVM
import EVM.ABI (Event(..), Indexed(..), decodeAbiValue, AbiType(AbiUIntType, AbiTupleType, AbiStringType))
import EVM.Dapp
import EVM.Dapp (DappContext(..), DappInfo(..))
import EVM.Format (showValues, showError, contractNamePart)
import EVM.Solidity (SolcContract(..))
import EVM.Types (Expr(ConcreteBuf), W256, maybeLitWord)
import EVM.Solidity (contractName)

import Echidna.Types.Buffer (forceLit)
import qualified Data.Map as Map

type EventMap = M.Map W256 Event
type Events = [Text]
Expand All @@ -30,15 +30,15 @@ emptyEvents = fromForest []

maybeContractNameFromCodeHash :: (?context :: DappContext) => EVM.Types.W256 -> Maybe Text
maybeContractNameFromCodeHash codeHash = fmap contractToName maybeContract
where maybeContract = preview (contextInfo . dappSolcByHash . ix codeHash . _2) ?context
contractToName = view (contractName . to contractNamePart)
where maybeContract = snd <$> Map.lookup codeHash ?context.info.solcByHash
contractToName c = contractNamePart c.contractName

extractEvents :: Bool -> DappInfo -> VM -> Events
extractEvents decodeErrors dappInfo' vm =
let eventMap = dappInfo'._dappEventMap
extractEvents decodeErrors dappInfo vm =
let eventMap = dappInfo.eventMap
forest = traceForest vm
showTrace trace =
let ?context = DappContext { _contextInfo = dappInfo', _contextEnv = vm ^?! EVM.env . EVM.contracts } in
let ?context = DappContext { info = dappInfo, env = vm._env._contracts } in
let codehash' = fromJust $ maybeLitWord trace._traceContract._codehash
maybeContractName = maybeContractNameFromCodeHash codehash'
in
Expand Down
2 changes: 1 addition & 1 deletion lib/Echidna/Fetch.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import Echidna.Exec (execTx)
import Echidna.Events (extractEvents)

deployContracts :: DappInfo -> [(Addr, SolcContract)] -> Addr -> VM -> IO VM
deployContracts di cs = deployBytecodes' di $ map (\(a, c) -> (a, c._creationCode)) cs
deployContracts di cs = deployBytecodes' di $ map (\(a, c) -> (a, c.creationCode)) cs

deployBytecodes :: DappInfo -> [(Addr, Text)] -> Addr -> VM -> IO VM
deployBytecodes di cs =
Expand Down
20 changes: 10 additions & 10 deletions lib/Echidna/Output/Source.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ module Echidna.Output.Source where

import Prelude hiding (writeFile)

import Control.Lens
import Data.Foldable
import Data.Maybe (fromMaybe, mapMaybe)
import Data.List (nub, sort)
import Data.Map qualified as M
import Data.Sequence qualified as Seq
import Data.Set qualified as S
import Data.Text (Text, pack)
import Data.Text qualified as T
Expand Down Expand Up @@ -37,8 +37,8 @@ ppCoveredCode :: Bool -> SourceCache -> [SolcContract] -> CoverageMap -> Text
ppCoveredCode isHtml sc cs s | s == mempty = "Coverage map is empty"
| otherwise =
let allFiles = zipWith (\(srcPath, _rawSource) srcLines -> (srcPath, V.map decodeUtf8 srcLines))
sc._sourceFiles
sc._sourceLines
sc.files
sc.lines
-- ^ Collect all the possible lines from all the files
covLines = srcMapCov sc s cs
-- ^ List of covered lines during the fuzzing campaing
Expand Down Expand Up @@ -111,14 +111,14 @@ srcMapCov sc s contracts =
M.map (M.fromListWith (++)) .
M.fromListWith (++) .
map (\(srcPath, line, txResult) -> (srcPath, [(line, [txResult])])) .
nub . -- Deduplicate results
mapMaybe (srcMapCodePosResult sc) $ -- Get the filename, number of line and tx result
nub . -- Deduplicate results
mapMaybe (srcMapCodePosResult sc) $ -- Get the filename, number of line and tx result
concatMap mapContract contracts
where
mapContract c =
mapMaybe (srcMapForOpLocation c) . -- Get the mapped line and tx result
S.toList . fromMaybe S.empty $ -- Convert from Set to list
M.lookup (getBytecodeMetadata c._runtimeCode) s -- Get the coverage information of the current contract
mapMaybe (srcMapForOpLocation c) . -- Get the mapped line and tx result
S.toList . fromMaybe S.empty $ -- Convert from Set to list
M.lookup (getBytecodeMetadata c.runtimeCode) s -- Get the coverage information of the current contract

-- | Given a source cache, a mapped line, return a tuple with the filename, number of line and tx result
srcMapCodePosResult :: SourceCache -> (SrcMap, TxResult) -> Maybe (Text, Int, TxResult)
Expand All @@ -129,7 +129,7 @@ srcMapCodePosResult sc (n, r) = case srcMapCodePos sc n of
-- | Given a contract, and tuple as coverage, return the corresponding mapped line (if any)
srcMapForOpLocation :: SolcContract -> CoverageInfo -> Maybe (SrcMap, TxResult)
srcMapForOpLocation contract (_,n,_,r) =
case preview (ix n) (contract._runtimeSrcmap <> contract._creationSrcmap) of
case Seq.lookup n (contract.runtimeSrcmap <> contract.creationSrcmap) of
Just sm -> Just (sm,r)
_ -> Nothing

Expand All @@ -141,4 +141,4 @@ buildRuntimeLinesMap sc contracts =
[(k, S.singleton v) | (k, v) <- mapMaybe (srcMapCodePos sc) srcMaps]
where
srcMaps = concatMap
(\c -> toList $ c._runtimeSrcmap <> c._creationSrcmap) contracts
(\c -> toList $ c.runtimeSrcmap <> c.creationSrcmap) contracts
2 changes: 1 addition & 1 deletion lib/Echidna/RPC.hs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ execEthenoTxs :: (MonadState VM m, MonadFail m, MonadThrow m) => Etheno -> m ()
execEthenoTxs et = do
setupEthenoTx et
vm <- get
res <- exec
res <- fromEVM exec
case (res, et) of
(_ , AccountCreated _) -> return ()
(Reversion, _) -> void $ put vm
Expand Down
45 changes: 24 additions & 21 deletions lib/Echidna/Solidity.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@ module Echidna.Solidity where

import Control.Lens
import Control.Arrow (first)
import Control.Monad (liftM2, when, unless, forM_)
import Control.Monad (when, unless, forM_)
import Control.Monad.Catch (MonadThrow(..))
import Control.Monad.Extra (whenM)
import Control.Monad.State.Strict (execStateT)
import Data.Foldable (toList)
import Data.HashMap.Strict qualified as M
import Data.List (find, partition, isSuffixOf, (\\))
import Data.List.NonEmpty (NonEmpty((:|)))
import Data.List.NonEmpty qualified as NE
import Data.List.NonEmpty.Extra qualified as NEE
import Data.Map (Map, keys, elems, unions, member)
import Data.Map (Map, keys, unions, member)
import Data.Map qualified as Map
import Data.Maybe (isJust, isNothing, catMaybes, listToMaybe)
import Data.Set (Set)
import Data.Set qualified as Set
import Data.Text (Text, isPrefixOf, isSuffixOf, append)
import Data.Text qualified as T
import Data.Text.Lens (unpacked)
import System.Directory (doesDirectoryExist, doesFileExist, findExecutable, listDirectory, removeFile)
import System.Process (StdStream(..), readCreateProcessWithExitCode, proc, std_err)
import System.Exit (ExitCode(..))
Expand All @@ -42,8 +44,6 @@ import Echidna.Types.Solidity hiding (deployBytecodes, deployContracts)
import Echidna.Types.Test (EchidnaTest(..))
import Echidna.Types.Tx (basicTx, createTxWithValue, unlimitedGasPerBlock, initialTimestamp, initialBlockNumber)
import Echidna.Types.World (World(..))
import qualified Data.Set as Set
import Data.Set (Set)

-- | Given a list of source caches (SourceCaches) and an optional contract name,
-- select one that includes that contract (if possible). Otherwise, use the first source
Expand Down Expand Up @@ -146,7 +146,10 @@ filterMethodsWithArgs ms = case NE.filter (\(_, xs) -> not $ null xs) ms of
fs -> NE.fromList fs

abiOf :: Text -> SolcContract -> NE.NonEmpty SolSignature
abiOf pref cc = fallback NE.:| filter (not . isPrefixOf pref . fst) (elems (cc._abiMap) <&> \m -> (m._methodName, m ^.. methodInputs . traverse . _2))
abiOf pref solcContract =
fallback :|
filter (not . isPrefixOf pref . fst)
(Map.elems solcContract.abiMap <&> \method -> (method.name, snd <$> method.inputs))

-- | Given an optional contract name and a list of 'SolcContract's, try to load the specified
-- contract, or, if not provided, the first contract in the list, into a 'VM' usable for Echidna
Expand All @@ -159,25 +162,24 @@ loadSpecified solConf name cs = do
c <- choose cs name
when (isNothing name && length cs > 1 && not solConf.quiet) $
putStrLn "Multiple contracts found, only analyzing the first"
unless solConf.quiet . putStrLn $ "Analyzing contract: " <> c ^. contractName . unpacked
unless solConf.quiet . putStrLn $ "Analyzing contract: " <> T.unpack c.contractName

-- Local variables
let SolConf ca d ads bala balc mcs pref _ _ libs _ fp dpc dpb ma tm _ ffi fs = solConf

-- generate the complete abi mapping
let bc = c._creationCode
abi = liftM2 (,) (view methodName) (fmap snd . view methodInputs) <$> toList (c._abiMap)
con = view constructorInputs c
let bc = c.creationCode
abi = Map.elems c.abiMap <&> \method -> (method.name, snd <$> method.inputs)
(tests, funs) = partition (isPrefixOf pref . fst) abi


-- Filter ABI according to the config options
let fabiOfc = if isDapptestMode tm then filterMethodsWithArgs (abiOf pref c) else filterMethods (c._contractName) fs $ abiOf pref c
let fabiOfc = if isDapptestMode tm then filterMethodsWithArgs (abiOf pref c) else filterMethods c.contractName fs $ abiOf pref c
-- Filter again for dapptest tests or assertions checking if enabled
let neFuns = filterMethods (c._contractName) fs (fallback NE.:| funs)
let neFuns = filterMethods c.contractName fs (fallback NE.:| funs)
-- Construct ABI mapping for World
let abiMapping = if ma then M.fromList $ cs <&> \cc -> (getBytecodeMetadata cc._runtimeCode, filterMethods (cc._contractName) fs $ abiOf pref cc)
else M.singleton (getBytecodeMetadata c._runtimeCode) fabiOfc
let abiMapping = if ma then M.fromList $ cs <&> \cc -> (getBytecodeMetadata cc.runtimeCode, filterMethods cc.contractName fs $ abiOf pref cc)
else M.singleton (getBytecodeMetadata c.runtimeCode) fabiOfc


-- Set up initial VM, either with chosen contract or Etheno initialization file
Expand All @@ -187,7 +189,8 @@ loadSpecified solConf name cs = do
blank' <- maybe (pure vm) (loadEthenoBatch ffi) fp
let blank = populateAddresses (Set.insert d ads) bala blank'

unless (null con || isJust fp) (throwM $ ConstructorArgs (show con))
unless (null c.constructorInputs || isJust fp) $
throwM $ ConstructorArgs (show c.constructorInputs)
-- Select libraries
ls <- mapM (choose cs . Just . T.pack) libs

Expand All @@ -197,12 +200,12 @@ loadSpecified solConf name cs = do
$ throwM NoTests
when (null abiMapping && isDapptestMode tm) -- < Dapptests checks
$ throwM NoTests
when (bc == mempty) $ throwM (NoBytecode c._contractName) -- Bytecode check
when (bc == mempty) $ throwM (NoBytecode c.contractName) -- Bytecode check
case find (not . null . snd) tests of
Just (t,_) -> throwM $ TestArgsFound t -- Test args check
Nothing -> do
-- dappinfo for debugging in case of failure
let di = dappInfo "/" (Map.fromList $ map (\x -> (x._contractName, x)) cs) mempty
let di = dappInfo "/" (Map.fromList $ map (\x -> (x.contractName, x)) cs) mempty

-- library deployment
vm0 <- deployContracts di (zip [addrLibrary ..] ls) d blank
Expand All @@ -225,12 +228,12 @@ loadSpecified solConf name cs = do

case vm4._result of
Just (VMFailure _) -> throwM SetUpCallFailed
_ -> return (vm4, unions $ map (view EVM.Solidity.eventMap) cs, neFuns, fst <$> tests, abiMapping)
_ -> return (vm4, unions $ map (.eventMap) cs, neFuns, fst <$> tests, abiMapping)

where choose [] _ = throwM NoContracts
choose (c:_) Nothing = return c
choose _ (Just n) = maybe (throwM $ ContractNotFound n) pure $
find (Data.Text.isSuffixOf (if T.any (== ':') n then n else ":" `append` n) . view contractName) cs
find (Data.Text.isSuffixOf (if T.any (== ':') n then n else ":" `append` n) . (.contractName)) cs
setUpFunction = ("setUp", [])


Expand Down Expand Up @@ -311,6 +314,6 @@ extremeConstants = concatMap (\i -> [mkSmallAbiInt i, mkLargeAbiInt i, mkLargeAb

returnTypes :: [SolcContract] -> Text -> Maybe AbiType
returnTypes cs t = do
method <- find ((== t) . view methodName) $ concatMap (toList . view abiMap) cs
(_, abiType) <- listToMaybe method._methodOutput
method <- find ((== t) . (.name)) $ concatMap (toList . (.abiMap)) cs
(_, abiType) <- listToMaybe method.output
pure abiType
2 changes: 1 addition & 1 deletion src/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ main = do
mapM_ (hPutStrLn stderr . ("Warning: unused option: " ++) . Aeson.Key.toString) ks

(v, sc, cs, w, ts, d, txs) <- prepareContract cfg cliFilePath cliSelectedContract g
let solcByName = fromList [(c._contractName, c) | c <- cs]
let solcByName = fromList [(c.contractName, c) | c <- cs]
-- TODO put in real path
let dappInfo' = dappInfo "/" solcByName sc
let env = Env { cfg = cfg, dapp = dappInfo' }
Expand Down
2 changes: 1 addition & 1 deletion src/test/Common.hs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ runContract :: FilePath -> Maybe ContractName -> EConfig -> IO Campaign
runContract f mc cfg = do
g <- getRandom
(v, sc, cs, w, ts, d, txs) <- prepareContract cfg (f :| []) mc g
let solcByName = fromList [(c._contractName, c) | c <- cs]
let solcByName = fromList [(c.contractName, c) | c <- cs]
let dappInfo' = dappInfo "/" solcByName sc
let env = Env { cfg = cfg, dapp = dappInfo' }
-- start ui and run tests
Expand Down
3 changes: 2 additions & 1 deletion stack.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ packages:

extra-deps:
- git: https://github.com/ethereum/hevm.git
commit: 2f498916e8d46966baa17472b16e5894b1adfc06
commit: 00b54a159d8c0b15dff30d790e89914e6488243f

- restless-git-0.7@sha256:346a5775a586f07ecb291036a8d3016c3484ccdc188b574bcdec0a82c12db293,968
- s-cargot-0.1.4.0@sha256:61ea1833fbb4c80d93577144870e449d2007d311c34d74252850bb48aa8c31fb,3525
- semver-range-0.2.8@sha256:44918080c220cf67b6e7c8ad16f01f3cfe1ac69d4f72e528e84d566348bb23c3,1941
- HSH-2.1.3@sha256:71ded11b224f5066373ce985ec63b10c87129850b33916736dd64fa2bea9ea0a,1705
- spool-0.1@sha256:77780cbfc2c0be23ff2ea9e474062f3df97fcd9db946ee0b3508280a923b83e2,1461
- smt2-parser-0.1.0.1@sha256:1e1a4565915ed851c13d1e6b8bb5185cf5d454da3b43170825d53e221f753d77,1421
- spawn-0.3@sha256:b91e01d8f2b076841410ae284b32046f91471943dc799c1af77d666c72101f02,1162

extra-include-dirs:
- /usr/local/opt/readline/include
Expand Down