11module Echidna.ABI where
22
3- import Control.Monad (liftM2 , liftM3 , foldM , replicateM )
3+ import Control.Monad (liftM2 , liftM3 , foldM , replicateM , zipWithM )
44import Control.Monad.Random.Strict (MonadRandom , join , getRandom , getRandoms , getRandomR , uniform , fromList )
55import Control.Monad.Random.Strict qualified as Random
66import Data.Binary.Put (runPut , putWord32be )
@@ -121,6 +121,8 @@ data GenDict = GenDict
121121 -- ^ Return types of any methods we scrape return values from
122122 , dictValues :: ! (Set W256 )
123123 -- ^ A set of int/uint constants for better performance
124+ , callbackSigs :: ! [SolSignature ]
125+ -- ^ A list of callback signatures (for generating random callbacks)
124126 }
125127
126128hashMapBy
@@ -142,6 +144,7 @@ mkGenDict
142144 -> Set SolCall -- ^ A list of complete 'SolCall's to mutate
143145 -> Int -- ^ A default seed
144146 -> (Text -> Maybe AbiType ) -- ^ A return value typing rule
147+ -> [SolSignature ]
145148 -> GenDict
146149mkGenDict mutationChance abiValues solCalls seed typingRule =
147150 GenDict mutationChance
@@ -152,7 +155,7 @@ mkGenDict mutationChance abiValues solCalls seed typingRule =
152155 (mkDictValues abiValues)
153156
154157emptyDict :: GenDict
155- emptyDict = mkGenDict 0 Set. empty Set. empty 0 (const Nothing )
158+ emptyDict = mkGenDict 0 Set. empty Set. empty 0 (const Nothing ) []
156159
157160mkDictValues :: Set AbiValue -> Set W256
158161mkDictValues =
@@ -382,31 +385,50 @@ pregenAbiAdds = map (AbiAddress . fromIntegral) pregenAdds
382385-- | Synthesize a random 'AbiValue' given its 'AbiType'. Requires a dictionary.
383386-- Only produce lists with number of elements in the range [1, 32]
384387genAbiValueM :: MonadRandom m => GenDict -> AbiType -> m AbiValue
385- genAbiValueM genDict = genWithDict genDict genDict. constants $ \ case
386- AbiUIntType n -> fixAbiUInt n . fromInteger <$> getRandomUint n
387- AbiIntType n -> fixAbiInt n . fromInteger <$> getRandomInt n
388- AbiAddressType -> rElem $ NE. fromList pregenAbiAdds
389- AbiBoolType -> AbiBool <$> getRandom
390- AbiBytesType n -> AbiBytes n . BS. pack . take n <$> getRandoms
391- AbiBytesDynamicType -> liftM2 (\ n -> AbiBytesDynamic . BS. pack . take n)
392- (getRandomR (1 , 32 )) getRandoms
393- AbiStringType -> liftM2 (\ n -> AbiString . BS. pack . take n)
394- (getRandomR (1 , 32 )) getRandoms
395- AbiArrayDynamicType t -> fmap (AbiArrayDynamic t) $ getRandomR (1 , 32 )
396- >>= flip V. replicateM (genAbiValueM genDict t)
397- AbiArrayType n t -> AbiArray n t <$> V. replicateM n (genAbiValueM genDict t)
398- AbiTupleType v -> AbiTuple <$> traverse (genAbiValueM genDict) v
399- AbiFunctionType -> liftM2 (\ n -> AbiString . BS. pack . take n)
400- (getRandomR (1 , 32 )) getRandoms
388+ genAbiValueM genDict = genAbiValueM' genDict " " 0
389+
390+ genAbiValueM' :: MonadRandom m => GenDict -> Text -> Int -> AbiType -> m AbiValue
391+ genAbiValueM' genDict funcName depth t =
392+ let go = \ case
393+ AbiUIntType n -> fixAbiUInt n . fromInteger <$> getRandomUint n
394+ AbiIntType n -> fixAbiInt n . fromInteger <$> getRandomInt n
395+ AbiAddressType -> rElem $ NE. fromList pregenAbiAdds
396+ AbiBoolType -> AbiBool <$> getRandom
397+ AbiBytesType n -> AbiBytes n . BS. pack . take n <$> getRandoms
398+ AbiBytesDynamicType ->
399+ let
400+ filteredSigs = filter ((/= funcName) . fst ) genDict. callbackSigs
401+ in if null filteredSigs || depth >= 2 then
402+ liftM2 (\ n -> AbiBytesDynamic . BS. pack . take n)
403+ (getRandomR (1 , 32 )) getRandoms
404+ else
405+ join $ Random. weighted
406+ [ (do
407+ sig@ (_, types) <- uniform filteredSigs
408+ params <- V. fromList <$> mapM (genAbiValueM' genDict " " $ depth + 1 ) types
409+ pure $ AbiBytesDynamic (abiCalldata (encodeSig sig) params), 9 )
410+ , (liftM2 (\ n -> AbiBytesDynamic . BS. pack . take n)
411+ (getRandomR (1 , 8 )) getRandoms, 1 )
412+ ]
413+ AbiStringType -> liftM2 (\ n -> AbiString . BS. pack . take n)
414+ (getRandomR (1 , 32 )) getRandoms
415+ AbiArrayDynamicType t' -> fmap (AbiArrayDynamic t') $ getRandomR (1 , 32 )
416+ >>= flip V. replicateM (genAbiValueM' genDict funcName (depth + 1 ) t')
417+ AbiArrayType n t' -> AbiArray n t' <$> V. replicateM n (genAbiValueM' genDict funcName (depth + 1 ) t')
418+ AbiTupleType v -> AbiTuple <$> traverse (genAbiValueM' genDict funcName (depth + 1 )) v
419+ AbiFunctionType -> liftM2 (\ n -> AbiString . BS. pack . take n)
420+ (getRandomR (1 , 32 )) getRandoms
421+ in genWithDict genDict genDict. constants go t
401422
402423-- | Given a 'SolSignature', generate a random 'SolCall' with that signature,
403424-- possibly with a dictionary.
404425genAbiCallM :: MonadRandom m => GenDict -> SolSignature -> m SolCall
405- genAbiCallM genDict abi = do
426+ genAbiCallM genDict (name, types) = do
427+ let genVals = zipWithM (flip (genAbiValueM' genDict name)) types (repeat 0 )
406428 solCall <- genWithDict genDict
407429 genDict. wholeCalls
408- (traverse $ traverse (genAbiValueM genDict ))
409- abi
430+ (const ((name,) <$> genVals ))
431+ (name, types)
410432 mutateAbiCall solCall
411433
412434-- | Given a list of 'SolSignature's, generate a random 'SolCall' for one,
0 commit comments