@@ -6,27 +6,29 @@ module Echidna.Transaction where
66import Optics.Core
77import Optics.State.Operators
88
9- import Control.Monad (join )
9+ import Control.Monad (join , when )
1010import Control.Monad.IO.Class (MonadIO , liftIO )
1111import Control.Monad.Random.Strict (MonadRandom , getRandomR , uniform )
1212import Control.Monad.Reader (MonadReader , ask )
1313import Control.Monad.State.Strict (MonadState , gets , modify' , execState )
1414import Control.Monad.ST (RealWorld )
15+ import Data.ByteString qualified as BS
1516import Data.Map (Map , toList )
1617import Data.Maybe (catMaybes )
1718import Data.Set (Set )
1819import Data.Set qualified as Set
1920import Data.Vector qualified as V
2021
21- import EVM (initialContract , loadContract , resetState )
22+ import EVM (ceilDiv , initialContract , loadContract , resetState )
2223import EVM.ABI (abiValueType )
23- import EVM.Types hiding (Env , VMOpts (timestamp , gasprice ))
24+ import EVM.FeeSchedule (FeeSchedule (.. ))
25+ import EVM.Types hiding (Env , Gas , VMOpts (timestamp , gasprice ))
2426
2527import Echidna.ABI
2628import Echidna.Orphans.JSON ()
2729import Echidna.SourceMapping (lookupUsingCodehash )
2830import Echidna.Symbolic (forceWord , forceAddr )
29- import Echidna.Types (fromEVM )
31+ import Echidna.Types (fromEVM , Gas )
3032import Echidna.Types.Config (Env (.. ), EConfig (.. ))
3133import Echidna.Types.Random
3234import Echidna.Types.Signature
@@ -177,25 +179,49 @@ setupTx tx@Tx{call} = fromEVM $ do
177179 , block = advanceBlock vm. block tx. delay
178180 , tx = vm. tx { gasprice = tx. gasprice, origin = LitAddr tx. src }
179181 }
180- case call of
181- SolCreate bc -> do
182- # env % # contracts % at ( LitAddr tx. dst) .=
183- Just (initialContract ( InitCode bc mempty ) & set # balance ( Lit tx. value) )
184- modify' $ execState $ loadContract ( LitAddr tx . dst )
185- # state % # code .= RuntimeCode ( ConcreteRuntimeCode bc)
186- SolCall cd -> do
187- incrementBalance
188- modify' $ execState $ loadContract ( LitAddr tx . dst)
189- # state % # calldata .= ConcreteBuf (encode cd)
190- SolCalldata cd -> do
191- incrementBalance
192- modify' $ execState $ loadContract ( LitAddr tx . dst)
193- # state % # calldata .= ConcreteBuf cd
182+ when isCreate $ do
183+ # env % # contracts % at ( LitAddr tx . dst) .=
184+ Just (initialContract ( InitCode calldata mempty ) & set # balance ( Lit tx. value))
185+ modify' $ execState $ loadContract ( LitAddr tx. dst )
186+ # state % # code .= RuntimeCode ( ConcreteRuntimeCode calldata )
187+ when isCall $ do
188+ incrementBalance
189+ modify' $ execState $ loadContract ( LitAddr tx . dst)
190+ # state % # calldata .= ConcreteBuf calldata
191+ modify' $ \ vm ->
192+ let intrinsicGas = txGasCost vm . block . schedule isCreate calldata
193+ burned = min intrinsicGas vm . state . gas
194+ in vm & # state % # gas %!~ subtract burned
195+ & # burned %!~ ( + burned)
194196 where
195197 incrementBalance = # env % # contracts % ix (LitAddr tx. dst) % # balance %= (\ v -> Lit $ forceWord v + tx. value)
196198 encode (n, vs) = abiCalldata (encodeSig (n, abiValueType <$> vs)) $ V. fromList vs
199+ isCall = case call of
200+ SolCall _ -> True
201+ SolCalldata _ -> True
202+ _ -> False
203+ isCreate = case call of
204+ SolCreate _ -> True
205+ _ -> False
206+ calldata = case call of
207+ SolCreate bc -> bc
208+ SolCall cd -> encode cd
209+ SolCalldata cd -> cd
197210
198211advanceBlock :: Block -> (W256 , W256 ) -> Block
199212advanceBlock blk (t,b) =
200213 blk { timestamp = Lit (forceWord blk. timestamp + t)
201214 , number = Lit (forceWord blk. number + b) }
215+
216+ -- | Calculate transaction gas cost for Echidna Tx
217+ -- Adapted from HEVM's txGasCost function
218+ txGasCost :: FeeSchedule Gas -> Bool -> BS. ByteString -> Gas
219+ txGasCost fs isCreate calldata = baseCost + zeroCost + nonZeroCost
220+ where
221+ zeroBytes = BS. count 0 calldata
222+ nonZeroBytes = BS. length calldata - zeroBytes
223+ baseCost = fs. g_transaction
224+ + (if isCreate then fs. g_txcreate + initcodeCost else 0 )
225+ zeroCost = fs. g_txdatazero * fromIntegral zeroBytes
226+ nonZeroCost = fs. g_txdatanonzero * fromIntegral nonZeroBytes
227+ initcodeCost = fs. g_initcodeword * fromIntegral (ceilDiv (BS. length calldata) 32 )
0 commit comments