{-# 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 = -- Sum up the event ABI maps from all the contracts. 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 "" Just sm -> case srcMapCodePos (view dappSources dapp) sm of Nothing -> Left "" Just (fileName, lineIx) -> Right (fileName <> ":" <> pack (show lineIx))