Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove prefixes from EchidnaTest #994

Merged
merged 1 commit into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions lib/Echidna/Campaign.hs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ isDone c = do
res (Large i) = if i >= conf.shrinkLimit then Just False else Nothing
res Solved = Just False
res (Failed _) = Just False
let testResults = res . (.testState) <$> c._tests
let testResults = res . (.state) <$> c._tests
let done = if conf.stopOnFail then Just False `elem` testResults
else all isJust testResults
pure done
Expand All @@ -73,7 +73,7 @@ isDone c = do
-- success or a failure.
isSuccessful :: Campaign -> Bool
isSuccessful Campaign{_tests} =
all (\case { Passed -> True; Open _ -> True; _ -> False; }) ((.testState) <$> _tests)
all (\case { Passed -> True; Open _ -> True; _ -> False; }) ((.state) <$> _tests)

-- | Given an initial 'VM' state and a @('SolTest', 'TestState')@ pair, as well as possibly a sequence
-- of transactions and the state after evaluation, see if:
Expand All @@ -87,10 +87,10 @@ updateTest :: (MonadIO m, MonadCatch m, MonadRandom m, MonadReader Env m)
updateTest vmForShrink (vm, xs) test = do
limit <- asks (.cfg.campaignConf.testLimit)
dappInfo <- asks (.dapp)
case test.testState of
case test.state of
Open i | i > limit -> case test.testType of
OptimizationTest _ _ -> pure $ test { testState = Large (-1) }
_ -> pure $ test { testState = Passed }
OptimizationTest _ _ -> pure $ test { state = Large (-1) }
_ -> pure $ test { state = Passed }
Open i -> do
(testValue, vm') <- evalStateT (checkETest test) vm
let events = extractEvents False dappInfo vm'
Expand Down Expand Up @@ -278,7 +278,7 @@ campaign u vm world ts dict initialCorpus = do
execStateT (evalRandT (lift u >> runCampaign) (mkStdGen effectiveSeed)) camp
where
memo = makeBytecodeCache . map (forceBuf . (^. bytecode)) . Map.elems
runCampaign = gets (fmap (.testState) . (._tests)) >>= update
runCampaign = gets (fmap (.state) . (._tests)) >>= update
update c = do
CampaignConf{testLimit, stopOnFail, seqLen, shrinkLimit} <- asks (.cfg.campaignConf)
Campaign{_ncallseqs} <- get
Expand Down
8 changes: 3 additions & 5 deletions lib/Echidna/Output/JSON.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import Echidna.Types (Gas)
import Echidna.Types.Coverage (CoverageInfo)
import Echidna.Types.Campaign qualified as C
import Echidna.Types.Test qualified as T
import Echidna.Types.Test (EchidnaTest, testState, testReproducer)
import Echidna.Types.Test (EchidnaTest(..))
import Echidna.Types.Tx (Tx(..), TxCall(..))

data Campaign = Campaign
Expand Down Expand Up @@ -104,10 +104,8 @@ encodeCampaign C.Campaign{..} = encode
}

mapTest :: EchidnaTest -> Test
mapTest echidnaTest =
let tst = echidnaTest.testState
txs = echidnaTest.testReproducer
(status, transactions, err) = mapTestState tst txs in
mapTest test =
let (status, transactions, err) = mapTestState test.state test.reproducer in
Test { contract = "" -- TODO add when mapping is available https://github.com/crytic/echidna/issues/415
, name = "name" --TODO add a proper name here
, status = status
Expand Down
25 changes: 12 additions & 13 deletions lib/Echidna/Shrink.hs
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,27 @@ shrinkTest :: (MonadIO m, MonadCatch m, MonadRandom m, MonadReader Env m)
shrinkTest vm test = do
sl <- asks (.cfg.campaignConf.shrinkLimit)
dappInfo <- asks (.dapp)
let x = test.testReproducer
case test.testState of
case test.state of
Large i | i >= sl ->
pure $ test { testState = Solved, testReproducer = x }
pure $ test { state = Solved }
Large i ->
if length x > 1 || any canShrinkTx x then do
maybeShrunk <- evalStateT (shrinkSeq (checkETest test) test.testValue x) vm
if length test.reproducer > 1 || any canShrinkTx test.reproducer then do
maybeShrunk <- evalStateT (shrinkSeq (checkETest test) test.value test.reproducer) vm
pure $ case maybeShrunk of
Just (txs, val, vm') -> do
test { testState = Large (i + 1)
, testReproducer = txs
, testEvents = extractEvents False dappInfo vm'
, testResult = getResultFromVM vm'
, testValue = val }
test { state = Large (i + 1)
, reproducer = txs
, events = extractEvents False dappInfo vm'
, result = getResultFromVM vm'
, value = val }
Nothing ->
-- No success with shrinking this time, just bump trials
test { testState = Large (i + 1) }
test { state = Large (i + 1) }
else
pure $ test { testState = if isOptimizationTest test.testType
pure $ test { state = if isOptimizationTest test.testType
then Large (i + 1)
else Solved
, testReproducer = x }
}
_ -> pure test

-- | Given a call sequence that solves some Echidna test, try to randomly generate a smaller one that
Expand Down
18 changes: 9 additions & 9 deletions lib/Echidna/Test.hs
Original file line number Diff line number Diff line change
Expand Up @@ -97,19 +97,19 @@ createTests m td ts r ss = case m of

updateOpenTest :: EchidnaTest -> [Tx] -> Int -> (TestValue, Events, TxResult) -> EchidnaTest
updateOpenTest test txs _ (BoolValue False,es,r) =
test { testState = Large (-1), testReproducer = txs, testEvents = es, testResult = r }
test { state = Large (-1), reproducer = txs, events = es, result = r }
updateOpenTest test _ i (BoolValue True,_,_) =
test { testState = Open (i + 1) }
test { state = Open (i + 1) }
updateOpenTest test txs i (IntValue v',es,r) =
if v' > v then
test { testState = Open (i + 1)
, testReproducer = txs
, testValue = IntValue v'
, testEvents = es
, testResult = r }
test { state = Open (i + 1)
, reproducer = txs
, value = IntValue v'
, events = es
, result = r }
else
test { testState = Open (i + 1) }
where v = case test.testValue of
test { state = Open (i + 1) }
where v = case test.value of
IntValue x -> x
_ -> error "Invalid type of value for optimization"
updateOpenTest _ _ _ _ = error "Invalid type of test"
Expand Down
140 changes: 72 additions & 68 deletions lib/Echidna/Types/Test.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,103 +6,107 @@ import Data.Maybe (maybeToList)
import Data.Text (Text)

import EVM (VM)
import EVM.Types (Addr)
import EVM.Dapp (DappInfo)
import EVM.Types (Addr)

import Echidna.Events (Events)
import Echidna.Types
import Echidna.Types.Tx (Tx, TxResult)
import Echidna.Types (ExecException)
import Echidna.Types.Signature (SolSignature)
import Echidna.Types.Tx (Tx, TxResult)

-- | Test mode is parsed from a string
type TestMode = String

-- | Configuration for the creation of Echidna tests.
data TestConf = TestConf { classifier :: Text -> VM -> Bool
-- ^ Given a VM state and test name, check if a test just passed (typically
-- examining '_result'.)
, testSender :: Addr -> Addr
-- ^ Given the address of a test, return the address to send test evaluation
-- transactions from.
}

type TestAttempts = Int

-- | State of a particular Echidna test. N.B.: \"Solved\" means a falsifying call sequence was found.
data TestState = Open Int -- ^ Maybe solvable, tracking attempts already made
| Large Int -- ^ Solved, maybe shrinable, tracking shrinks tried
| Passed -- ^ Presumed unsolvable
| Solved -- ^ Solved with no need for shrinking
| Failed ExecException -- ^ Broke the execution environment
deriving Show

data TestValue = BoolValue Bool
| IntValue Int256
| NoValue
deriving (Eq)
data TestConf = TestConf
{ classifier :: Text -> VM -> Bool
-- ^ Given a VM state and test name, check if a test just passed (typically
-- examining '_result'.)
, testSender :: Addr -> Addr
-- ^ Given the address of a test, return the address to send test evaluation
-- transactions from.
}

-- | State of a particular Echidna test. N.B.: 'Solved' means a falsifying
-- call sequence was found.
data TestState
= Open Int -- ^ Maybe solvable, tracking attempts already made
| Large Int -- ^ Solved, maybe shrinable, tracking shrinks tried
| Passed -- ^ Presumed unsolvable
| Solved -- ^ Solved with no need for shrinking
| Failed ExecException -- ^ Broke the execution environment
deriving Show

data TestValue
= BoolValue Bool
| IntValue Int256
| NoValue
deriving Eq

instance Show TestValue where
show (BoolValue x) = show x
show (IntValue x) = show x
show NoValue = ""

data TestType = PropertyTest Text Addr
| OptimizationTest Text Addr
| AssertionTest Bool SolSignature Addr
| CallTest Text (DappInfo -> VM -> TestValue)
| Exploration
data TestType
= PropertyTest Text Addr
| OptimizationTest Text Addr
| AssertionTest Bool SolSignature Addr
| CallTest Text (DappInfo -> VM -> TestValue)
| Exploration

instance Eq TestType where
(PropertyTest t a) == (PropertyTest t' a') = t == t' && a == a'
(AssertionTest b s a) == (AssertionTest b' s' a') = b == b' && s == s' && a == a'
(OptimizationTest s a) == (OptimizationTest s' a') = s == s' && a == a'
(CallTest t _) == (CallTest t' _) = t == t'
Exploration == Exploration = True
_ == _ = False

PropertyTest t a == PropertyTest t' a' = t == t' && a == a'
AssertionTest b s a == AssertionTest b' s' a' = b == b' && s == s' && a == a'
OptimizationTest s a == OptimizationTest s' a' = s == s' && a == a'
CallTest t _ == CallTest t' _ = t == t'
Exploration == Exploration = True
_ == _ = False

instance Eq TestState where
(Open i) == (Open j) = i == j
(Large i) == (Large j) = i == j
Passed == Passed = True
Solved == Solved = True
_ == _ = False
Open i == Open j = i == j
Large i == Large j = i == j
Passed == Passed = True
Solved == Solved = True
_ == _ = False

-- | An Echidna test is represented with the following data record
data EchidnaTest = EchidnaTest {
testState :: TestState
, testType :: TestType
, testValue :: TestValue
, testReproducer :: [Tx]
, testResult :: TxResult
, testEvents :: Events
} deriving Eq
data EchidnaTest = EchidnaTest
{ state :: TestState
, testType :: TestType
, value :: TestValue
, reproducer :: [Tx]
, result :: TxResult
, events :: Events
} deriving Eq

isOptimizationTest :: TestType -> Bool
isOptimizationTest (OptimizationTest _ _) = True
isOptimizationTest _ = False

isOpen :: EchidnaTest -> Bool
isOpen t = case t.testState of
Open _ -> True
_ -> False
isOpen t = case t.state of
Open _ -> True
_ -> False

didFailed :: EchidnaTest -> Bool
didFailed t = case t.testState of
Large _ -> True
Solved -> True
_ -> False
didFail :: EchidnaTest -> Bool
didFail t = case t.state of
Large _ -> True
Solved -> True
_ -> False

isPassed :: EchidnaTest -> Bool
isPassed t = case t.testState of
Passed -> True
_ -> False

isPassed t = case t.state of
Passed -> True
_ -> False

instance ToJSON TestState where
toJSON s = object $ ("passed", toJSON passed) : maybeToList desc where
(passed, desc) = case s of Open _ -> (True, Nothing)
Passed -> (True, Nothing)
Large _ -> (False, Nothing)
Solved -> (False, Nothing)
Failed e -> (False, Just ("exception", toJSON $ show e))
toJSON s =
object $ ("passed", toJSON passed) : maybeToList desc
where
(passed, desc) = case s of
Open _ -> (True, Nothing)
Passed -> (True, Nothing)
Large _ -> (False, Nothing)
Solved -> (False, Nothing)
Failed e -> (False, Just ("exception", toJSON $ show e))
2 changes: 1 addition & 1 deletion lib/Echidna/Types/Tx.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}

module Echidna.Types.Tx where

Expand Down
6 changes: 3 additions & 3 deletions lib/Echidna/UI.hs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import Echidna.Types.Campaign
import Echidna.Types.Config
import Echidna.Types.Corpus (corpusSize)
import Echidna.Types.Coverage (scoveragePoints)
import Echidna.Types.Test (EchidnaTest(..), TestState(..), didFailed, isOpen)
import Echidna.Types.Test (EchidnaTest(..), TestState(..), didFail, isOpen)
import Echidna.Types.Tx (Tx)
import Echidna.Types.World (World)
import Echidna.UI.Report
Expand Down Expand Up @@ -213,12 +213,12 @@ isTerminal = (&&) <$> queryTerminal (Fd 0) <*> queryTerminal (Fd 1)
-- | Composes a compact text status line of the campaign
statusLine :: CampaignConf -> Campaign -> String
statusLine campaignConf camp =
"tests: " <> show (length $ filter didFailed camp._tests) <> "/" <> show (length camp._tests)
"tests: " <> show (length $ filter didFail camp._tests) <> "/" <> show (length camp._tests)
<> ", fuzzing: " <> show fuzzRuns <> "/" <> show campaignConf.testLimit
<> ", cov: " <> show (scoveragePoints camp._coverage)
<> ", corpus: " <> show (corpusSize camp._corpus)
where
fuzzRuns = case filter isOpen camp._tests of
-- fuzzing progress is the same for all Open tests, grab the first one
EchidnaTest { testState = Open t }:_ -> t
EchidnaTest { state = Open t }:_ -> t
_ -> campaignConf.testLimit
12 changes: 6 additions & 6 deletions lib/Echidna/UI/Report.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import Echidna.Types (Gas)
import Echidna.Types.Campaign
import Echidna.Types.Corpus (Corpus, corpusSize)
import Echidna.Types.Coverage (CoverageMap, scoveragePoints)
import Echidna.Types.Test (testEvents, testState, TestState(..), testType, TestType(..), testReproducer, testValue)
import Echidna.Types.Test (EchidnaTest(..), TestState(..), TestType(..))
import Echidna.Types.Tx (Tx(..), TxCall(..), TxConf(..))
import Echidna.Types.Config

Expand Down Expand Up @@ -136,17 +136,17 @@ ppTests Campaign { _tests = ts } = unlines . catMaybes <$> mapM pp ts
pp t =
case t.testType of
PropertyTest n _ -> do
status <- ppTS t.testState t.testEvents t.testReproducer
status <- ppTS t.state t.events t.reproducer
pure $ Just (T.unpack n <> ": " <> status)
CallTest n _ -> do
status <- ppTS t.testState t.testEvents t.testReproducer
status <- ppTS t.state t.events t.reproducer
pure $ Just (T.unpack n <> ": " <> status)
AssertionTest _ s _ -> do
status <- ppTS t.testState t.testEvents t.testReproducer
status <- ppTS t.state t.events t.reproducer
pure $ Just (T.unpack (encodeSig s) <> ": " <> status)
OptimizationTest n _ -> do
status <- ppOPT t.testState t.testEvents t.testReproducer
pure $ Just (T.unpack n <> ": max value: " <> show t.testValue <> "\n" <> status)
status <- ppOPT t.state t.events t.reproducer
pure $ Just (T.unpack n <> ": max value: " <> show t.value <> "\n" <> status)
Exploration -> pure Nothing

-- | Given a number of boxes checked and a number of total boxes, pretty-print progress in box-checking.
Expand Down
Loading