{-# Language TemplateHaskell #-}
module EVM.Dapp where
import EVM (Trace, traceCodehash, traceOpIx)
import EVM.ABI (Event)
import EVM.Debug (srcMapCodePos)
import EVM.Keccak (abiKeccak)
import EVM.Solidity (SolcContract, CodeType (..), SourceCache, SrcMap)
import EVM.Solidity (contractName)
import EVM.Solidity (runtimeCodehash, creationCodehash, abiMap)
import EVM.Solidity (runtimeSrcmap, creationSrcmap, eventMap)
import EVM.Solidity (methodSignature, contractAst, astIdMap, astSrcMap)
import EVM.Types (W256)
import Data.Aeson (Value)
import Data.Text (Text, isPrefixOf, pack)
import Data.Text.Encoding (encodeUtf8)
import Data.Map (Map)
import Data.Monoid ((<>))
import Data.Word (Word32)
import Data.List (sort)
import Control.Arrow ((>>>))
import Control.Lens
import qualified Data.Map as Map
data DappInfo = DappInfo
{ _dappRoot :: FilePath
, _dappSolcByName :: Map Text SolcContract
, _dappSolcByHash :: Map W256 (CodeType, SolcContract)
, _dappSources :: SourceCache
, _dappUnitTests :: [(Text, [Text])]
, _dappEventMap :: Map W256 Event
, _dappAstIdMap :: Map Int Value
, _dappAstSrcMap :: (SrcMap -> Maybe Value)
}
makeLenses ''DappInfo
dappInfo
:: FilePath -> Map Text SolcContract -> SourceCache -> DappInfo
dappInfo root solcByName sources =
let
solcs = Map.elems solcByName
astIds = astIdMap (map (view contractAst) solcs)
in DappInfo
{ _dappRoot = root
, _dappUnitTests = findUnitTests ("test" `isPrefixOf`) solcs
, _dappSources = sources
, _dappSolcByName = solcByName
, _dappSolcByHash =
let
f g k = Map.fromList [(view g x, (k, x)) | x <- solcs]
in
mappend
(f runtimeCodehash Runtime)
(f creationCodehash Creation)
, _dappEventMap =
mconcat (map (view eventMap) solcs)
, _dappAstIdMap = astIds
, _dappAstSrcMap = astSrcMap astIds
}
unitTestMarkerAbi :: Word32
unitTestMarkerAbi = abiKeccak (encodeUtf8 "IS_TEST()")
findUnitTests :: (Text -> Bool) -> ([SolcContract] -> [(Text, [Text])])
findUnitTests matcher =
concatMap $ \c ->
case preview (abiMap . ix unitTestMarkerAbi) c of
Nothing -> []
Just _ ->
let testNames = (unitTestMethods matcher) c
in if null testNames
then []
else [(view contractName c, testNames)]
unitTestMethods :: (Text -> Bool) -> (SolcContract -> [Text])
unitTestMethods matcher =
view abiMap
>>> Map.elems
>>> map (view methodSignature)
>>> filter matcher
>>> sort
traceSrcMap :: DappInfo -> Trace -> Maybe SrcMap
traceSrcMap dapp trace =
let
h = view traceCodehash trace
i = view traceOpIx trace
in case preview (dappSolcByHash . ix h) dapp of
Nothing ->
Nothing
Just (Creation, solc) ->
preview (creationSrcmap . ix i) solc
Just (Runtime, solc) ->
preview (runtimeSrcmap . ix i) solc
showTraceLocation :: DappInfo -> Trace -> Either Text Text
showTraceLocation dapp trace =
case traceSrcMap dapp trace of
Nothing -> Left "<no source map>"
Just sm ->
case srcMapCodePos (view dappSources dapp) sm of
Nothing -> Left "<source not found>"
Just (fileName, lineIx) ->
Right (fileName <> ":" <> pack (show lineIx))