forked from crytic/echidna
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Main.hs
93 lines (78 loc) · 3.07 KB
/
Main.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
{-# LANGUAGE FlexibleContexts, OverloadedStrings, TupleSections, DoAndIfThenElse #-}
module Main where
import Control.Lens hiding (argument)
import Control.Concurrent.MVar (newMVar, readMVar, swapMVar)
import Control.Monad (forM, replicateM_)
import Control.Monad.Catch (MonadThrow(..))
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Reader (runReaderT)
import Data.List (foldl')
import Data.Set (unions, size)
import Data.Text (pack)
import Data.Semigroup ((<>))
import Echidna.Config
import Echidna.Coverage (ePropertySeqCoverage, getCover)
import Echidna.Exec
import Echidna.Solidity
import Hedgehog hiding (checkParallel)
import Hedgehog.Internal.Property (GroupName(..), PropertyName(..))
import Options.Applicative
data Options = Options
{ filePath :: FilePath
, selectedContract :: Maybe String
, coverageSelector :: Bool
, configFilepath :: Maybe FilePath
}
options :: Parser Options
options = Options
<$> argument str
( metavar "FILE"
<> help "Solidity file to analyze" )
<*> optional ( argument str
( metavar "CONTRACT"
<> help "Contract inside of file to analyze" ))
<*> switch
( long "coverage"
<> help "Turn on coverage")
<*> optional ( option str
( long "config"
<> help "Echidna config file" ))
opts :: ParserInfo Options
opts = info (options <**> helper)
( fullDesc
<> progDesc "Fuzzing/property based testing of EVM code"
<> header "Echidna - Ethereum fuzz testing framework" )
main :: IO ()
main = do
-- Read cmd line options and load config
(Options file contract usecov configFile) <- execParser opts
config <- maybe (pure defaultConfig) parseConfig configFile
let f = checkTest (config ^. returnType)
checkGroup = if config ^. outputJson
then
checkParallelJson
else
checkParallel
flip runReaderT config $ do
-- Load solidity contract and get VM
(v,a,ts) <- loadSolidity file (pack <$> contract)
if null ts
then throwM NoTests
else pure ()
if not $ usecov || config ^. printCoverage
-- Run without coverage
then do
let prop t = ePropertySeq (`f` t) a v >>= \x -> return (PropertyName $ show t, x)
_ <- checkGroup . Group (GroupName file) =<< mapM prop ts
return ()
-- Run with coverage
else do
tests <- liftIO $ mapM (\t -> fmap (t,) (newMVar [])) ts
let prop (cov,t,mvar) =
ePropertySeqCoverage cov mvar (`f` t) a v >>= \x -> return (PropertyName $ show t, x)
replicateM_ (config ^. epochs) $ do
xs <- liftIO $ forM tests $ \(x,y) -> swapMVar y [] <&> (, x, y) . getCover
checkGroup . Group (GroupName file) =<< mapM prop xs
ls <- liftIO $ mapM (readMVar . snd) tests
let ci = foldl' (\acc xs -> unions (acc : map snd xs)) mempty ls
liftIO . putStrLn $ "Coverage: " ++ show (size ci) ++ " unique PC's"