Skip to content

Commit fb40b77

Browse files
refactoring and probabily tweaking
1 parent 84c9005 commit fb40b77

File tree

2 files changed

+47
-11
lines changed

2 files changed

+47
-11
lines changed

lib/Echidna/Agent/Fuzzer.hs

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ import Data.IORef (IORef, writeIORef, readIORef, atomicModifyIORef')
1919
import Data.Map (Map)
2020
import qualified Data.Map as Map
2121
import qualified Data.Set as Set
22+
import Data.Text (Text)
2223
import System.Directory (getCurrentDirectory)
2324

2425
import Echidna.Output.Source (saveLcovHook)
2526
import EVM.Dapp (DappInfo(..))
2627
import EVM.Types (VM(..), VMType(Concrete), Expr(..), EType(..), Contract)
2728
import qualified EVM.Types as EVM
2829

30+
import EVM.ABI (AbiValue)
2931
import Echidna.ABI (GenDict(..))
3032
import Echidna.Execution (replayCorpus, callseq, updateTests)
3133
import Echidna.Mutator.Corpus (getCorpusMutation, seqMutatorsStateless, seqMutatorsStateful, fromConsts)
@@ -226,59 +228,90 @@ randseq
226228
=> Map (Expr 'EAddr) Contract
227229
-> m [Tx]
228230
randseq deployedContracts = do
229-
env <- ask
230-
let world = env.world
231-
232-
let
233-
mutConsts = env.cfg.campaignConf.mutConsts
234-
seqLen = env.cfg.campaignConf.seqLen
235-
231+
-- 1. Check for prioritized sequences injected via tools
236232
prioritized <- gets (.prioritizedSequences)
237233

238234
mbSeq <- if null prioritized
239235
then pure Nothing
240236
else do
237+
-- Select a prioritized sequence based on probability
241238
(prob, seqPrototype) <- rElem (NE.fromList prioritized)
242239
useIt <- (<= prob) <$> getRandom
243240
pure $ if useIt then Just seqPrototype else Nothing
244241

245242
case mbSeq of
246-
Just seqPrototype -> do
243+
Just seqPrototype -> genPrioritizedSeq deployedContracts seqPrototype
244+
Nothing -> genStandardSeq deployedContracts
245+
246+
-- | Generate a sequence of transactions based on a prioritized prototype
247+
genPrioritizedSeq
248+
:: (MonadRandom m, MonadReader Env m, MonadState WorkerState m, MonadIO m)
249+
=> Map (Expr 'EAddr) Contract
250+
-> [(Text, [Maybe AbiValue])]
251+
-> m [Tx]
252+
genPrioritizedSeq deployedContracts seqPrototype = do
253+
env <- ask
254+
let world = env.world
255+
seqLen = env.cfg.campaignConf.seqLen
256+
257+
-- 2. If a prioritized sequence is selected:
258+
-- Expand the prototype into concrete transactions
247259
let expandPrototype [] = return []
248260
expandPrototype [p] = do
249261
tx <- genTxFromPrototype world deployedContracts p
250262
return [tx]
251263
expandPrototype (p:ps) = do
252264
tx <- genTxFromPrototype world deployedContracts p
265+
-- Insert random transactions between prototype transactions to increase fuzzing diversity
253266
n <- getRandomR (0, 3)
254267
rndTxs <- replicateM n (genTx world deployedContracts)
255268
rest <- expandPrototype ps
256269
return ((tx : rndTxs) ++ rest)
257270

258271
expandedTxs <- expandPrototype seqPrototype
259272
corpusSet <- liftIO $ readIORef env.corpusRef
260-
prefix <- if Set.null corpusSet
273+
wid <- gets (.workerId)
274+
275+
-- Select a prefix from the existing corpus
276+
-- Special handling for worker 0: always use empty prefix (position 0)
277+
prefix <- if Set.null corpusSet || wid == 0
261278
then pure []
262279
else do
280+
-- Pick a random sequence from corpus
263281
idx <- getRandomR (0, Set.size corpusSet - 1)
264282
let (_, cTxs) = Set.elemAt idx corpusSet
265283
let middleLen = length expandedTxs
266284
let maxPrefix = seqLen - middleLen
267285
if maxPrefix <= 0
268286
then pure []
269287
else do
288+
-- Take a random prefix length
270289
k <- getRandomR (0, min (length cTxs) maxPrefix)
271290
pure (take k cTxs)
272291

273292
let combined = prefix ++ expandedTxs
274293
let len = length combined
294+
295+
-- Pad with random transactions if sequence is too short
275296
if len < seqLen
276297
then do
277298
paddingTxs <- replicateM (seqLen - len) (genTx world deployedContracts)
278299
pure (combined ++ paddingTxs)
279300
else
280301
pure (take seqLen combined)
281-
Nothing -> do
302+
303+
-- | Generate a sequence of transactions using standard fuzzing techniques
304+
genStandardSeq
305+
:: (MonadRandom m, MonadReader Env m, MonadState WorkerState m, MonadIO m)
306+
=> Map (Expr 'EAddr) Contract
307+
-> m [Tx]
308+
genStandardSeq deployedContracts = do
309+
env <- ask
310+
let world = env.world
311+
mutConsts = env.cfg.campaignConf.mutConsts
312+
seqLen = env.cfg.campaignConf.seqLen
313+
314+
-- 3. Standard fuzzing behavior (no prioritized sequence selected)
282315
-- Generate new random transactions
283316
randTxs <- replicateM seqLen (genTx world deployedContracts)
284317
-- Generate a random mutator

lib/Echidna/MCP.hs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,11 @@ fuzzTransactionTool args env bus _ = do
262262
else do
263263
let nWorkers = getNFuzzWorkers env.cfg.campaignConf
264264
calcProb i
265-
| i == 0 = 0.0
265+
-- Worker 0 always injects transactions at position 0 with a probability of 90%
266+
| i == 0 = 0.9
267+
-- For small campaigns (<= 2 workers), all workers share a low probability (20%)
266268
| nWorkers <= 2 = 0.2
269+
-- For larger campaigns, scale probability linearly from 20% to 90% for other workers
267270
| otherwise = 0.2 + fromIntegral (i - 1) * (0.7 / fromIntegral (nWorkers - 2))
268271

269272
mapM_ (\i -> atomically $ writeTChan bus (WrappedMessage AIId (ToFuzzer i (FuzzSequence seqPrototype (calcProb i))))) [0 .. nWorkers - 1]

0 commit comments

Comments
 (0)