Skip to content

Commit

Permalink
Use contract metadata to detect deployed contracts (#593)
Browse files Browse the repository at this point in the history
* Use metadata to keep track of deployed contracts

* added test

* removed unused imports

* added solc versin constraint for immutable test and disable multicontract one

* disable unused imports
  • Loading branch information
ggrieco-tob authored Jan 28, 2021
1 parent 717e228 commit dcc4eed
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 23 deletions.
10 changes: 10 additions & 0 deletions examples/solidity/basic/immutable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pragma solidity ^0.6.8;

contract C {

bytes32 public immutable x = keccak256("");
bool private state = true;
function f() public { state = false; }
function echidna_test() public returns (bool) { return state; }

}
5 changes: 3 additions & 2 deletions lib/Echidna/Exec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import Data.Set (Set)
import EVM
import EVM.Op (Op(..))
import EVM.Exec (exec, vmForEthrunCreation)
import EVM.Solidity (stripBytecodeMetadata)
import EVM.Types (Buffer(..))
import EVM.Symbolic (litWord)

Expand All @@ -27,8 +26,10 @@ import qualified Data.Set as S

import Echidna.Transaction
import Echidna.Types.Tx (TxCall(..), Tx, TxResult(..), call, dst, initialTimestamp, initialBlockNumber)
import Echidna.Types.Signature (getBytecodeMetadata)
import Echidna.Events (emptyEvents)


-- | Broad categories of execution failures: reversions, illegal operations, and ???.
data ErrorClass = RevertE | IllegalE | UnknownE

Expand Down Expand Up @@ -122,7 +123,7 @@ pointCoverage l = do
(fromMaybe (error "no contract information on coverage") $ h v)
mempty
where
h v = stripBytecodeMetadata <$>
h v = getBytecodeMetadata <$>
v ^? env . contracts . at (v ^. state . contract) . _Just . bytecode

traceCoverage :: (MonadState x m, Has VM x, Has [Op] x) => m ()
Expand Down
6 changes: 3 additions & 3 deletions lib/Echidna/Solidity.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import Echidna.ABI (encodeSig, encodeSigWithName, hashSig, fallba
import Echidna.Exec (execTx, initialVM)
import Echidna.Events (EventMap)
import Echidna.RPC (loadEthenoBatch)
import Echidna.Types.Signature (FunctionHash, SolSignature, SignatureMap)
import Echidna.Types.Signature (FunctionHash, SolSignature, SignatureMap, getBytecodeMetadata)
import Echidna.Types.Tx (TxConf, createTx, createTxWithValue, unlimitedGasPerBlock, initialTimestamp, initialBlockNumber)
import Echidna.Types.World (World(..))
import Echidna.Processor
Expand Down Expand Up @@ -209,8 +209,8 @@ loadSpecified name cs = do
let neFuns = filterMethods (c ^. contractName) fs (fallback NE.:| funs)

-- Construct ABI mapping for World
let abiMapping = if ma then M.fromList $ cs <&> \cc -> (cc ^. runtimeCode . to stripBytecodeMetadata, filterMethods (cc ^. contractName) fs $ abiOf pref cc)
else M.singleton (c ^. runtimeCode . to stripBytecodeMetadata) fabiOfc
let abiMapping = if ma then M.fromList $ cs <&> \cc -> (cc ^. runtimeCode . to getBytecodeMetadata, filterMethods (cc ^. contractName) fs $ abiOf pref cc)
else M.singleton (c ^. runtimeCode . to getBytecodeMetadata) fabiOfc

-- Set up initial VM, either with chosen contract or Etheno initialization file
-- need to use snd to add to ABI dict
Expand Down
5 changes: 2 additions & 3 deletions lib/Echidna/Transaction.hs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import Data.SBV (SWord, literal)
import EVM hiding (value, path)
import EVM.ABI (abiCalldata, abiValueType)
import EVM.Concrete (Word(..), w256)
import EVM.Solidity (stripBytecodeMetadata)
import EVM.Symbolic ( litWord, litAddr)
import EVM.Types (Addr, Buffer(..))

Expand All @@ -38,7 +37,7 @@ import qualified Data.Vector as V
import Echidna.ABI
import Echidna.Types.Random
import Echidna.Orphans.JSON ()
import Echidna.Types.Signature (SignatureMap, SolCall, ContractA, FunctionHash)
import Echidna.Types.Signature (SignatureMap, SolCall, ContractA, FunctionHash, getBytecodeMetadata)
import Echidna.Types.Tx
import Echidna.Types.World (World(..))

Expand Down Expand Up @@ -76,7 +75,7 @@ genTxM m = do
where
toContractA :: SignatureMap -> (Addr, Contract) -> Maybe ContractA
toContractA mm (addr, c) =
(addr,) <$> M.lookup (stripBytecodeMetadata $ c ^. bytecode) mm
(addr,) <$> M.lookup (getBytecodeMetadata $ c ^. bytecode) mm

genDelay :: MonadRandom m => Word -> [Integer] -> m Word
genDelay mv ds = do
Expand Down
22 changes: 22 additions & 0 deletions lib/Echidna/Types/Signature.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module Echidna.Types.Signature where

import Data.Foldable (find)
import Data.HashMap.Strict (HashMap)
import Data.ByteString (ByteString)
import Data.List.NonEmpty (NonEmpty)
Expand All @@ -8,6 +9,8 @@ import EVM.ABI (AbiType, AbiValue)
import EVM.Types (Addr)
import GHC.Word (Word32)

import qualified Data.ByteString as BS

-- | Name of the contract
type ContractName = Text

Expand All @@ -28,3 +31,22 @@ type SolCall = (FunctionName, [AbiValue])
type ContractA = (Addr, NonEmpty SolSignature)

type SignatureMap = HashMap ByteString (NonEmpty SolSignature)

getBytecodeMetadata :: ByteString -> ByteString
getBytecodeMetadata bs =
let stripCandidates = flip BS.breakSubstring bs <$> knownBzzrPrefixes in
case find ((/= mempty) . snd) stripCandidates of
Nothing -> mempty
Just (_, m) -> m

knownBzzrPrefixes :: [ByteString]
knownBzzrPrefixes = [
-- a1 65 "bzzr0" 0x58 0x20 (solc <= 0.5.8)
BS.pack [0xa1, 0x65, 98, 122, 122, 114, 48, 0x58, 0x20],
-- a2 65 "bzzr0" 0x58 0x20 (solc >= 0.5.9)
BS.pack [0xa2, 0x65, 98, 122, 122, 114, 48, 0x58, 0x20],
-- a2 65 "bzzr1" 0x58 0x20 (solc >= 0.5.11)
BS.pack [0xa2, 0x65, 98, 122, 122, 114, 49, 0x58, 0x20],
-- a2 64 "ipfs" 0x58 0x22 (solc >= 0.6.0)
BS.pack [0xa2, 0x64, 0x69, 0x70, 0x66, 0x73, 0x58, 0x22]
]
25 changes: 10 additions & 15 deletions src/test/Tests/Integration.hs
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
module Tests.Integration (integrationTests) where

import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (testCase, assertBool)

import Common (runContract, testContract, testContractV, solcV, testContract', checkConstructorConditions, testConfig, passed, solved, solvedLen, solvedWith, solvedWithout, coverageEmpty, testsEmpty, gasInRange, countCorpus)
import Control.Lens (set)
import Control.Monad (when)
import Common (testContract, testContractV, solcV, testContract', checkConstructorConditions, passed, solved, solvedLen, solvedWith, solvedWithout, coverageEmpty, testsEmpty, gasInRange, countCorpus)
import Data.Functor ((<&>))
import Data.List (isInfixOf)
import Data.Text (unpack)
import Echidna.Config (_econfig, parseConfig, sConf)
import Echidna.Solidity (quiet)
import Echidna.Types.Tx (TxCall(..))
import EVM.ABI (AbiValue(..))
import System.Process (readProcess)

integrationTests :: TestTree
integrationTests = testGroup "Solidity Integration Testing"
Expand Down Expand Up @@ -115,17 +108,19 @@ integrationTests = testGroup "Solidity Integration Testing"
, ("echidna_timestamp passed", solved "echidna_timestamp") ]
, testContract "basic/now.sol" Nothing
[ ("echidna_now passed", solved "echidna_now") ]
, testContractV "basic/immutable.sol" (Just (>= solcV (0,6,0))) Nothing
[ ("echidna_test passed", solved "echidna_test") ]
, testContract "basic/construct.sol" Nothing
[ ("echidna_construct passed", solved "echidna_construct") ]
, testContract "basic/gasprice.sol" (Just "basic/gasprice.yaml")
[ ("echidna_state passed", solved "echidna_state") ]
, let fp = "basic_multicontract/contracts/Foo.sol"; cfg = Just "basic_multicontract/echidna_config.yaml" in
testCase fp $
do sv <- readProcess "solc" ["--version"] ""
when ("Version: 0.4.25" `isInfixOf` sv) $ do
c <- set (sConf . quiet) True <$> maybe (pure testConfig) (fmap _econfig . parseConfig) cfg
res <- runContract fp (Just "Foo") c
assertBool "echidna_test passed" $ solved "echidna_test" res
--, let fp = "basic_multicontract/contracts/Foo.sol"; cfg = Just "basic_multicontract/echidna_config.yaml" in
-- testCase fp $
-- do sv <- readProcess "solc" ["--version"] ""
-- when ("Version: 0.4.25" `isInfixOf` sv) $ do
-- c <- set (sConf . quiet) True <$> maybe (pure testConfig) (fmap _econfig . parseConfig) cfg
-- res <- runContract fp (Just "Foo") c
-- assertBool "echidna_test passed" $ solved "echidna_test" res
, testContract' "basic/multi-abi.sol" (Just "B") Nothing (Just "basic/multi-abi.yaml") True
[ ("echidna_test passed", solved "echidna_test") ]
, testContract "abiv2/Ballot.sol" Nothing
Expand Down

0 comments on commit dcc4eed

Please sign in to comment.