Skip to content

Commit

Permalink
hevm 0.50.5 (#1023)
Browse files Browse the repository at this point in the history
  • Loading branch information
arcz authored Apr 18, 2023
1 parent f7112fc commit 2f067ec
Show file tree
Hide file tree
Showing 20 changed files with 145 additions and 134 deletions.
10 changes: 9 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,16 @@
'';
};

hevm = pkgs.haskell.lib.dontCheck (
pkgs.haskellPackages.callCabal2nix "hevm" (pkgs.fetchFromGitHub {
owner = "ethereum";
repo = "hevm";
rev = "release/0.50.5";
sha256 = "sha256-Vi6kL1nJdujfS1oePwqks1owVPlS5Dd5hAn0r8Rpw+k=";
}) { secp256k1 = pkgs.secp256k1; });

echidna = with pkgs; lib.pipe
(haskellPackages.callCabal2nix "echidna" ./. { })
(haskellPackages.callCabal2nix "echidna" ./. { inherit hevm; })
[
(haskell.lib.compose.addTestToolDepends [ haskellPackages.hpack slither-analyzer solc ])
(haskell.lib.compose.disableCabalFlag "static")
Expand Down
6 changes: 3 additions & 3 deletions lib/Echidna.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Data.Map.Strict qualified as Map
import Data.Set qualified as Set
import System.FilePath ((</>))

import EVM hiding (Env, env, contracts)
import EVM hiding (Env)
import EVM.ABI (AbiValue(AbiAddress))
import EVM.Solidity (SolcContract(..))

Expand Down Expand Up @@ -62,13 +62,13 @@ prepareContract env contracts solFiles specifiedContract seed = do
echidnaTests = createTests solConf.testMode
solConf.testDestruction
testNames
vm._state._contract
vm.state.contract
funs

eventMap = Map.unions $ map (.eventMap) contracts
world = mkWorld solConf eventMap signatureMap specifiedContract slitherInfo

deployedAddresses = Set.fromList $ AbiAddress <$> Map.keys vm._env._contracts
deployedAddresses = Set.fromList $ AbiAddress <$> Map.keys vm.env.contracts
constants = enhanceConstants slitherInfo
<> timeConstants
<> extremeConstants
Expand Down
6 changes: 3 additions & 3 deletions lib/Echidna/ABI.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import Data.Word (Word8)
import Numeric (showHex)

import EVM.ABI hiding (genAbiValue)
import EVM.Types (Addr, abiKeccak, W256)
import EVM.Types (Addr, abiKeccak, W256, FunctionSelector(..))

import Echidna.Mutator.Array (mutateLL, replaceAt)
import Echidna.Types.Random
Expand Down Expand Up @@ -95,7 +95,7 @@ encodeSigWithName cn (n, ts) =
last (T.split (==':') cn) <> "." <> n <> "(" <> T.intercalate "," (abiTypeSolidity <$> ts) <> ")"

-- | Get the signature of a solidity method
hashSig :: Text -> FunctionHash
hashSig :: Text -> FunctionSelector
hashSig = abiKeccak . TE.encodeUtf8

-- | Configuration necessary for generating new 'SolCall's. Don't construct this
Expand Down Expand Up @@ -377,5 +377,5 @@ genInteractionsM genDict solSignatures =

abiCalldata :: Text -> Vector AbiValue -> ByteString
abiCalldata s xs = BSLazy.toStrict . runPut $ do
putWord32be (abiKeccak (encodeUtf8 s))
putWord32be (abiKeccak (encodeUtf8 s)).unFunctionSelector
putAbi (AbiTuple xs)
13 changes: 7 additions & 6 deletions lib/Echidna/Campaign.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

module Echidna.Campaign where

import Optics.Core

import Control.DeepSeq (force)
import Control.Lens
import Control.Monad (foldM, replicateM, when, unless, void)
import Control.Monad.Catch (MonadCatch(..), MonadThrow(..))
import Control.Monad.Random.Strict (MonadRandom, RandT, evalRandT)
Expand All @@ -23,8 +24,8 @@ import Data.Set qualified as Set
import Data.Text (Text)
import System.Random (mkStdGen)

import EVM (Contract, VM(..), VMResult(..), bytecode, cheatCode)
import EVM qualified (Env(..))
import EVM hiding (Env, Frame(state), VM(state))
import EVM (VM)
import EVM.ABI (getAbi, AbiType(AbiAddressType), AbiValue(AbiAddress))
import EVM.Types (Addr, Expr(ConcreteBuf))

Expand Down Expand Up @@ -104,7 +105,7 @@ runCampaign callback vm world tests dict initialCorpus = do
metaCacheRef <- asks (.metadataCache)
fetchContractCacheRef <- asks (.fetchContractCache)
external <- liftIO $ Map.mapMaybe id <$> readIORef fetchContractCacheRef
liftIO $ writeIORef metaCacheRef (mkMemo (vm._env._contracts <> external))
liftIO $ writeIORef metaCacheRef (mkMemo (vm.env.contracts <> external))

let
covMap = fromMaybe mempty conf.knownCoverage
Expand Down Expand Up @@ -145,7 +146,7 @@ runCampaign callback vm world tests dict initialCorpus = do
| otherwise ->
void $ lift callback

fuzz = randseq vm._env._contracts world >>= callseq vm
fuzz = randseq vm.env.contracts world >>= callseq vm

continue = do
runUpdate (shrinkTest vm)
Expand Down Expand Up @@ -215,7 +216,7 @@ callseq vm txSeq = do

let
-- compute the addresses not present in the old VM via set difference
newAddrs = Map.keys $ vm'._env._contracts \\ vm._env._contracts
newAddrs = Map.keys $ vm'.env.contracts \\ vm.env.contracts
-- and construct a set to union to the constants table
diffs = Map.fromList [(AbiAddressType, Set.fromList $ AbiAddress <$> newAddrs)]
-- Now we try to parse the return values as solidity constants, and add then to the 'GenDict'
Expand Down
2 changes: 1 addition & 1 deletion lib/Echidna/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ instance FromJSON EConfigWithUsage where
psender <- v ..:? "psender" ..!= 0x10000
fprefix <- v ..:? "prefix" ..!= "echidna_"
let goal fname = if (fprefix <> "revert_") `isPrefixOf` fname then ResRevert else ResTrue
classify fname vm = maybe ResOther classifyRes vm._result == goal fname
classify fname vm = maybe ResOther classifyRes vm.result == goal fname
pure $ TestConf classify (const psender)

campaignConfParser = CampaignConf
Expand Down
2 changes: 1 addition & 1 deletion lib/Echidna/Deploy.hs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ deployBytecodes' cs src initialVM = foldM deployOne initialVM cs
deployOne vm (dst, bytecode) = do
vm' <- flip execStateT vm $
execTx $ createTx (bytecode <> zeros) src dst unlimitedGasPerBlock (0, 0)
case vm'._result of
case vm'.result of
Just (VMSuccess _) -> pure vm'
_ -> do
di <- asks (.dapp)
Expand Down
16 changes: 9 additions & 7 deletions lib/Echidna/Etheno.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ module Echidna.Etheno where

import Prelude hiding (Word)

import Optics.Core
import Optics.State.Operators

import Control.Exception (Exception)
import Control.Lens
import Control.Monad (void)
import Control.Monad.Catch (MonadThrow, throwM)
import Control.Monad.Fail qualified as M (MonadFail(..))
import Control.Monad.State.Strict (MonadState, get, put, execStateT)
import Control.Monad.State.Strict (MonadState, get, put, execStateT, gets)
import Data.Aeson (FromJSON(..), (.:), withObject, eitherDecodeFileStrict)
import Data.ByteString.Base16 qualified as BS16 (decode)
import Data.ByteString.Char8 (ByteString)
Expand Down Expand Up @@ -130,14 +132,14 @@ loadEthenoBatch ffi fp = do

initAddress :: MonadState VM m => Addr -> m ()
initAddress addr = do
cs <- use (env . EVM.contracts)
cs <- gets (.env.contracts)
if addr `member` cs then pure ()
else env . EVM.contracts . at addr .= Just account
else #env % #contracts % at addr .= Just account
where
account =
initialContract (RuntimeCode (ConcreteRuntimeCode mempty))
& set nonce 0
& set balance 100000000000000000000 -- default balance for EOAs in etheno
& set #nonce 0
& set #balance 100000000000000000000 -- default balance for EOAs in etheno

crashWithQueryError
:: (MonadState VM m, MonadFail m, MonadThrow m)
Expand Down Expand Up @@ -174,7 +176,7 @@ execEthenoTxs et = do
(VMFailure x, _) -> vmExcept x >> M.fail "impossible"
(VMSuccess (ConcreteBuf bc),
ContractCreated _ ca _ _ _ _) -> do
env . contracts . at ca . _Just . contractcode .= InitCode mempty mempty
#env % #contracts % at ca % _Just % #contractcode .= InitCode mempty mempty
fromEVM $ do
replaceCodeOfSelf (RuntimeCode (ConcreteRuntimeCode bc))
loadContract ca
Expand Down
8 changes: 4 additions & 4 deletions lib/Echidna/Events.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ extractEvents decodeErrors dappInfo vm =
++ catMaybes (concatMap flatten (fmap (fmap showTrace) forest))
where
showTrace trace =
let ?context = DappContext { info = dappInfo, env = vm._env._contracts } in
let codehash' = fromJust $ maybeLitWord trace._traceContract._codehash
let ?context = DappContext { info = dappInfo, env = vm.env.contracts } in
let codehash' = fromJust $ maybeLitWord trace.contract.codehash
maybeContractName = maybeContractNameFromCodeHash dappInfo codehash'
in case trace._traceData of
in case trace.tracedata of
EventTrace addr bytes (topic:_) ->
case Map.lookup (forceLit topic) dappInfo.eventMap of
Just (Event name _ types) ->
Expand Down Expand Up @@ -78,7 +78,7 @@ maybeContractNameFromCodeHash info codeHash = contractToName <$> maybeContract

decodeRevert :: Bool -> VM -> Maybe Text
decodeRevert decodeErrors vm =
case vm._result of
case vm.result of
Just (VMFailure (Revert (ConcreteBuf bs))) -> decodeRevertMsg decodeErrors bs
_ -> Nothing

Expand Down
55 changes: 29 additions & 26 deletions lib/Echidna/Exec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

module Echidna.Exec where

import Control.Lens
import Control.Monad (forM_, when)
import Optics.Core
import Optics.State
import Optics.State.Operators

import Control.Monad (when, forM_)
import Control.Monad.Catch (MonadThrow(..))
import Control.Monad.State.Strict (MonadState(get, put), execState, runStateT, MonadIO(liftIO))
import Control.Monad.Reader (MonadReader, asks)
Expand All @@ -19,7 +22,7 @@ import Data.Vector qualified as V
import Data.Vector.Unboxed.Mutable qualified as V
import System.Process (readProcessWithExitCode)

import EVM hiding (pc, Env, cache, contract, tx, value)
import EVM hiding (Env)
import EVM.ABI
import EVM.Exec (exec, vmForEthrunCreation)
import EVM.Fetch qualified
Expand Down Expand Up @@ -84,12 +87,12 @@ execTxWith l onErr executeTx tx = do
if hasSelfdestructed vm tx.dst then
pure (VMFailure (Revert (ConcreteBuf "")), 0)
else do
l . traces .= emptyEvents
l % #traces .= emptyEvents
vmBeforeTx <- use l
l %= execState (setupTx tx)
gasLeftBeforeTx <- use $ l . state . gas
gasLeftBeforeTx <- use $ l % #state % #gas
vmResult <- runFully
gasLeftAfterTx <- use $ l . state . gas
gasLeftAfterTx <- use $ l % #state % #gas
handleErrorsAndConstruction vmResult vmBeforeTx
pure (vmResult, gasLeftBeforeTx - gasLeftAfterTx)
where
Expand Down Expand Up @@ -118,7 +121,7 @@ execTxWith l onErr executeTx tx = do
ret <- liftIO $ safeFetchContractFrom rpcBlock rpcUrl addr
case ret of
-- TODO: fix hevm to not return an empty contract in case of an error
Just contract | contract._contractcode /= EVM.RuntimeCode (EVM.ConcreteRuntimeCode "") -> do
Just contract | contract.contractcode /= EVM.RuntimeCode (EVM.ConcreteRuntimeCode "") -> do
metaCacheRef <- asks (.metadataCache)
metaCache <- liftIO $ readIORef metaCacheRef
let bc = forceBuf (contract ^. bytecode)
Expand Down Expand Up @@ -187,25 +190,25 @@ execTxWith l onErr executeTx tx = do
-- (`vmResult`) of executing transaction `tx`.
handleErrorsAndConstruction vmResult vmBeforeTx = case (vmResult, tx.call) of
(Reversion, _) -> do
tracesBeforeVMReset <- use $ l . traces
codeContractBeforeVMReset <- use $ l . state . codeContract
calldataBeforeVMReset <- use $ l . state . calldata
callvalueBeforeVMReset <- use $ l . state . callvalue
tracesBeforeVMReset <- use $ l % #traces
codeContractBeforeVMReset <- use $ l % #state % #codeContract
calldataBeforeVMReset <- use $ l % #state % #calldata
callvalueBeforeVMReset <- use $ l % #state % #callvalue
-- If a transaction reverts reset VM to state before the transaction.
l .= vmBeforeTx
-- Undo reset of some of the VM state.
-- Otherwise we'd loose all information about the reverted transaction like
-- contract address, calldata, result and traces.
l . result ?= vmResult
l . state . calldata .= calldataBeforeVMReset
l . state . callvalue .= callvalueBeforeVMReset
l . traces .= tracesBeforeVMReset
l . state . codeContract .= codeContractBeforeVMReset
l % #result ?= vmResult
l % #state % #calldata .= calldataBeforeVMReset
l % #state % #callvalue .= callvalueBeforeVMReset
l % #traces .= tracesBeforeVMReset
l % #state % #codeContract .= codeContractBeforeVMReset
(VMFailure x, _) -> onErr x
(VMSuccess (ConcreteBuf bytecode'), SolCreate _) ->
-- Handle contract creation.
l %= execState (do
env . contracts . at tx.dst . _Just . contractcode .= InitCode mempty mempty
#env % #contracts % at tx.dst % _Just % #contractcode .= InitCode mempty mempty
replaceCodeOfSelf (RuntimeCode (ConcreteRuntimeCode bytecode'))
loadContract tx.dst)
_ -> pure ()
Expand All @@ -224,7 +227,7 @@ execTx
:: (MonadIO m, MonadState VM m, MonadReader Env m, MonadThrow m)
=> Tx
-> m (VMResult, Gas)
execTx = execTxWith id vmExcept $ fromEVM exec
execTx = execTxWith equality' vmExcept $ fromEVM exec

-- | A type alias for the context we carry while executing instructions
type CoverageContext = (CoverageMap, Bool, Maybe (BS.ByteString, Int))
Expand Down Expand Up @@ -267,7 +270,7 @@ execTxWithCov tx cov = do

-- | Repeatedly exec a step and add coverage until we have an end result
loop :: MetadataCache -> VM -> CoverageContext -> IO (VMResult, VM, CoverageContext)
loop cache !vm !cc = case vm._result of
loop cache !vm !cc = case vm.result of
Nothing -> addCoverage cache vm cc >>= loop cache (stepVM vm)
Just r -> pure (r, vm, cc)

Expand All @@ -283,7 +286,7 @@ execTxWithCov tx cov = do
case Map.lookup meta cm of
Nothing -> do
let size = BS.length . forceBuf . view bytecode . fromJust $
Map.lookup vm._state._contract vm._env._contracts
Map.lookup vm.state.contract vm.env.contracts
if size > 0 then do
vec <- V.new size
-- We use -1 for opIx to indicate that the location was not covered
Expand All @@ -304,17 +307,17 @@ execTxWithCov tx cov = do
pure (cm, new, Just (meta, pc))

-- | Get the VM's current execution location
currentCovLoc vm = (vm._state._pc, fromMaybe 0 $ vmOpIx vm, length vm._frames)
currentCovLoc vm = (vm.state.pc, fromMaybe 0 $ vmOpIx vm, length vm.frames)

-- | Get the current contract's bytecode metadata
currentMeta cache vm = fromMaybe (error "no contract information on coverage") $ do
buffer <- vm ^? env . contracts . at vm._state._contract . _Just . bytecode
buffer <- vm ^? #env % #contracts % at vm.state.contract % _Just % bytecode
let bc = forceBuf buffer
pure $ lookupBytecodeMetadata cache bc

initialVM :: Bool -> VM
initialVM ffi = vmForEthrunCreation mempty
& block . timestamp .~ Lit initialTimestamp
& block . number .~ initialBlockNumber
& env . contracts .~ mempty -- fixes weird nonce issues
& allowFFI .~ ffi
& #block % #timestamp .~ Lit initialTimestamp
& #block % #number .~ initialBlockNumber
& #env % #contracts .~ mempty -- fixes weird nonce issues
& #allowFFI .~ ffi
6 changes: 3 additions & 3 deletions lib/Echidna/Processor.hs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ import System.Process (StdStream(..), readCreateProcessWithExitCode, proc, std_e
import Text.Read (readMaybe)

import EVM.ABI (AbiValue(..))
import EVM.Types (Addr(..))
import EVM.Types (Addr(..), FunctionSelector)

import Echidna.ABI (hashSig, makeNumAbiValues, makeArrayAbiValues)
import Echidna.Types.Signature (ContractName, FunctionName, FunctionHash)
import Echidna.Types.Signature (ContractName, FunctionName)
import Echidna.Types.Solidity (SolConf(..))
import Echidna.Utility (measureIO)

Expand All @@ -46,7 +46,7 @@ instance Exception ProcException

-- | This function is used to filter the lists of function names according to the supplied
-- contract name (if any) and returns a list of hashes
filterResults :: Maybe ContractName -> Map ContractName [FunctionName] -> [FunctionHash]
filterResults :: Maybe ContractName -> Map ContractName [FunctionName] -> [FunctionSelector]
filterResults (Just c) rs =
case Map.lookup c rs of
Nothing -> filterResults Nothing rs
Expand Down
Loading

0 comments on commit 2f067ec

Please sign in to comment.