1- {-# LANGUAGE LambdaCase #-}
21{-# LANGUAGE FlexibleContexts #-}
32{-# LANGUAGE OverloadedStrings #-}
43{-# LANGUAGE ScopedTypeVariables #-}
@@ -9,43 +8,53 @@ import Prelude hiding (Word)
98
109import Control.Exception (Exception )
1110import Control.Lens
12- import Control.Monad (foldM )
11+ import Control.Monad (foldM , void )
1312import Control.Monad.Catch (MonadThrow , throwM )
1413import Control.Monad.IO.Class (MonadIO (.. ))
1514import Control.Monad.Reader.Class (MonadReader (.. ))
16- import Control.Monad.State.Strict (MonadState , execStateT , runStateT , get , put )
15+ import Control.Monad.State.Strict (MonadState , runStateT , get , put )
1716import Data.Aeson (FromJSON (.. ), (.:) , withObject , eitherDecodeFileStrict )
18- import Data.Binary.Get (runGetOrFail )
1917import Data.ByteString.Char8 (ByteString )
2018import Data.Has (Has (.. ))
2119import Data.Text.Encoding (encodeUtf8 )
20+
2221import EVM
23- import EVM.ABI (AbiType (.. ), getAbi )
22+ import EVM.ABI (AbiType (.. ), AbiValue ( .. ), decodeAbiValue , selector )
2423import EVM.Concrete (w256 )
2524import EVM.Exec (exec )
2625import EVM.Types (Addr , Buffer (.. ), W256 )
2726import Text.Read (readMaybe )
2827
2928import qualified Control.Monad.Fail as M (MonadFail (.. ))
3029import qualified Data.ByteString.Base16 as BS16 (decode )
31- import qualified Data.Text as T (Text , drop , unpack )
32- import qualified Data.Vector as V (fromList )
30+ import qualified Data.Text as T (drop , unpack )
31+ import qualified Data.Vector as V (fromList , toList )
32+ import qualified Data.ByteString.Char8 as BS
33+ import qualified Data.ByteString.Lazy as LBS
3334
3435import Echidna.Exec
3536import Echidna.Transaction
36- import Echidna.Types.Tx (TxCall (.. ), Tx (.. ), TxConf , basicTx , createTxWithValue , propGas , unlimitedGasPerBlock )
37+ -- import Echidna.Types.Tx (TxCall(..), Tx(Tx), TxConf, propGas, makeSingleTx)
38+ import Echidna.Types.Signature (SolSignature )
39+ import Echidna.ABI (encodeSig )
40+
41+ import Echidna.Types.Tx (TxCall (.. ), Tx (.. ), TxConf , makeSingleTx , createTxWithValue , unlimitedGasPerBlock )
3742
3843-- | During initialization we can either call a function or create an account or contract
3944data Etheno = AccountCreated Addr -- ^ Registers an address with the echidna runtime
4045 | ContractCreated Addr Addr Integer Integer ByteString W256 -- ^ A contract was constructed on the blockchain
4146 | FunctionCall Addr Addr Integer Integer ByteString W256 -- ^ A contract function was executed
47+ | BlockMined Integer Integer -- ^ A new block was mined contract
48+
4249 deriving (Eq , Show )
4350
4451instance FromJSON Etheno where
4552 parseJSON = withObject " Etheno" $ \ v -> do
4653 (ev :: String ) <- v .: " event"
4754 let gu = maybe (M. fail " could not parse gas_used" ) pure . readMaybe =<< v .: " gas_used"
4855 gp = maybe (M. fail " could not parse gas_price" ) pure . readMaybe =<< v .: " gas_price"
56+ ni = maybe (M. fail " could not parse number_increase" ) pure . readMaybe =<< v .: " number_increment"
57+ ti = maybe (M. fail " could not parse timestamp_increase" ) pure . readMaybe =<< v .: " timestamp_increment"
4958 case ev of
5059 " AccountCreated" -> AccountCreated <$> v .: " address"
5160 " ContractCreated" -> ContractCreated <$> v .: " from"
@@ -60,6 +69,9 @@ instance FromJSON Etheno where
6069 <*> gp
6170 <*> (decode =<< (v .: " data" ))
6271 <*> v .: " value"
72+ " BlockMined" -> BlockMined <$> ni
73+ <*> ti
74+
6375 _ -> M. fail " event should be one of \" AccountCreated\" , \" ContractCreated\" , or \" FunctionCall\" "
6476 where decode x = case BS16. decode . encodeUtf8 . T. drop 2 $ x of
6577 (a, " " ) -> pure a
@@ -75,68 +87,70 @@ instance Show EthenoException where
7587
7688instance Exception EthenoException
7789
90+ loadEtheno :: (MonadThrow m , MonadIO m , M. MonadFail m )
91+ => FilePath -> m [Etheno ]
92+ loadEtheno fp = do
93+ bs <- liftIO $ eitherDecodeFileStrict fp
94+
95+ case bs of
96+ (Left e) -> throwM $ EthenoException e
97+ (Right (ethenoInit :: [Etheno ])) -> return ethenoInit
98+
99+ extractFromEtheno :: [Etheno ] -> [SolSignature ] -> [Tx ]
100+ extractFromEtheno ess ss = case ess of
101+ (BlockMined ni ti : es) -> Tx NoCall 0 0 0 0 0 (fromInteger ti, fromInteger ni) : extractFromEtheno es ss
102+ (c@ FunctionCall {} : es) -> concatMap (`matchSignatureAndCreateTx` c) ss ++ extractFromEtheno es ss
103+ (_: es) -> extractFromEtheno es ss
104+ _ -> []
105+
106+ matchSignatureAndCreateTx :: SolSignature -> Etheno -> [Tx ]
107+ matchSignatureAndCreateTx (" " , [] ) _ = [] -- Not sure if we should match this.
108+ matchSignatureAndCreateTx (s,ts) (FunctionCall a d _ _ bs v) =
109+ if BS. take 4 bs == selector (encodeSig (s,ts))
110+ then makeSingleTx a d v $ SolCall (s, fromTuple $ decodeAbiValue t (LBS. fromStrict $ BS. drop 4 bs))
111+ else []
112+ where t = AbiTupleType (V. fromList ts)
113+ fromTuple (AbiTuple xs) = V. toList xs
114+ fromTuple _ = []
115+ matchSignatureAndCreateTx _ _ = []
116+
78117-- | Main function: takes a filepath where the initialization sequence lives and returns
79118-- | the initialized VM along with a list of Addr's to put in GenConf
80119loadEthenoBatch :: (MonadThrow m , MonadIO m , Has TxConf y , MonadReader y m , M. MonadFail m )
81- => [ T. Text ] -> FilePath -> m VM
82- loadEthenoBatch ts fp = do
120+ => FilePath -> m VM
121+ loadEthenoBatch fp = do
83122 bs <- liftIO $ eitherDecodeFileStrict fp
84123
85124 case bs of
86125 (Left e) -> throwM $ EthenoException e
87126 (Right (ethenoInit :: [Etheno ])) -> do
88127 -- Execute contract creations and initial transactions,
89- let initVM = foldM (execEthenoTxs ts) Nothing ethenoInit
90-
91- (addr, vm') <- runStateT initVM initialVM
92- case addr of
93- Nothing -> throwM $ EthenoException " Could not find a contract with echidna tests"
94- Just a -> execStateT (liftSH . loadContract $ a) vm'
128+ let initVM = foldM execEthenoTxs () ethenoInit
129+ (_, vm') <- runStateT initVM initialVM
130+ return vm'
95131
96132-- | Takes a list of Etheno transactions and loads them into the VM, returning the
97133-- | address containing echidna tests
98134execEthenoTxs :: (MonadState x m , Has VM x , MonadThrow m , Has TxConf y , MonadReader y m , M. MonadFail m )
99- => [ T. Text ] -> Maybe Addr -> Etheno -> m (Maybe Addr )
100- execEthenoTxs ts addr et = do
135+ => () -> Etheno -> m ()
136+ execEthenoTxs _ et = do
101137 setupEthenoTx et
138+ sb <- get
102139 res <- liftSH exec
103- g <- view (hasLens . propGas)
104140 case (res, et) of
105- (Reversion , _) -> throwM $ EthenoException " Encountered reversion while setting up Etheno transactions"
141+ (_ , AccountCreated _) -> return ()
142+ (Reversion , _) -> void $ put sb
106143 (VMFailure x, _) -> vmExcept x >> M. fail " impossible"
107144 (VMSuccess (ConcreteBuffer bc),
108145 ContractCreated _ ca _ _ _ _) -> do
109146 hasLens . env . contracts . at ca . _Just . contractcode .= InitCode " "
110147 liftSH (replaceCodeOfSelf (RuntimeCode bc) >> loadContract ca)
111- og <- get
112- -- See if current contract is the same as echidna test
113- case addr of
114- -- found the tests, so just return the contract
115- Just m -> return $ Just m
116- -- try to see if this is the contract we wish to test
117- Nothing -> let txs = ts <&> \ t -> basicTx t [] ca ca g
118- -- every test was executed successfully
119- go [] = return (Just ca)
120- -- execute x and check if it returned something of the correct type
121- go (x: xs) = setupTx x >> liftSH exec >>= \ case
122- -- executing the test function succeeded
123- VMSuccess (ConcreteBuffer r) -> do
124- put og
125- case runGetOrFail (getAbi . AbiTupleType . V. fromList $ [AbiBoolType ]) (r ^. lazy) ^? _Right . _3 of
126- -- correct type ==> check the rest of the tests
127- Just _ -> go xs
128- -- incorrect type ==> bad ABI, this is not the contract we wish to test
129- Nothing -> return Nothing
130- -- some vm failure or reversion, not what we want
131- -- TODO: this breaks any test that is supposed to revert, maybe add a check here?
132- _ -> put og >> return Nothing in
133- -- actually test everything
134- go txs
135- _ -> return addr
136-
148+ return ()
149+ _ -> return ()
137150
138151-- | For an etheno txn, set up VM to execute txn
139152setupEthenoTx :: (MonadState x m , Has VM x ) => Etheno -> m ()
140153setupEthenoTx (AccountCreated _) = pure ()
141- setupEthenoTx (ContractCreated f c _ _ d v) = setupTx $ createTxWithValue d f c (fromInteger unlimitedGasPerBlock) (w256 v)
142- setupEthenoTx (FunctionCall f t _ _ d v) = setupTx $ Tx (SolCalldata d) f t (fromInteger unlimitedGasPerBlock) 0 (w256 v) (0 , 0 )
154+ setupEthenoTx (ContractCreated f c _ _ d v) = setupTx $ createTxWithValue d f c (fromInteger unlimitedGasPerBlock) (w256 v) (1 , 1 )
155+ setupEthenoTx (FunctionCall f t _ _ d v) = setupTx $ Tx (SolCalldata d) f t (fromInteger unlimitedGasPerBlock) 0 (w256 v) (1 , 1 )
156+ setupEthenoTx (BlockMined n t) = setupTx $ Tx NoCall 0 0 0 0 0 (fromInteger t, fromInteger n)
0 commit comments