Skip to content

Commit ba9b727

Browse files
committed
Update hevm to 0.51.2
1 parent d029295 commit ba9b727

24 files changed

+171
-149
lines changed

flake.nix

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@
4242
pkgs.haskellPackages.callCabal2nix "hevm" (pkgs.fetchFromGitHub {
4343
owner = "ethereum";
4444
repo = "hevm";
45-
rev = "release/0.50.5";
46-
sha256 = "sha256-Vi6kL1nJdujfS1oePwqks1owVPlS5Dd5hAn0r8Rpw+k=";
45+
rev = "d0ff524c0c3ae739834eb4866b439d945f449c5c";
46+
sha256 = "sha256-zXIab/ok5V8gxYuvcKEadNrNDdLuYpmJnbILinRJ/Qo=";
4747
}) { secp256k1 = pkgs.secp256k1; });
4848

4949
echidna = with pkgs; lib.pipe

lib/Echidna.hs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ import Data.Map.Strict qualified as Map
99
import Data.Set qualified as Set
1010
import System.FilePath ((</>))
1111

12-
import EVM hiding (Env)
12+
import EVM (cheatCode)
1313
import EVM.ABI (AbiValue(AbiAddress))
1414
import EVM.Solidity (SolcContract(..))
15+
import EVM.Types hiding (Env)
1516

1617
import Echidna.ABI
1718
import Echidna.Etheno (loadEtheno, extractFromEtheno)

lib/Echidna/Campaign.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ import Data.Set qualified as Set
2424
import Data.Text (Text)
2525
import System.Random (mkStdGen)
2626

27-
import EVM hiding (Env, Frame(state))
27+
import EVM (bytecode, cheatCode)
2828
import EVM.ABI (getAbi, AbiType(AbiAddressType), AbiValue(AbiAddress))
29-
import EVM.Types (Addr, Expr(ConcreteBuf))
29+
import EVM.Types hiding (Env, Frame(state))
3030

3131
import Echidna.ABI
3232
import Echidna.Exec

lib/Echidna/Config.hs

+1-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ import Data.Set qualified as Set
1313
import Data.Text (isPrefixOf)
1414
import Data.Yaml qualified as Y
1515

16-
import EVM (VM(..))
17-
import EVM.Types (W256)
16+
import EVM.Types (VM(..), W256)
1817

1918
import Echidna.Test
2019
import Echidna.Types.Campaign

lib/Echidna/Deploy.hs

+1-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ import Data.Either (fromRight)
1111
import Data.Text (Text, unlines)
1212
import Data.Text.Encoding (encodeUtf8)
1313

14-
import EVM hiding (bytecode, Env)
1514
import EVM.Solidity
16-
import EVM.Types (Addr)
15+
import EVM.Types hiding (Env)
1716

1817
import Echidna.Exec (execTx)
1918
import Echidna.Events (extractEvents)

lib/Echidna/Etheno.hs

+24-15
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import Control.Exception (Exception)
1111
import Control.Monad (void)
1212
import Control.Monad.Catch (MonadThrow, throwM)
1313
import Control.Monad.Fail qualified as M (MonadFail(..))
14-
import Control.Monad.State.Strict (MonadState, get, put, execStateT, gets)
14+
import Control.Monad.State.Strict (MonadState, get, put, execStateT, gets, modify', execState)
1515
import Data.Aeson (FromJSON(..), (.:), withObject, eitherDecodeFileStrict)
1616
import Data.ByteString.Base16 qualified as BS16 (decode)
1717
import Data.ByteString.Char8 (ByteString)
@@ -26,7 +26,7 @@ import Text.Read (readMaybe)
2626
import EVM
2727
import EVM.ABI (AbiType(..), AbiValue(..), decodeAbiValue, selector)
2828
import EVM.Exec (exec)
29-
import EVM.Types (Addr, W256, Expr(ConcreteBuf))
29+
import EVM.Types
3030

3131
import Echidna.Exec
3232
import Echidna.Transaction
@@ -168,19 +168,28 @@ execEthenoTxs :: (MonadState VM m, MonadFail m, MonadThrow m) => Etheno -> m ()
168168
execEthenoTxs et = do
169169
setupEthenoTx et
170170
vm <- get
171-
res <- fromEVM exec
172-
case (res, et) of
173-
(_ , AccountCreated _) -> pure ()
174-
(Reversion, _) -> void $ put vm
175-
(VMFailure (Query q), _) -> crashWithQueryError q et
176-
(VMFailure x, _) -> vmExcept x >> M.fail "impossible"
177-
(VMSuccess (ConcreteBuf bc),
178-
ContractCreated _ ca _ _ _ _) -> do
179-
#env % #contracts % at ca % _Just % #contractcode .= InitCode mempty mempty
180-
fromEVM $ do
181-
replaceCodeOfSelf (RuntimeCode (ConcreteRuntimeCode bc))
182-
loadContract ca
183-
_ -> pure ()
171+
runFully vm
172+
where
173+
runFully vm = do
174+
res <- fromEVM exec
175+
case (res, et) of
176+
(_ , AccountCreated _) -> pure ()
177+
(Reversion, _) -> void $ put vm
178+
(HandleEffect (Query (PleaseAskSMT (Lit c) _ continue)), _) -> do
179+
-- NOTE: this is not a real SMT query, we know it is concrete and can
180+
-- resume right away. It is done this way to support iterations counting
181+
-- in hevm.
182+
modify' $ execState (continue (Case (c > 0)))
183+
runFully vm
184+
(HandleEffect (Query q), _) -> crashWithQueryError q et
185+
(VMFailure x, _) -> vmExcept x >> M.fail "impossible"
186+
(VMSuccess (ConcreteBuf bc),
187+
ContractCreated _ ca _ _ _ _) -> do
188+
#env % #contracts % at ca % _Just % #contractcode .= InitCode mempty mempty
189+
fromEVM $ do
190+
replaceCodeOfSelf (RuntimeCode (ConcreteRuntimeCode bc))
191+
loadContract ca
192+
_ -> pure ()
184193

185194
-- | For an etheno txn, set up VM to execute txn
186195
setupEthenoTx :: MonadState VM m => Etheno -> m ()

lib/Echidna/Events.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ import Data.Tree (flatten)
1313
import Data.Tree.Zipper (fromForest, TreePos, Empty)
1414
import Data.Vector (fromList)
1515

16-
import EVM hiding (code)
16+
import EVM (traceForest)
1717
import EVM.ABI (Event(..), Indexed(..), decodeAbiValue, AbiType(..), AbiValue(..))
1818
import EVM.Dapp (DappContext(..), DappInfo(..))
1919
import EVM.Format (showValues, showError, contractNamePart)
2020
import EVM.Solidity (SolcContract(..))
21-
import EVM.Types (Expr(ConcreteBuf), W256, maybeLitWord)
21+
import EVM.Types
2222

2323
import Echidna.Types.Buffer (forceLit, forceBuf)
2424
import Data.ByteString (ByteString)

lib/Echidna/Exec.hs

+19-8
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ import Data.Vector qualified as V
2222
import Data.Vector.Unboxed.Mutable qualified as VMut
2323
import System.Process (readProcessWithExitCode)
2424

25-
import EVM hiding (Env)
25+
import EVM (bytecode, replaceCodeOfSelf, loadContract, exec1, vmOpIx)
2626
import EVM.ABI
2727
import EVM.Exec (exec, vmForEthrunCreation)
2828
import EVM.Fetch qualified
29-
import EVM.Types (Expr(ConcreteBuf, Lit), hexText)
29+
import EVM.Format (hexText)
30+
import EVM.Types hiding (Env)
3031

3132
import Echidna.Events (emptyEvents)
3233
import Echidna.RPC (safeFetchContractFrom, safeFetchSlotFrom)
@@ -43,7 +44,7 @@ import Echidna.Utility (getTimestamp, timePrefix)
4344
data ErrorClass = RevertE | IllegalE | UnknownE
4445

4546
-- | Given an execution error, classify it. Mostly useful for nice @pattern@s ('Reversion', 'Illegal').
46-
classifyError :: Error -> ErrorClass
47+
classifyError :: EvmError -> ErrorClass
4748
classifyError = \case
4849
OutOfGas _ _ -> RevertE
4950
Revert _ -> RevertE
@@ -56,8 +57,8 @@ classifyError = \case
5657

5758
-- | Extracts the 'Query' if there is one.
5859
getQuery :: VMResult -> Maybe Query
59-
getQuery (VMFailure (Query q)) = Just q
60-
getQuery _ = Nothing
60+
getQuery (HandleEffect (Query q)) = Just q
61+
getQuery _ = Nothing
6162

6263
-- | Matches execution errors that just cause a reversion.
6364
pattern Reversion :: VMResult
@@ -68,7 +69,7 @@ pattern Illegal :: VMResult
6869
pattern Illegal <- VMFailure (classifyError -> IllegalE)
6970

7071
-- | Given an execution error, throw the appropriate exception.
71-
vmExcept :: MonadThrow m => Error -> m ()
72+
vmExcept :: MonadThrow m => EvmError -> m ()
7273
vmExcept e = throwM $
7374
case VMFailure e of {Illegal -> IllegalExec e; _ -> UnknownFailure e}
7475

@@ -77,7 +78,7 @@ vmExcept e = throwM $
7778
execTxWith
7879
:: (MonadIO m, MonadState s m, MonadReader Env m)
7980
=> Lens' s VM
80-
-> (Error -> m ())
81+
-> (EvmError -> m ())
8182
-> m VMResult
8283
-> Tx
8384
-> m (VMResult, Gas)
@@ -120,7 +121,7 @@ execTxWith l onErr executeTx tx = do
120121
ret <- liftIO $ safeFetchContractFrom rpcBlock rpcUrl addr
121122
case ret of
122123
-- TODO: fix hevm to not return an empty contract in case of an error
123-
Just contract | contract.contractcode /= EVM.RuntimeCode (EVM.ConcreteRuntimeCode "") -> do
124+
Just contract | contract.contractcode /= RuntimeCode (ConcreteRuntimeCode "") -> do
124125
metaCacheRef <- asks (.metadataCache)
125126
metaCache <- liftIO $ readIORef metaCacheRef
126127
let bc = forceBuf (contract ^. bytecode)
@@ -182,6 +183,16 @@ execTxWith l onErr executeTx tx = do
182183
l %= execState (continuation encodedResponse)
183184
runFully
184185

186+
Just (PleaseAskSMT (Lit c) _ continue) -> do
187+
-- NOTE: this is not a real SMT query, we know it is concrete and can
188+
-- resume right away. It is done this way to support iterations counting
189+
-- in hevm.
190+
l %= execState (continue (Case (c > 0)))
191+
runFully
192+
193+
Just q@(PleaseAskSMT {}) ->
194+
error $ "Unexpected SMT query: " <> show q
195+
185196
-- No queries to answer, the tx is fully executed and the result is final
186197
_ -> pure vmResult
187198

lib/Echidna/Output/Source.hs

+12-13
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
{-# LANGUAGE ViewPatterns #-}
2+
13
module Echidna.Output.Source where
24

35
import Prelude hiding (writeFile)
46

57
import Data.Aeson (ToJSON(..), FromJSON(..), withText)
8+
import Data.ByteString qualified as BS
69
import Data.Foldable
710
import Data.List (nub, sort)
811
import Data.Maybe (fromMaybe, mapMaybe)
@@ -28,8 +31,6 @@ import Echidna.Types.Coverage (OpIx, unpackTxResults, CoverageMap)
2831
import Echidna.Types.Tx (TxResult(..))
2932
import Echidna.Types.Signature (getBytecodeMetadata)
3033

31-
type FilePathText = Text
32-
3334
saveCoverages
3435
:: [CoverageFileType]
3536
-> Int
@@ -78,20 +79,18 @@ coverageFileExtension Txt = ".txt"
7879
ppCoveredCode :: CoverageFileType -> SourceCache -> [SolcContract] -> CoverageMap -> IO Text
7980
ppCoveredCode fileType sc cs s | null s = pure "Coverage map is empty"
8081
| otherwise = do
81-
let allFiles = zipWith (\(srcPath, _rawSource) srcLines -> (srcPath, V.map decodeUtf8 srcLines))
82-
sc.files
83-
sc.lines
84-
-- ^ Collect all the possible lines from all the files
82+
-- List of covered lines during the fuzzing campaing
8583
covLines <- srcMapCov sc s cs
86-
-- ^ List of covered lines during the fuzzing campaing
8784
let
85+
-- Collect all the possible lines from all the files
86+
allFiles = (\(path, src) -> (path, V.fromList (decodeUtf8 <$> BS.split 0xa src))) <$> Map.elems sc.files
87+
-- Excludes lines such as comments or blanks
8888
runtimeLinesMap = buildRuntimeLinesMap sc cs
89-
-- ^ Excludes lines such as comments or blanks
89+
-- Pretty print individual file coverage
9090
ppFile (srcPath, srcLines) =
9191
let runtimeLines = fromMaybe mempty $ Map.lookup srcPath runtimeLinesMap
9292
marked = markLines fileType srcLines runtimeLines (fromMaybe Map.empty (Map.lookup srcPath covLines))
9393
in T.unlines (changeFileName srcPath : changeFileLines (V.toList marked))
94-
-- ^ Pretty print individual file coverage
9594
topHeader = case fileType of
9695
Lcov -> "TN:\n"
9796
Html -> "<style> code { white-space: pre-wrap; display: block; background-color: #eee; }" <>
@@ -102,7 +101,7 @@ ppCoveredCode fileType sc cs s | null s = pure "Coverage map is empty"
102101
"</style>"
103102
Txt -> ""
104103
-- ^ Text to add to top of the file
105-
changeFileName fn = case fileType of
104+
changeFileName (T.pack -> fn) = case fileType of
106105
Lcov -> "SF:" <> fn
107106
Html -> "<b>" <> HTML.text fn <> "</b>"
108107
Txt -> fn
@@ -158,11 +157,11 @@ getMarker ErrorOutOfGas = 'o'
158157
getMarker _ = 'e'
159158

160159
-- | Given a source cache, a coverage map, a contract returns a list of covered lines
161-
srcMapCov :: SourceCache -> CoverageMap -> [SolcContract] -> IO (Map FilePathText (Map Int [TxResult]))
160+
srcMapCov :: SourceCache -> CoverageMap -> [SolcContract] -> IO (Map FilePath (Map Int [TxResult]))
162161
srcMapCov sc covMap contracts = do
163162
Map.unionsWith Map.union <$> mapM linesCovered contracts
164163
where
165-
linesCovered :: SolcContract -> IO (Map Text (Map Int [TxResult]))
164+
linesCovered :: SolcContract -> IO (Map FilePath (Map Int [TxResult]))
166165
linesCovered c =
167166
case Map.lookup (getBytecodeMetadata c.runtimeCode) covMap of
168167
Just vec -> VU.foldl' (\acc covInfo -> case covInfo of
@@ -193,7 +192,7 @@ srcMapForOpLocation contract opIx =
193192

194193
-- | Builds a Map from file paths to lines that can be executed, this excludes
195194
-- for example lines with comments
196-
buildRuntimeLinesMap :: SourceCache -> [SolcContract] -> Map Text (S.Set Int)
195+
buildRuntimeLinesMap :: SourceCache -> [SolcContract] -> Map FilePath (S.Set Int)
197196
buildRuntimeLinesMap sc contracts =
198197
Map.fromListWith (<>)
199198
[(k, S.singleton v) | (k, v) <- mapMaybe (srcMapCodePos sc) srcMaps]

lib/Echidna/RPC.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ import Network.HTTP.Simple (HttpException)
1414
import System.Environment
1515
import Text.Read (readMaybe)
1616

17-
import EVM (Contract(..), ContractCode(RuntimeCode), RuntimeCode (..), initialContract)
17+
import EVM (initialContract)
1818
import EVM.Fetch qualified
19-
import EVM.Types (Addr, W256)
19+
import EVM.Types
2020

2121
import Echidna.Orphans.JSON ()
2222
import Echidna.Types (emptyAccount)

lib/Echidna/Shrink.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Control.Monad.State.Strict (MonadIO)
88
import Data.Set qualified as Set
99
import Data.List qualified as List
1010

11-
import EVM (VM)
11+
import EVM.Types (VM)
1212

1313
import Echidna.Events (extractEvents)
1414
import Echidna.Exec

0 commit comments

Comments
 (0)