mirror of
https://github.com/crytic/echidna.git
synced 2026-10-12 02:12:32 +00:00
The decomposition is a slice cut out of this branch and reworked on its own, so it wins every conflict: the agent layer is rebuilt on top of it rather than alongside it. Both sides had split Echidna.Campaign the same way under different names, and keeping both copies would have meant carrying the same code twice and replaying this merge when the slice lands in master. Echidna.Execution -> Echidna.Worker.Sequence + Echidna.Test.State Echidna.Agent.Fuzzer -> Echidna.Worker.Fuzz Echidna.Agent.Symbolic -> Echidna.Worker.Symbolic Campaign.spawnListener -> Worker.spawnListener Worker.Sequence keeps the two things Execution had added: callseq's isReplaying flag with the NewCoverageInfo bus broadcast, and the sampledFunctions update behind a Map.null guard. Everything else it duplicated is gone, including the second copy of updateTests that Shrink was already importing from Test.State. The workers keep the bus and message handling that made them agents, and lose the helpers the decomposition had already factored out: the hand-rolled WorkerState is initialWorkerState, the local shrinkable and final predicates are isShrinkable and isConclusiveFailure, and the inlined assertion-test updates are setAssertionTestState. That drops three defects the agent copies had picked up: an unbounded shrinkable predicate, a shrinkAndRandomlyExplore that recursed on the same counter, and a symexecTxs call passing Nothing where the CLI-selected contract belonged. The symbolic worker goes back to the event queue. Its bus listener counted Broadcast WorkerStopped to know when to exit, but nothing has ever sent that message, so it would have blocked on readTChan once the fuzzers finished. The ToSymbolic commands it also handled have no senders either; a listener that can block on the queue and the bus at once is what they need. Config, Solidity, Test and the foundry fixtures take the decomposition's version of the verification test mode and the Foundry check/prove handling, which is the reworked form of what this branch had drafted. Agent.runAgent now returns WorkerStopReason instead of pushing WorkerStopped itself. An agent killed by the campaign timeout never reaches its own last line, so spawnWorker had to push the event too, and the shrink-only pass it then runs pushed a second one for the same worker. Listeners retire a worker per event, so that shrink pass could end a campaign early. Reporting the reason to the caller, as runWorker did, keeps it to one event per worker.