From 46f98a6e84160f99c159c1fa0340696703a3bad2 Mon Sep 17 00:00:00 2001 From: Divya Ranjan Pattanaik Date: Wed, 20 Aug 2025 15:57:53 +0000 Subject: [PATCH] refactor: add changes from #1100 --- lib/Echidna/Utility.hs | 4 ++++ package.yaml | 1 + src/Main.hs | 46 +++++++++++++++++++++++++++++++++--------- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/lib/Echidna/Utility.hs b/lib/Echidna/Utility.hs index c73d855a8..8fd46701c 100644 --- a/lib/Echidna/Utility.hs +++ b/lib/Echidna/Utility.hs @@ -4,6 +4,7 @@ import Control.Monad (unless) import Control.Monad.Catch (bracket) import Data.Time (diffUTCTime, getCurrentTime, zonedTimeToLocalTime, LocalTime, getZonedTime) import Data.Time.Format (defaultTimeLocale, formatTime) +import Language.Haskell.TH import System.Directory (getDirectoryContents, getCurrentDirectory, setCurrentDirectory) import System.IO (hFlush, stdout) @@ -38,3 +39,6 @@ withCurrentDirectory dir action = bracket getCurrentDirectory setCurrentDirectory $ \_ -> do setCurrentDirectory dir action + +includeFile :: FilePath -> Q Exp +includeFile fp = LitE . StringL <$> runIO (readFile fp) diff --git a/package.yaml b/package.yaml index 99bf5e138..5587f6a9a 100644 --- a/package.yaml +++ b/package.yaml @@ -67,6 +67,7 @@ library: - signal - split - strip-ansi-escape + - template-haskell - time - unliftio - unliftio-core diff --git a/src/Main.hs b/src/Main.hs index 5839e8db7..513396eb5 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -1,4 +1,5 @@ {-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TemplateHaskell #-} module Main where @@ -47,11 +48,27 @@ import Echidna.Types.Solidity import Echidna.Types.Test (TestMode, EchidnaTest(..)) import Echidna.UI import Echidna.UI.Report (ppFailWithTraces, ppTestName) -import Echidna.Utility (measureIO) +import Echidna.Utility (includeFile, measureIO) main :: IO () main = withUtf8 $ withCP65001 $ do - opts@Options{..} <- execParser optsParser + cli <- execParser cliParser + case cli of + InitCommand -> do + let config = "echidna.yaml" + configExists <- doesFileExist config + if configExists + then do + putStrLn $ "Config file " <> config <> " already exists." + exitWith (ExitFailure 1) + else do + writeFile config $(includeFile "tests/solidity/basic/default.yaml") + putStrLn $ "Sample config file written to " <> config + FuzzCommand fuzzOpts -> + fuzz fuzzOpts + +fuzz :: FuzzOptions -> IO () +fuzz opts@FuzzOptions {..} = do EConfigWithUsage loadedCfg ks _ <- maybe (pure (EConfigWithUsage defaultConfig mempty mempty)) parseConfig cliConfigFilepath cfg <- overrideConfig loadedCfg opts @@ -118,7 +135,11 @@ main = withUtf8 $ withCP65001 $ do if isSuccessful tests then exitSuccess else exitWith (ExitFailure 1) -data Options = Options +data CLI + = InitCommand + | FuzzCommand FuzzOptions + +data FuzzOptions = FuzzOptions { cliFilePath :: NE.NonEmpty FilePath , cliWorkers :: Maybe Word8 , cliServerPort :: Maybe Word16 @@ -147,8 +168,8 @@ data Options = Options , cliSymExecNSolvers :: Maybe Int } -optsParser :: ParserInfo Options -optsParser = info (helper <*> versionOption <*> options) $ fullDesc +cliParser :: ParserInfo CLI +cliParser = info (helper <*> versionOption <*> commands) $ fullDesc <> progDesc "EVM property-based testing framework" <> header "Echidna" @@ -158,8 +179,15 @@ bool = maybeReader (f . map toLower) where f "false" = Just False f _ = Nothing -options :: Parser Options -options = Options . NE.fromList + where + commands = subparser $ + command "init" (info (pure InitCommand) + (progDesc "Write a sample config file to echidna.yaml")) + <> command "fuzz" (info (FuzzCommand <$> fuzzOptions) + (progDesc "Run fuzzing")) + +fuzzOptions :: Parser FuzzOptions +fuzzOptions = FuzzOptions <$> some (argument str (metavar "FILES" <> help "Solidity files to analyze")) <*> optional (option auto $ long "workers" @@ -240,8 +268,8 @@ versionOption = infoOption ("Echidna " ++ showVersion version) (long "version" <> help "Show version") -overrideConfig :: EConfig -> Options -> IO EConfig -overrideConfig config Options{..} = do +overrideConfig :: EConfig -> FuzzOptions -> IO EConfig +overrideConfig config FuzzOptions{..} = do envRpcUrl <- Onchain.rpcUrlEnv envRpcBlock <- Onchain.rpcBlockEnv envEtherscanApiKey <- Onchain.etherscanApiKey