generate transactions using a bytecode to abi mapping instead of a static address to abi mapping

pull/344/head
Will Song 5 years ago
parent 694e1df842
commit 1604a672b5
  1. 2
      lib/Echidna/Campaign.hs
  2. 16
      lib/Echidna/Solidity.hs
  3. 37
      lib/Echidna/Transaction.hs

@ -191,7 +191,7 @@ callseq v w ql = do
-- Then, we get the current campaign state
ca <- use hasLens
-- Then, we generate the actual transaction in the sequence
is <- replicateM ql (evalStateT genTxM (w, ca ^. genDict))
is <- replicateM ql (evalStateT (genTxM old) (w, ca ^. genDict))
-- We then run each call sequentially. This gives us the result of each call, plus a new state
(res, s) <- runStateT (evalSeq v ef is) (v, ca)
let new = s ^. _1 . env . EVM.contracts

@ -160,12 +160,12 @@ linkLibraries ls = "--libraries " ++
-- usable for Echidna. NOTE: Contract names passed to this function should be prefixed by the
-- filename their code is in, plus a colon.
loadSpecified :: (MonadIO m, MonadThrow m, MonadReader x m, Has SolConf x, Has TxConf x, MonadFail m)
=> Maybe Text -> [SolcContract] -> m (VM, NE.NonEmpty SolSignature, [Text], M.HashMap BS.ByteString (M.HashMap Text [AbiType]))
=> Maybe Text -> [SolcContract] -> m (VM, NE.NonEmpty SolSignature, [Text], M.HashMap BS.ByteString (NE.NonEmpty SolSignature))
loadSpecified name cs = do
-- generate the complete abi mapping
let abiOf :: SolcContract -> M.HashMap Text [AbiType]
abiOf c = M.fromList $ elems (c ^. abiMap) <&> \m -> (m ^. methodName, m ^.. methodInputs . traverse . _2)
abiMapping = M.fromList $ cs <&> \c -> (c ^. runtimeCode, abiOf c)
let abiOf :: SolcContract -> Maybe (NE.NonEmpty SolSignature)
abiOf c = NE.nonEmpty $ elems (c ^. abiMap) <&> \m -> (m ^. methodName, m ^.. methodInputs . traverse . _2)
abiMapping = M.fromList . catMaybes $ cs <&> \c -> case abiOf c of { Nothing -> Nothing; Just x -> Just (c ^. runtimeCode, x) }
-- Pick contract to load
c <- choose cs name
q <- view (hasLens . quiet)
@ -218,16 +218,16 @@ loadSpecified name cs = do
-- => FilePath -> Maybe Text -> m (VM, [SolSignature], [Text])
--loadSolidity fp name = contracts fp >>= loadSpecified name
loadWithCryticCompile :: (MonadIO m, MonadThrow m, MonadReader x m, Has SolConf x, Has TxConf x, MonadFail m)
=> NE.NonEmpty FilePath -> Maybe Text -> m (VM, NE.NonEmpty SolSignature, [Text], M.HashMap BS.ByteString (M.HashMap Text [AbiType]))
=> NE.NonEmpty FilePath -> Maybe Text -> m (VM, NE.NonEmpty SolSignature, [Text], M.HashMap BS.ByteString (NE.NonEmpty SolSignature))
loadWithCryticCompile fp name = contracts fp >>= loadSpecified name
-- | Given the results of 'loadSolidity', assuming a single-contract test, get everything ready
-- for running a 'Campaign' against the tests found.
prepareForTest :: (MonadReader x m, Has SolConf x)
=> (VM, NE.NonEmpty SolSignature, [Text], M.HashMap BS.ByteString (M.HashMap Text [AbiType])) -> m (VM, World, [SolTest])
prepareForTest (v, a, ts, _) = view hasLens <&> \(SolConf _ _ s _ _ _ _ _ _ _ _ ch) ->
(v, World s ((r, a) NE.:| []), fmap Left (zip ts $ repeat r) ++ if ch then Right <$> drop 1 a' else []) where
=> (VM, NE.NonEmpty SolSignature, [Text], M.HashMap BS.ByteString (NE.NonEmpty SolSignature)) -> m (VM, World, [SolTest])
prepareForTest (v, a, ts, m) = view hasLens <&> \(SolConf _ _ s _ _ _ _ _ _ _ _ ch) ->
(v, World s m, fmap Left (zip ts $ repeat r) ++ if ch then Right <$> drop 1 a' else []) where
r = v ^. state . contract
a' = NE.toList a

@ -20,6 +20,8 @@ import Control.Monad.State.Strict (MonadState, State, evalStateT, runState)
import Data.Aeson (ToJSON(..), object)
import Data.ByteString (ByteString)
import Data.Has (Has(..))
import Data.Map (Map, toList)
import Data.Maybe (catMaybes)
import Data.List (intercalate)
import EVM hiding (value)
import EVM.ABI (abiCalldata, abiValueType)
@ -29,6 +31,7 @@ import EVM.Types (Addr)
import qualified Control.Monad.State.Strict as S (state)
import qualified Data.ByteString.Base16 as BS16
import qualified Data.ByteString.Char8 as BSC8
import qualified Data.HashMap.Strict as M
import qualified Data.List.NonEmpty as NE
import qualified Data.Text as T
import qualified Data.Vector as V
@ -103,16 +106,18 @@ level x = x
-- | A contract is just an address with an ABI (for our purposes).
type ContractA = (Addr, NE.NonEmpty SolSignature)
-- | The world is made our of humans with an address, and contracts with an address + ABI.
data World = World { _senders :: NE.NonEmpty Addr
, _receivers :: NE.NonEmpty ContractA
-- | The world is made our of humans with an address, and a way to map contract
-- bytecodes to an ABI
data World = World { _senders :: NE.NonEmpty Addr
, _bytecodeMapping :: M.HashMap ByteString (NE.NonEmpty SolSignature)
}
makeLenses ''World
-- | Given generators for an origin, destination, value, and function call, generate a call
-- transaction. Note: This doesn't generate @CREATE@s because I don't know how to do that at random.
genTxWith :: (MonadRandom m, MonadState x m, Has World x, MonadThrow m)
=> (NE.NonEmpty Addr -> m Addr) -- ^ Sender generator
=> Map Addr Contract -- ^ List of contracts
-> (NE.NonEmpty Addr -> m Addr) -- ^ Sender generator
-> (NE.NonEmpty ContractA -> m ContractA) -- ^ Receiver generator
-> (Addr -> ContractA -> m SolCall) -- ^ Call generator
-> m Word -- ^ Gas generator
@ -120,17 +125,29 @@ genTxWith :: (MonadRandom m, MonadState x m, Has World x, MonadThrow m)
-> (Addr -> ContractA -> SolCall -> m Word) -- ^ Value generator
-> m (Word, Word) -- ^ Delay generator
-> m Tx
genTxWith s r c g gp v t = use hasLens >>= \(World ss rs) ->
let s' = s ss; r' = r rs; c' = join $ liftM2 c s' r' in
genTxWith m s r c g gp v t = use hasLens >>= \(World ss mm) ->
let s' = s ss
r' = r rs
c' = join $ liftM2 c s' r'
rs = NE.fromList . catMaybes $ mkR <$> toList m
mkR (a, cc) = case M.lookup (cc ^. bytecode) mm of
Nothing -> Nothing
Just x -> Just (a, x)
in
((liftM5 Tx (SolCall <$> c') s' (fst <$> r') g gp <*>) =<< liftM3 v s' r' c') <*> t
-- | Synthesize a random 'Transaction', not using a dictionary.
genTx :: forall m x y. (MonadRandom m, MonadReader x m, Has TxConf x, MonadState y m, Has World y, MonadThrow m) => m Tx
genTx = use (hasLens :: Lens' y World) >>= evalStateT genTxM . (defaultDict,)
genTx :: forall m x y. (MonadRandom m, MonadReader x m, Has TxConf x, MonadState y m, Has World y, MonadThrow m)
=> Map Addr Contract
-> m Tx
genTx m = use (hasLens :: Lens' y World) >>= evalStateT (genTxM m) . (defaultDict,)
-- | Generate a random 'Transaction' with either synthesis or mutation of dictionary entries.
genTxM :: (MonadRandom m, MonadReader x m, Has TxConf x, MonadState y m, Has GenDict y, Has World y, MonadThrow m) => m Tx
genTxM = view hasLens >>= \(TxConf _ g maxGp t b) -> genTxWith
genTxM :: (MonadRandom m, MonadReader x m, Has TxConf x, MonadState y m, Has GenDict y, Has World y, MonadThrow m)
=> Map Addr Contract
-> m Tx
genTxM m = view hasLens >>= \(TxConf _ g maxGp t b) -> genTxWith
m
rElem rElem -- src and dst
(const $ genInteractionsM . snd) -- call itself
(pure g) (inRange maxGp) (\_ _ _ -> pure 0) -- gas, gasprice, value

Loading…
Cancel
Save