mirror of https://github.com/crytic/echidna
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
80 lines
2.9 KiB
80 lines
2.9 KiB
module Echidna where
|
|
|
|
import Control.Lens (view, (^.), to)
|
|
import Data.Has (Has(..))
|
|
import Control.Monad.Catch (MonadCatch(..), MonadThrow(..))
|
|
import Control.Monad.Reader (MonadReader, MonadIO, liftIO)
|
|
import Control.Monad.Random (MonadRandom)
|
|
import Data.HashMap.Strict (toList)
|
|
import Data.Map.Strict (keys)
|
|
import Data.List (nub, find)
|
|
import Data.List.NonEmpty qualified as NE
|
|
|
|
import EVM (env, contracts, VM)
|
|
import EVM.ABI (AbiValue(AbiAddress))
|
|
import EVM.Solidity (SourceCache, SolcContract)
|
|
|
|
import Echidna.ABI
|
|
import Echidna.Types.Config hiding (cfg)
|
|
import Echidna.Types.Solidity
|
|
import Echidna.Types.Campaign
|
|
import Echidna.Types.Random
|
|
import Echidna.Types.Signature
|
|
import Echidna.Types.Test
|
|
import Echidna.Types.Tx
|
|
import Echidna.Types.World
|
|
import Echidna.Solidity
|
|
import Echidna.Processor
|
|
import Echidna.Output.Corpus
|
|
import Echidna.RPC (loadEtheno, extractFromEtheno)
|
|
|
|
-- | This function is used to prepare, process, compile and initialize smart contracts for testing.
|
|
-- It takes:
|
|
-- * A config record
|
|
-- * A list of contract files paths for the smart contract code
|
|
-- * A contract name (if any)
|
|
-- * A seed used during the random generation
|
|
-- and returns:
|
|
-- * A VM with the contract deployed and ready for testing
|
|
-- * A World with all the required data for generating random transctions
|
|
-- * A list of Echidna tests to check
|
|
-- * A prepopulated dictionary (if any)
|
|
-- * A list of transaction sequences to initialize the corpus
|
|
prepareContract :: (MonadCatch m, MonadRandom m, MonadReader x m, MonadIO m, MonadFail m,
|
|
Has TestConf x, Has TxConf x, Has SolConf x)
|
|
=> EConfig -> NE.NonEmpty FilePath -> Maybe ContractName -> Seed
|
|
-> m (VM, SourceCache, [SolcContract], World, [EchidnaTest], Maybe GenDict, [[Tx]])
|
|
prepareContract cfg fs c g = do
|
|
ctxs <- liftIO $ loadTxs cd
|
|
|
|
-- compile and load contracts
|
|
(cs, scs) <- Echidna.Solidity.contracts fs
|
|
p <- loadSpecified c cs
|
|
|
|
-- run processors
|
|
ca <- view (hasLens . cryticArgs)
|
|
si <- runSlither (NE.head fs) ca
|
|
case find (< minSupportedSolcVersion) $ solcVersions si of
|
|
Just outdatedVersion -> throwM $ OutdatedSolcVersion outdatedVersion
|
|
Nothing -> return ()
|
|
|
|
-- load tests
|
|
(v, w, ts) <- prepareForTest p c si
|
|
|
|
-- get signatures
|
|
let sigs = nub $ concatMap (NE.toList . snd) (toList $ w ^. highSignatureMap)
|
|
|
|
ads <- addresses
|
|
let ads' = AbiAddress <$> v ^. env . EVM.contracts . to keys
|
|
let constants' = enhanceConstants si ++ timeConstants ++ extremeConstants ++ NE.toList ads ++ ads'
|
|
|
|
-- load transactions from init sequence (if any)
|
|
es' <- liftIO $ maybe (return []) loadEtheno it
|
|
let txs = ctxs ++ maybe [] (const [extractFromEtheno es' sigs]) it
|
|
|
|
-- start ui and run tests
|
|
let sc = selectSourceCache c scs
|
|
return (v, sc, cs, w, ts, Just $ mkGenDict df constants' [] g (returnTypes cs), txs)
|
|
where cd = cfg ^. cConf . corpusDir
|
|
df = cfg ^. cConf . dictFreq
|
|
it = cfg ^. sConf . initialize
|
|
|