diff --git a/lib/Echidna/ABI.hs b/lib/Echidna/ABI.hs index 28aee7663..d5b8214ab 100644 --- a/lib/Echidna/ABI.hs +++ b/lib/Echidna/ABI.hs @@ -47,7 +47,10 @@ import Echidna.Types.Signature -- | Fallback function is the null string fallback :: SolSignature -fallback = ("",[]) +fallback = ("*fallback*",[]) + +receive :: SolSignature +receive = ("*receive*",[]) commonTypeSizes :: [Int] commonTypeSizes = [8,16..256] diff --git a/lib/Echidna/Solidity.hs b/lib/Echidna/Solidity.hs index 7ef1dd01c..9f78d1e2e 100644 --- a/lib/Echidna/Solidity.hs +++ b/lib/Echidna/Solidity.hs @@ -25,7 +25,7 @@ import System.Exit (ExitCode(..)) import System.Directory (doesDirectoryExist, doesFileExist, findExecutable, listDirectory, removeFile) import System.FilePath.Posix (()) -import Echidna.ABI (encodeSig, encodeSigWithName, hashSig, fallback, commonTypeSizes, mkValidAbiInt, mkValidAbiUInt) +import Echidna.ABI (encodeSig, encodeSigWithName, hashSig, fallback, receive, commonTypeSizes, mkValidAbiInt, mkValidAbiUInt) import Echidna.Exec (execTx, initialVM) import Echidna.Events (EventMap) import Echidna.Test (createTests, isAssertionMode, isPropertyMode) @@ -144,7 +144,7 @@ filterMethods cn f@(Blacklist ig) ms = case NE.filter (\s -> encodeSigWithName c fs -> NE.fromList fs abiOf :: Text -> SolcContract -> NE.NonEmpty SolSignature -abiOf pref cc = fallback NE.:| filter (not . isPrefixOf pref . fst) (elems (cc ^. abiMap) <&> \m -> (m ^. methodName, m ^.. methodInputs . traverse . _2)) +abiOf pref cc = fallback NE.<| (receive NE.:| filter (not . isPrefixOf pref . fst) (elems (cc ^. abiMap) <&> \m -> (m ^. methodName, m ^.. methodInputs . traverse . _2))) -- | Given an optional contract name and a list of 'SolcContract's, try to load the specified -- contract, or, if not provided, the first contract in the list, into a 'VM' usable for Echidna diff --git a/lib/Echidna/Transaction.hs b/lib/Echidna/Transaction.hs index df0c9af89..51bc61e1d 100644 --- a/lib/Echidna/Transaction.hs +++ b/lib/Echidna/Transaction.hs @@ -153,10 +153,12 @@ setupTx (Tx c s r g gp v (t, b)) = liftSH . sequence_ $ , tx . gasprice .= gp, tx . origin .= s, state . caller .= litAddr s, state . callvalue .= litWord v , block . timestamp += litWord t, block . number += b, setup] where setup = case c of - SolCreate bc -> assign (env . contracts . at r) (Just $ initialContract (InitCode (ConcreteBuffer bc)) & set balance v) >> loadContract r >> state . code .= ConcreteBuffer bc - SolCall cd -> incrementBalance >> loadContract r >> state . calldata .= concreteCalldata (encode cd) - SolCalldata cd -> incrementBalance >> loadContract r >> state . calldata .= concreteCalldata cd - NoCall -> error "NoCall" + SolCreate bc -> assign (env . contracts . at r) (Just $ initialContract (InitCode (ConcreteBuffer bc)) & set balance v) >> loadContract r >> state . code .= ConcreteBuffer bc + SolCall ("*receive*",_) -> incrementBalance >> loadContract r >> state . calldata .= concreteCalldata "" + SolCall ("*fallback*",_) -> incrementBalance >> loadContract r >> state . calldata .= concreteCalldata "\0" + SolCall cd -> incrementBalance >> loadContract r >> state . calldata .= concreteCalldata (encode cd) + SolCalldata cd -> incrementBalance >> loadContract r >> state . calldata .= concreteCalldata cd + NoCall -> error "NoCall" incrementBalance = (env . contracts . ix r . balance) += v encode (n, vs) = abiCalldata (encodeSig (n, abiValueType <$> vs)) $ V.fromList vs diff --git a/src/test/Tests/Integration.hs b/src/test/Tests/Integration.hs index 091964b58..2caec9570 100644 --- a/src/test/Tests/Integration.hs +++ b/src/test/Tests/Integration.hs @@ -59,8 +59,6 @@ integrationTests = testGroup "Solidity Integration Testing" [ ("echidna_library_call failed", solved "echidna_library_call") , ("echidna_valid_timestamp failed", passed "echidna_valid_timestamp") ] - , testContractV "basic/fallback.sol" (Just (< solcV (0,6,0))) Nothing - [ ("echidna_fallback failed", solved "echidna_fallback") ] , testContract "basic/push_long.sol" (Just "basic/push_long.yaml") [ ("test_long_5 passed", solvedWithout NoCall "test_long_5")] , testContract "basic/propGasLimit.sol" (Just "basic/propGasLimit.yaml") diff --git a/src/test/Tests/Values.hs b/src/test/Tests/Values.hs index e2b6d7873..36b109b9d 100644 --- a/src/test/Tests/Values.hs +++ b/src/test/Tests/Values.hs @@ -2,7 +2,7 @@ module Tests.Values (valuesTests) where import Test.Tasty (TestTree, testGroup) -import Common (testContract, testContract', solved, solvedLen) +import Common (testContract, testContract', testContractV, solcV, solved, solvedLen) valuesTests :: TestTree valuesTests = testGroup "Value extraction tests" @@ -33,5 +33,7 @@ valuesTests = testGroup "Value extraction tests" , testContract "values/darray.sol" Nothing [ ("echidna_darray passed", solved "echidna_darray") , ("echidna_darray didn't shrink optimally", solvedLen 1 "echidna_darray") ] - + , testContractV "values/receive.sol" (Just (>= solcV (0,6,0))) Nothing + [ ("echidna_fallback failed", solved "echidna_fallback") + , ("echidna_receive failed", solved "echidna_receive") ] ] diff --git a/tests/solidity/values/receive.sol b/tests/solidity/values/receive.sol new file mode 100644 index 000000000..0dbb67e33 --- /dev/null +++ b/tests/solidity/values/receive.sol @@ -0,0 +1,22 @@ +pragma solidity ^0.6.0; + +contract Receive { + bool found_fallback = false; + bool found_receive = false; + + function echidna_found_fallback() public view returns (bool) { + return(!found_fallback); + } + + function echidna_found_receive() public view returns (bool) { + return(!found_receive); + } + + fallback() external payable { + found_fallback = true; + } + + receive() external payable { + found_receive = true; + } +}