diff --git a/gastest/Main.hs b/gastest/Main.hs new file mode 100644 index 000000000..20d9d6459 --- /dev/null +++ b/gastest/Main.hs @@ -0,0 +1,39 @@ +{-# LANGUAGE LambdaCase #-} + +module Main where + +import Prelude hiding (Word) + +import Control.Lens (view) +import Control.Monad (forM_, replicateM) +import Control.Monad.IO.Class (liftIO) +import Control.Monad.Reader (runReaderT) +import Control.Monad.State (execState) +import Data.Foldable (maximumBy, minimumBy) +import Data.Ord (comparing) +import Data.Text (unpack) +import EVM (gas, state) +import Hedgehog.Gen (sample, prune) +import System.Environment (getArgs) + +import Echidna.ABI +import Echidna.Config +import Echidna.Exec +import Echidna.Solidity + + +main :: IO () +main = let conf m = runReaderT m defaultConfig in getArgs >>= \case + [] -> putStrLn "need to specify solidity file" + prog:_ -> do + (v,a,_) <- conf $ loadSolidity prog Nothing + forM_ a $ \f -> do + inputs <- liftIO $ replicateM 1000 . sample . prune . conf $ genAbiCall f + let results = map (\i -> (i, g v - g (execState (execCall i) v))) inputs; g = view $ state . gas + mapM_ putStrLn [ unpack $ fst f + , pp maximumBy "Max" results, pp minimumBy "Min" results + , " Avg: " ++ show ( fromIntegral (sum $ snd <$> results) + / fromIntegral (length results) :: Double) + ] where pp f s r = let (c, n) = f (comparing snd) r in + " " ++ s ++ ": " ++ show (toInteger n) + ++ " (" ++ displayAbiCall c ++ ")" diff --git a/lib/Echidna/ABI.hs b/lib/Echidna/ABI.hs index c8cd16c41..7b85de1bd 100644 --- a/lib/Echidna/ABI.hs +++ b/lib/Echidna/ABI.hs @@ -12,6 +12,7 @@ module Echidna.ABI ( , genAbiBool , genAbiBytes , genAbiBytesDynamic + , genAbiCall , genAbiInt , genInteractions , genAbiString @@ -32,7 +33,7 @@ import Data.DoubleWord (Word128(..), Word160(..)) import Data.Monoid ((<>)) import Data.ByteString (ByteString) import Data.Text (Text, unpack) -import Data.Vector (Vector) +import Data.Vector (Vector, generateM) import Hedgehog.Internal.Gen (MonadGen) import GHC.Exts (IsList(..), Item) import Hedgehog.Range (exponential, exponentialFrom, constant, singleton, Range) @@ -115,14 +116,16 @@ genAbiType = Gen.choice [ pure AbiBytesDynamicType ] genVecOfType :: (MonadReader Config m, MonadGen m) => AbiType -> Range Int -> m (Vector AbiValue) -genVecOfType t r = fmap fromList . Gen.list r $ case t of - AbiUIntType n -> genAbiUInt n - AbiIntType n -> genAbiInt n - AbiAddressType -> genAbiAddress - AbiBoolType -> genAbiBool - AbiBytesType n -> genAbiBytes n - AbiArrayType n t' -> genAbiArray n t' - _ -> error "Arrays must only contain statically sized types" +genVecOfType t r = do + s <- Gen.integral r + generateM s $ \_ -> case t of + AbiUIntType n -> genAbiUInt n + AbiIntType n -> genAbiInt n + AbiAddressType -> genAbiAddress + AbiBoolType -> genAbiBool + AbiBytesType n -> genAbiBytes n + AbiArrayType n t' -> genAbiArray n t' + _ -> error "Arrays must only contain statically sized types" genAbiArrayDynamic :: (MonadReader Config m, MonadGen m) => AbiType -> m AbiValue genAbiArrayDynamic t = AbiArrayDynamic t <$> genVecOfType t (constant 0 256) diff --git a/package.yaml b/package.yaml index b07fd00ef..f4e27b9d4 100644 --- a/package.yaml +++ b/package.yaml @@ -60,3 +60,7 @@ executables: main: Main.hs source-dirs: perprop dependencies: echidna + gastest-exe: + main: Main.hs + source-dirs: gastest + dependencies: echidna