Collect coverage during deploy

covDuringDeploy
Sam Alws 2 months ago
parent 778f63c2dd
commit 0ff8ad479e
  1. 6
      lib/Echidna/Deploy.hs
  2. 29
      lib/Echidna/Exec.hs
  3. 7
      lib/Echidna/Solidity.hs

@ -3,7 +3,7 @@ module Echidna.Deploy where
import Control.Monad (foldM) import Control.Monad (foldM)
import Control.Monad.Catch (MonadThrow(..), throwM) import Control.Monad.Catch (MonadThrow(..), throwM)
import Control.Monad.Reader (MonadReader, asks) import Control.Monad.Reader (MonadReader, asks)
import Control.Monad.State.Strict (MonadIO) import Control.Monad.State.Strict (MonadIO, runStateT)
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.ByteString qualified as BS import Data.ByteString qualified as BS
import Data.ByteString.Base16 qualified as BS16 (decode) import Data.ByteString.Base16 qualified as BS16 (decode)
@ -14,7 +14,7 @@ import Data.Text.Encoding (encodeUtf8)
import EVM.Solidity import EVM.Solidity
import EVM.Types hiding (Env) import EVM.Types hiding (Env)
import Echidna.Exec (execTx) import Echidna.Exec (execTxWithCov)
import Echidna.Events (extractEvents) import Echidna.Events (extractEvents)
import Echidna.Types.Config (Env(..)) import Echidna.Types.Config (Env(..))
import Echidna.Types.Solidity (SolException(..)) import Echidna.Types.Solidity (SolException(..))
@ -51,7 +51,7 @@ deployBytecodes' cs src initialVM = foldM deployOne initialVM cs
where where
deployOne vm (dst, bytecode) = do deployOne vm (dst, bytecode) = do
(_, vm') <- (_, vm') <-
execTx vm $ createTx (bytecode <> zeros) src dst unlimitedGasPerBlock (0, 0) runStateT (execTxWithCov $ createTx (bytecode <> zeros) src dst unlimitedGasPerBlock (0, 0)) vm
case vm'.result of case vm'.result of
Just (VMSuccess _) -> pure vm' Just (VMSuccess _) -> pure vm'
_ -> do _ -> do

@ -13,27 +13,27 @@ import Control.Monad.State.Strict (MonadState(get, put), execState, runStateT, M
import Control.Monad.Reader (MonadReader, ask, asks) import Control.Monad.Reader (MonadReader, ask, asks)
import Control.Monad.ST (ST, stToIO, RealWorld) import Control.Monad.ST (ST, stToIO, RealWorld)
import Data.Bits import Data.Bits
import Data.ByteString qualified as BS
import Data.IORef (readIORef, atomicWriteIORef, newIORef, writeIORef, modifyIORef') import Data.IORef (readIORef, atomicWriteIORef, newIORef, writeIORef, modifyIORef')
import Data.Map qualified as Map import Data.Map qualified as Map
import Data.Maybe (fromMaybe, fromJust) import Data.Maybe (fromMaybe)
import Data.Text qualified as T import Data.Text qualified as T
import Data.Vector qualified as V import Data.Vector qualified as V
import Data.Vector.Unboxed.Mutable qualified as VMut import Data.Vector.Unboxed.Mutable qualified as VMut
import System.Process (readProcessWithExitCode) import System.Process (readProcessWithExitCode)
import EVM (bytecode, replaceCodeOfSelf, loadContract, exec1, vmOpIx, clearTStorages) import EVM (replaceCodeOfSelf, loadContract, exec1, vmOpIx, clearTStorages)
import EVM.ABI import EVM.ABI
import EVM.Dapp (DappInfo) import EVM.Dapp (DappInfo(..))
import EVM.Exec (exec, vmForEthrunCreation) import EVM.Exec (exec, vmForEthrunCreation)
import EVM.Fetch qualified import EVM.Fetch qualified
import EVM.Format (hexText, showTraceTree) import EVM.Format (hexText, showTraceTree)
import EVM.Solidity (SolcContract(..))
import EVM.Types hiding (Env, Gas) import EVM.Types hiding (Env, Gas)
import Echidna.Events (emptyEvents) import Echidna.Events (emptyEvents)
import Echidna.Onchain (safeFetchContractFrom, safeFetchSlotFrom) import Echidna.Onchain (safeFetchContractFrom, safeFetchSlotFrom)
import Echidna.SourceMapping (lookupUsingCodehashOrInsert) import Echidna.SourceMapping (lookupUsingCodehashOrInsert, lookupCodehash)
import Echidna.Symbolic (forceBuf) import Echidna.Symbolic (forceWord)
import Echidna.Transaction import Echidna.Transaction
import Echidna.Types (ExecException(..), Gas, fromEVM, emptyAccount) import Echidna.Types (ExecException(..), Gas, fromEVM, emptyAccount)
import Echidna.Types.Config (Env(..), EConfig(..), UIConf(..), OperationMode(..), OutputFormat(Text)) import Echidna.Types.Config (Env(..), EConfig(..), UIConf(..), OperationMode(..), OutputFormat(Text))
@ -285,11 +285,17 @@ execTxWithCov tx = do
-- | Add current location to the CoverageMap -- | Add current location to the CoverageMap
addCoverage :: VM Concrete RealWorld -> IO () addCoverage :: VM Concrete RealWorld -> IO ()
addCoverage !vm = do addCoverage !vm = do
let (pc, opIx, depth) = currentCovLoc vm let
contract = currentContract vm contract = currentContract vm
getCodehash = lookupCodehash env.codehashMap (forceWord contract.codehash) contract env.dapp
getContractLengths = contractLengthsFromEnv <$> getCodehash
(pc, opIx_, depth) = currentCovLoc vm
opIx <- if isDeploy vm then (opIx_ +) . fst <$> getContractLengths else pure opIx_
maybeCovVec <- lookupUsingCodehashOrInsert env.codehashMap contract env.dapp env.coverageRef $ do maybeCovVec <- lookupUsingCodehashOrInsert env.codehashMap contract env.dapp env.coverageRef $ do
let size = BS.length . forceBuf . fromJust . view bytecode $ contract (sizeA, sizeB) <- getContractLengths
let size = sizeA + sizeB
if size == 0 then pure Nothing else do if size == 0 then pure Nothing else do
-- IO for making a new vec -- IO for making a new vec
vec <- VMut.new size vec <- VMut.new size
@ -316,6 +322,13 @@ execTxWithCov tx = do
-- | Get the VM's current execution location -- | Get the VM's current execution location
currentCovLoc vm = (vm.state.pc, fromMaybe 0 $ vmOpIx vm, length vm.frames) currentCovLoc vm = (vm.state.pc, fromMaybe 0 $ vmOpIx vm, length vm.frames)
isDeploy vm = case (.code) (currentContract vm) of
InitCode _ _ -> True
_ -> False
contractLengthsFromEnv codehash = maybe (0,0) (contractLengths . snd) $ Map.lookup codehash env.dapp.solcByHash
contractLengths contract = (length contract.runtimeSrcmap, length contract.creationSrcmap)
-- | Get the current contract being executed -- | Get the current contract being executed
currentContract vm = fromMaybe (error "no contract information on coverage") $ currentContract vm = fromMaybe (error "no contract information on coverage") $
vm ^? #env % #contracts % at vm.state.codeContract % _Just vm ^? #env % #contracts % at vm.state.codeContract % _Just

@ -4,6 +4,7 @@ import Optics.Core hiding (filtered)
import Control.Monad (when, unless, forM_) import Control.Monad (when, unless, forM_)
import Control.Monad.Catch (MonadThrow(..)) import Control.Monad.Catch (MonadThrow(..))
import Control.Monad.State (runStateT)
import Control.Monad.Extra (whenM) import Control.Monad.Extra (whenM)
import Control.Monad.Reader (ReaderT(runReaderT)) import Control.Monad.Reader (ReaderT(runReaderT))
import Control.Monad.ST (stToIO, RealWorld) import Control.Monad.ST (stToIO, RealWorld)
@ -39,7 +40,7 @@ import Echidna.ABI
import Echidna.Deploy (deployContracts, deployBytecodes) import Echidna.Deploy (deployContracts, deployBytecodes)
import Echidna.Etheno (loadEthenoBatch) import Echidna.Etheno (loadEthenoBatch)
import Echidna.Events (extractEvents) import Echidna.Events (extractEvents)
import Echidna.Exec (execTx, initialVM) import Echidna.Exec (execTx, initialVM, execTxWithCov)
import Echidna.SourceAnalysis.Slither import Echidna.SourceAnalysis.Slither
import Echidna.Test (createTests, isAssertionMode, isPropertyMode, isDapptestMode) import Echidna.Test (createTests, isAssertionMode, isPropertyMode, isDapptestMode)
import Echidna.Types.Config (EConfig(..), Env(..)) import Echidna.Types.Config (EConfig(..), Env(..))
@ -199,13 +200,13 @@ loadSpecified env mainContract cs = do
vm2 <- deployBytecodes solConf.deployBytecodes solConf.deployer vm1 vm2 <- deployBytecodes solConf.deployBytecodes solConf.deployer vm1
-- main contract deployment -- main contract deployment
let deployment = execTx vm2 $ createTxWithValue let deployment = runStateT (execTxWithCov (createTxWithValue
mainContract.creationCode mainContract.creationCode
solConf.deployer solConf.deployer
solConf.contractAddr solConf.contractAddr
unlimitedGasPerBlock unlimitedGasPerBlock
(fromIntegral solConf.balanceContract) (fromIntegral solConf.balanceContract)
(0, 0) (0, 0))) vm2
(_, vm3) <- deployment (_, vm3) <- deployment
when (isNothing $ currentContract vm3) $ when (isNothing $ currentContract vm3) $
throwM $ DeploymentFailed solConf.contractAddr $ T.unlines $ extractEvents True env.dapp vm3 throwM $ DeploymentFailed solConf.contractAddr $ T.unlines $ extractEvents True env.dapp vm3

Loading…
Cancel
Save