From 4bd81d3118516ea58a2af9ebebce9385d024f87a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Mon, 16 Sep 2024 13:57:48 +0200 Subject: [PATCH 1/3] Upgrade `hevm` to upstream `fix_prank` Commit 443c9040f6a8bde7d8b2cfafd99c0cb7dd7d049f --- flake.nix | 4 ++-- lib/Echidna/SymExec.hs | 6 ++++-- lib/Echidna/Types/Tx.hs | 2 +- stack.yaml | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/flake.nix b/flake.nix index 9225a5dfc..e456114a0 100644 --- a/flake.nix +++ b/flake.nix @@ -52,8 +52,8 @@ hevm = pkgs: pkgs.lib.pipe ((hsPkgs pkgs).callCabal2nix "hevm" (pkgs.fetchFromGitHub { owner = "ethereum"; repo = "hevm"; - rev = "f1f45d3c0d9767a38df04f398d1eab8b66dbe7fc"; - sha256 = "sha256-3zEUwcZm4uZZLecvFTgVTV5CAm4qMfKPbLdwO88LnrY="; + rev = "443c9040f6a8bde7d8b2cfafd99c0cb7dd7d049f"; + sha256 = "sha256-IC/q+2SJoyDansmbTHXfkKFfnhmMy97G13aTPNOkR30="; }) { secp256k1 = pkgs.secp256k1; }) ([ pkgs.haskell.lib.compose.dontCheck diff --git a/lib/Echidna/SymExec.hs b/lib/Echidna/SymExec.hs index 9aa5b6bc7..3fd67f9c4 100644 --- a/lib/Echidna/SymExec.hs +++ b/lib/Echidna/SymExec.hs @@ -77,7 +77,7 @@ exploreContract conf contract tx vm = do doneChan <- newEmptyMVar resultChan <- newEmptyMVar - flip runReaderT defaultEnv $ withSolvers Z3 (fromIntegral conf.campaignConf.symExecNSolvers) timeout $ \solvers -> do + flip runReaderT defaultEnv $ withSolvers Z3 (fromIntegral conf.campaignConf.symExecNSolvers) 1 timeout $ \solvers -> do threadId <- liftIO $ forkIO $ flip runReaderT defaultEnv $ do res <- forM methods $ \method -> do let @@ -108,7 +108,7 @@ exploreContract conf contract tx vm = do pure (threadId, resultChan) -- | Turn the expression returned by `interpret` into into SMT2 values to feed into the solver -manipulateExprInter :: Bool -> Expr End -> [SMT2] +manipulateExprInter :: Bool -> Expr End -> [Either String SMT2] manipulateExprInter isConc = map (assertProps defaultConfig) . middleStep . map (extractProps . simplify) . flattenExpr . simplify where middleStep = if isConc then middleStepConc else id middleStepConc = map singleton . concatMap (go (PBool True)) @@ -155,6 +155,8 @@ frameStateMakeSymbolic fs , gas = () , returndata = fs.returndata , static = fs.static + , overrideCaller = fs.overrideCaller + , resetCaller = fs.resetCaller } frameMakeSymbolic :: Frame Concrete s -> Frame Symbolic s diff --git a/lib/Echidna/Types/Tx.hs b/lib/Echidna/Types/Tx.hs index ea844eec0..7ff01d192 100644 --- a/lib/Echidna/Types/Tx.hs +++ b/lib/Echidna/Types/Tx.hs @@ -234,7 +234,7 @@ getResult = \case VMFailure BadJumpDestination -> ErrorBadJumpDestination VMFailure (Revert _) -> ErrorRevert VMFailure (OutOfGas _ _) -> ErrorOutOfGas - VMFailure (BadCheatCode _) -> ErrorBadCheatCode + VMFailure (BadCheatCode _ _) -> ErrorBadCheatCode VMFailure StackLimitExceeded -> ErrorStackLimitExceeded VMFailure IllegalOverflow -> ErrorIllegalOverflow VMFailure StateChangeWhileStatic -> ErrorStateChangeWhileStatic diff --git a/stack.yaml b/stack.yaml index 12c0d0699..261e3d398 100644 --- a/stack.yaml +++ b/stack.yaml @@ -5,7 +5,7 @@ packages: extra-deps: - git: https://github.com/ethereum/hevm.git - commit: f1f45d3c0d9767a38df04f398d1eab8b66dbe7fc + commit: 443c9040f6a8bde7d8b2cfafd99c0cb7dd7d049f - smt2-parser-0.1.0.1@sha256:1e1a4565915ed851c13d1e6b8bb5185cf5d454da3b43170825d53e221f753d77,1421 - spawn-0.3@sha256:b91e01d8f2b076841410ae284b32046f91471943dc799c1af77d666c72101f02,1162 From 4a38578fde8719ce084b7050447a2d453514cd62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Wed, 4 Dec 2024 19:18:15 +0100 Subject: [PATCH 2/3] Add test for `prank()` cheatcode --- src/test/Tests/Cheat.hs | 8 ++- tests/solidity/cheat/prank.sol | 94 +++++++++++++++++++++++++++++++++ tests/solidity/cheat/prank.yaml | 3 ++ 3 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 tests/solidity/cheat/prank.sol create mode 100644 tests/solidity/cheat/prank.yaml diff --git a/src/test/Tests/Cheat.hs b/src/test/Tests/Cheat.hs index 9666a7d50..cce8d37dd 100644 --- a/src/test/Tests/Cheat.hs +++ b/src/test/Tests/Cheat.hs @@ -2,7 +2,7 @@ module Tests.Cheat (cheatTests) where import Test.Tasty (TestTree, testGroup) -import Common (testContract', solcV, solved) +import Common (testContract', solcV, solved, passed) import Echidna.Types.Campaign (WorkerType(..)) @@ -15,4 +15,10 @@ cheatTests = [ ("echidna_ffi passed", solved "echidna_ffi") ] , testContract' "cheat/gas.sol" (Just "TestCheatGas") (Just (> solcV (0,5,0))) (Just "cheat/ffi.yaml") False FuzzWorker [ ("echidna_gas_zero passed", solved "echidna_gas_zero") ] + , testContract' "cheat/prank.sol" (Just "TestPrank") (Just (> solcV (0,5,0))) (Just "cheat/prank.yaml") False FuzzWorker + [ ("withPrank failed", passed "withPrank") + , ("withStartPrank failed", passed "withStartPrank") + , ("withStartPrankStopPrank failed", passed "withStartPrankStopPrank") + , ("withNothing failed", passed "withNothing") + ] ] diff --git a/tests/solidity/cheat/prank.sol b/tests/solidity/cheat/prank.sol new file mode 100644 index 000000000..125a8a023 --- /dev/null +++ b/tests/solidity/cheat/prank.sol @@ -0,0 +1,94 @@ +interface IHevm { + function prank(address newSender) external; + function startPrank(address sender) external; + function stopPrank() external; +} + +contract ExpectedCreator { + event Sender(address); + event AssertionFailed(string); + IHevm constant vm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + constructor(address s) public { + if (s != msg.sender) { + emit Sender(msg.sender); + emit AssertionFailed("fail on construct"); + } + } +} + +contract SenderVerifierChild { + event Sender(address); + event AssertionFailed(string); + IHevm constant vm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + function verifyMsgSender(address s) public { + if (s != msg.sender) { + emit Sender(msg.sender); + emit AssertionFailed("fail on inner call"); + } + } +} + +contract SenderVerifierParent { + event Sender(address); + event AssertionFailed(string); + IHevm constant vm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + SenderVerifierChild c; + constructor() public { + c = new SenderVerifierChild(); + } + + function verifyMsgSender(address s) public { + if (s != msg.sender) { + emit Sender(msg.sender); + emit AssertionFailed("fail on first call"); + } + c.verifyMsgSender(address(this)); + new ExpectedCreator(address(this)); + } +} + +contract TestPrank { + IHevm constant vm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + SenderVerifierParent p; + constructor() public { + p = new SenderVerifierParent(); + } + + function withPrank() public { + vm.prank(address(0x123)); + p.verifyMsgSender(address(0x123)); + p.verifyMsgSender(address(this)); + + vm.prank(address(0x123)); + new ExpectedCreator(address(0x123)); + new ExpectedCreator(address(this)); + } + + function withStartPrank() public { + vm.startPrank(address(0x123)); + p.verifyMsgSender(address(0x123)); + p.verifyMsgSender(address(0x123)); + new ExpectedCreator(address(0x123)); + new ExpectedCreator(address(0x123)); + } + + function withStartPrankStopPrank() public { + vm.startPrank(address(0x123)); + p.verifyMsgSender(address(0x123)); + p.verifyMsgSender(address(0x123)); + new ExpectedCreator(address(0x123)); + new ExpectedCreator(address(0x123)); + vm.stopPrank(); + p.verifyMsgSender(address(this)); + new ExpectedCreator(address(this)); + } + + function withNothing() public { + p.verifyMsgSender(address(this)); + new ExpectedCreator(address(this)); + } +} diff --git a/tests/solidity/cheat/prank.yaml b/tests/solidity/cheat/prank.yaml new file mode 100644 index 000000000..5311e3c89 --- /dev/null +++ b/tests/solidity/cheat/prank.yaml @@ -0,0 +1,3 @@ +testLimit: 1000 +seqLen: 10 +testMode: assertion \ No newline at end of file From 3aa07981a441e3d8c3616157499592615dcdd765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Thu, 5 Dec 2024 20:03:51 +0100 Subject: [PATCH 3/3] Build echidna with GHC 9.8 --- .github/container-linux-static/Dockerfile | 10 ++++---- .github/workflows/ci.yml | 12 ++++----- flake.lock | 6 ++--- flake.nix | 31 +++++++++++++---------- lib/Echidna/Campaign.hs | 2 +- lib/Echidna/Exec.hs | 2 +- src/test/Common.hs | 2 +- stack.yaml | 4 +-- 8 files changed, 36 insertions(+), 33 deletions(-) diff --git a/.github/container-linux-static/Dockerfile b/.github/container-linux-static/Dockerfile index 9ab126465..dfd7680f8 100644 --- a/.github/container-linux-static/Dockerfile +++ b/.github/container-linux-static/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.18.6 +FROM alpine:3.18.9 # Based on https://github.com/fpco/alpine-haskell-stack/blob/9.2.8v2/ghc-Dockerfile RUN apk upgrade --no-cache &&\ @@ -27,7 +27,7 @@ RUN apk upgrade --no-cache &&\ lld \ shadow # for stack --docker, provides groupadd -RUN curl -sSLo /usr/local/bin/stack https://github.com/commercialhaskell/stack/releases/download/v2.13.1/stack-2.13.1-linux-x86_64-bin && \ +RUN curl -sSLo /usr/local/bin/stack https://github.com/commercialhaskell/stack/releases/download/v3.1.1/stack-3.1.1-linux-x86_64-bin && \ chmod +x /usr/local/bin/stack # https://stackoverflow.com/a/41517423 @@ -36,12 +36,12 @@ RUN ln -s /usr/lib/libncurses.a /usr/lib/libtinfo.a COPY stack-config.yaml /root/.stack/config.yaml RUN cd /tmp && \ - curl -sSLo /tmp/ghc.tar.xz https://downloads.haskell.org/~ghc/9.6.5/ghc-9.6.5-x86_64-alpine3_12-linux.tar.xz && \ + curl -sSLo /tmp/ghc.tar.xz https://downloads.haskell.org/~ghc/9.8.2/ghc-9.8.2-x86_64-alpine3_12-linux.tar.xz && \ tar xf ghc.tar.xz && \ - cd ghc-9.6.5-x86_64-unknown-linux && \ + cd ghc-9.8.2-x86_64-unknown-linux && \ ./configure --prefix=/usr/local && \ make install && \ - rm -rf /tmp/ghc.tar.xz /tmp/ghc-9.6.5-x86_64-unknown-linux + rm -rf /tmp/ghc.tar.xz /tmp/ghc-9.8.2-x86_64-unknown-linux RUN apk upgrade --no-cache &&\ apk add --no-cache \ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 756de78e3..7b1107ddf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: include: - os: ubuntu-20.04 shell: bash - container: "{\"image\": \"elopeztob/alpine-haskell-stack-echidna:ghc-9.6.5\", \"options\": \"--user 1001\"}" + container: "{\"image\": \"elopeztob/alpine-haskell-stack-echidna:ghc-9.8.2\", \"options\": \"--user 1001\"}" - os: macos-13 # x86_64 macOS shell: bash - os: windows-latest @@ -64,7 +64,7 @@ jobs: id: stack if: matrix.container == '' with: - ghc-version: '9.6.5' + ghc-version: '9.8.2' enable-stack: true stack-version: 'latest' @@ -90,7 +90,7 @@ jobs: echo "- $HOME/.local/lib"; echo; echo "ghc-options:"; - echo ' "$locals": -Werror' + echo ' "$locals": -Werror -Wno-error=x-partial' "$REPLACE_LINKER_WIN" && echo ' "$everything": -pgml='$(cygpath -m "$GHC_MINGW_PATH/bin/clang.exe"); echo; "$SKIP_MSYS" && echo "skip-msys: true" || true @@ -156,12 +156,12 @@ jobs: - name: Build Dependencies run: | export PATH="$HASKELL_PATHS:$PATH" - stack build --ghc-options="-Werror" --only-dependencies + stack build --ghc-options="-Werror -Wno-error=x-partial" --only-dependencies - name: Build and install echidna run: | export PATH="$HASKELL_PATHS:$PATH" - stack install --ghc-options="-Werror" + stack install --ghc-options="-Werror -Wno-error=x-partial" - name: Amend and compress binaries (macOS) if: runner.os == 'macOS' @@ -185,7 +185,7 @@ jobs: if: runner.os != 'macOS' run: | export PATH="$HASKELL_PATHS:$PATH" - stack build --test --no-run-tests --ghc-options="-Werror" + stack build --test --no-run-tests --ghc-options="-Werror -Wno-error=x-partial" cp "$(find "$PWD" -name 'echidna-testsuite*' -type f)" . - name: Upload testsuite diff --git a/flake.lock b/flake.lock index a44f65c89..0f51596ee 100644 --- a/flake.lock +++ b/flake.lock @@ -70,11 +70,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1726436956, - "narHash": "sha256-a3rP7uafX/qBFX0y4CGS8vvTPvxsLl9eZQ85DkIn3DI=", + "lastModified": 1733229606, + "narHash": "sha256-FLYY5M0rpa5C2QAE3CKLYAM6TwbKicdRK6qNrSHlNrE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "039b72d0c738c934e2e36d7fc5520d1b425287a6", + "rev": "566e53c2ad750c84f6d31f9ccb9d00f823165550", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index e456114a0..8b3eca0b6 100644 --- a/flake.nix +++ b/flake.nix @@ -38,22 +38,25 @@ ncurses-static = pkgsStatic.ncurses.override { enableStatic = true; }; hsPkgs = ps : - ps.haskellPackages.override { + ps.haskell.packages.ghc98.override { overrides = hfinal: hprev: { - with-utf8 = - if (with ps.stdenv; hostPlatform.isDarwin && hostPlatform.isx86) - then ps.haskell.lib.compose.overrideCabal (_ : { extraLibraries = [ps.libiconv]; }) hprev.with-utf8 - else hprev.with-utf8; - # TODO: temporary fix for static build which is still on 9.4 - witch = ps.haskell.lib.doJailbreak hprev.witch; + with-utf8 = ps.haskell.lib.compose.overrideCabal (drv: { + version = "1.1.0.0"; + src = pkgs.fetchFromGitHub { + owner = "serokell"; + repo = "haskell-with-utf8"; + rev = "cf6e31475da3d9f54439650a70170819daa35f54"; + sha256 = "sha256-hxUiZbbcA6RvrVgGk4Vbt/rZT6wnBF3bfYbbQflzQ24="; + }; + }) hprev.with-utf8; }; }; hevm = pkgs: pkgs.lib.pipe ((hsPkgs pkgs).callCabal2nix "hevm" (pkgs.fetchFromGitHub { owner = "ethereum"; repo = "hevm"; - rev = "443c9040f6a8bde7d8b2cfafd99c0cb7dd7d049f"; - sha256 = "sha256-IC/q+2SJoyDansmbTHXfkKFfnhmMy97G13aTPNOkR30="; + rev = "53bccde13eeb6712eb9bc1d99b53529614a33690"; + sha256 = "sha256-sd7DEWz7hrs8AO+0juFz5S7Y5l/cWmQmullFeJH7FeE="; }) { secp256k1 = pkgs.secp256k1; }) ([ pkgs.haskell.lib.compose.dontCheck @@ -65,7 +68,7 @@ # FIXME: figure out solc situation, it conflicts with the one from # solc-select that is installed with slither, disable tests in the meantime haskell.lib.compose.dontCheck - (haskell.lib.compose.addTestToolDepends [ haskellPackages.hpack slither-analyzer solc ]) + (haskell.lib.compose.addTestToolDepends [ (hsPkgs pkgs).hpack slither-analyzer solc ]) (haskell.lib.compose.disableCabalFlag "static") ]); @@ -148,7 +151,7 @@ packages.echidna-redistributable = echidnaRedistributable; devShell = with pkgs; - haskellPackages.shellFor { + (hsPkgs pkgs).shellFor { packages = _: [ (echidna pkgs) ]; shellHook = '' hpack @@ -156,9 +159,9 @@ buildInputs = [ solc slither-analyzer - haskellPackages.hlint - haskellPackages.cabal-install - haskellPackages.haskell-language-server + (hsPkgs pkgs).hlint + (hsPkgs pkgs).cabal-install + (hsPkgs pkgs).haskell-language-server ]; withHoogle = true; }; diff --git a/lib/Echidna/Campaign.hs b/lib/Echidna/Campaign.hs index 2f3085884..589adab23 100644 --- a/lib/Echidna/Campaign.hs +++ b/lib/Echidna/Campaign.hs @@ -48,7 +48,7 @@ import Echidna.Types.Config import Echidna.Types.Signature (FunctionName) import Echidna.Types.Test import Echidna.Types.Test qualified as Test -import Echidna.Types.Tx (TxCall(..), Tx(..), call) +import Echidna.Types.Tx (TxCall(..), Tx(..)) import Echidna.Utility (getTimestamp) instance MonadThrow m => MonadThrow (RandT g m) where diff --git a/lib/Echidna/Exec.hs b/lib/Echidna/Exec.hs index 130e1b335..4cea32816 100644 --- a/lib/Echidna/Exec.hs +++ b/lib/Echidna/Exec.hs @@ -40,7 +40,7 @@ import Echidna.Types (ExecException(..), Gas, fromEVM, emptyAccount) import Echidna.Types.Config (Env(..), EConfig(..), UIConf(..), OperationMode(..), OutputFormat(Text)) import Echidna.Types.Coverage (CoverageInfo) import Echidna.Types.Solidity (SolConf(..)) -import Echidna.Types.Tx (TxCall(..), Tx, TxResult(..), call, dst, initialTimestamp, initialBlockNumber, getResult) +import Echidna.Types.Tx (TxCall(..), Tx(call, dst), TxResult(..), initialTimestamp, initialBlockNumber, getResult) import Echidna.Utility (getTimestamp, timePrefix) -- | Broad categories of execution failures: reversions, illegal operations, and ???. diff --git a/src/test/Common.hs b/src/test/Common.hs index 2eb400aaa..b67fb6f34 100644 --- a/src/test/Common.hs +++ b/src/test/Common.hs @@ -50,7 +50,7 @@ import Echidna.Types.Campaign import Echidna.Types.Signature (ContractName) import Echidna.Types.Solidity (SolConf(..)) import Echidna.Types.Test -import Echidna.Types.Tx (Tx(..), TxCall(..), call) +import Echidna.Types.Tx (Tx(..), TxCall(..)) import Echidna.Types.World (World(..)) import EVM.Solidity (Contracts(..), BuildOutput(..), SolcContract(..)) diff --git a/stack.yaml b/stack.yaml index 261e3d398..ec905e40d 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,11 +1,11 @@ -resolver: lts-22.34 +resolver: nightly-2024-10-21 packages: - '.' extra-deps: - git: https://github.com/ethereum/hevm.git - commit: 443c9040f6a8bde7d8b2cfafd99c0cb7dd7d049f + commit: 53bccde13eeb6712eb9bc1d99b53529614a33690 - smt2-parser-0.1.0.1@sha256:1e1a4565915ed851c13d1e6b8bb5185cf5d454da3b43170825d53e221f753d77,1421 - spawn-0.3@sha256:b91e01d8f2b076841410ae284b32046f91471943dc799c1af77d666c72101f02,1162