Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade hevm #1317

Merged
merged 2 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
# TODO: maybe only override it for echidna-redistributable?
pkgsStatic = if pkgs.stdenv.hostPlatform.isLinux then pkgs.pkgsStatic else pkgs;
# this is not perfect for development as it hardcodes solc to 0.5.7, test suite runs fine though
solc = solc-pkgs.mkDefault pkgs pkgs.solc_0_5_7;
# 0.5.7 is not available on aarch64 darwin so alternatively pick 0.8.5
solc = solc-pkgs.mkDefault pkgs (pkgs.solc_0_5_7 or pkgs.solc_0_8_5);

secp256k1-static = pkgsStatic.secp256k1.overrideAttrs (attrs: {
configureFlags = attrs.configureFlags ++ [ "--enable-static" ];
Expand All @@ -51,8 +52,8 @@
hevm = pkgs: pkgs.lib.pipe ((hsPkgs pkgs).callCabal2nix "hevm" (pkgs.fetchFromGitHub {
owner = "ethereum";
repo = "hevm";
rev = "c779777d18c8ff60867f009d434b44ce08188e01";
sha256 = "sha256-JnJUZ9AxhxTP+TBMThksh0D4R6KFdzjgu1+fBeBERws=";
rev = "f1f45d3c0d9767a38df04f398d1eab8b66dbe7fc";
sha256 = "sha256-3zEUwcZm4uZZLecvFTgVTV5CAm4qMfKPbLdwO88LnrY=";
}) { secp256k1 = pkgs.secp256k1; })
([
pkgs.haskell.lib.compose.dontCheck
Expand Down
15 changes: 12 additions & 3 deletions lib/Echidna/Exec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import Data.Maybe (fromMaybe, fromJust)
import Data.Text qualified as T
import Data.Vector qualified as V
import Data.Vector.Unboxed.Mutable qualified as VMut
import System.Process (readProcessWithExitCode)
import System.Environment (lookupEnv, getEnvironment)
import System.Process qualified as P

import EVM (bytecode, replaceCodeOfSelf, loadContract, exec1, vmOpIx, clearTStorages)
import EVM.ABI
Expand Down Expand Up @@ -178,13 +179,21 @@ execTxWith executeTx tx = do
runFully -- resume execution

-- Execute a FFI call
Just (PleaseDoFFI (cmd : args) continuation) -> do
(_, stdout, _) <- liftIO $ readProcessWithExitCode cmd args ""
Just (PleaseDoFFI (cmd : args) envs continuation) -> do
existingEnv <- liftIO getEnvironment
let mergedEnv = Map.toList $ Map.union envs $ Map.fromList existingEnv
let process = (P.proc cmd args) { P.env = Just mergedEnv }
(_, stdout, _) <- liftIO $ P.readCreateProcessWithExitCode process ""
let encodedResponse = encodeAbiValue $
AbiTuple (V.fromList [AbiBytesDynamic . hexText . T.pack $ stdout])
fromEVM (continuation encodedResponse)
runFully

Just (PleaseReadEnv var continuation) -> do
value <- liftIO $ lookupEnv var
fromEVM (continuation $ fromMaybe "" value)
runFully -- resume execution

-- No queries to answer, the tx is fully executed and the result is final
_ -> pure vmResult

Expand Down
1 change: 1 addition & 0 deletions lib/Echidna/SymExec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ vmMakeSymbolic vm
, forks = vm.forks
, currentFork = vm.currentFork
, labels = vm.labels
, osEnv = vm.osEnv
}

frameStateMakeSymbolic :: FrameState Concrete s -> FrameState Symbolic s
Expand Down
4 changes: 2 additions & 2 deletions lib/Echidna/Test.hs
Original file line number Diff line number Diff line change
Expand Up @@ -271,12 +271,12 @@ checkAssertionEvent = any (T.isPrefixOf "AssertionFailed(")

checkSelfDestructedTarget :: Addr -> DappInfo -> VM Concrete RealWorld -> TestValue
checkSelfDestructedTarget addr _ vm =
let selfdestructs' = vm.tx.substate.selfdestructs
let selfdestructs' = vm.tx.subState.selfdestructs
in BoolValue $ LitAddr addr `notElem` selfdestructs'

checkAnySelfDestructed :: DappInfo -> VM Concrete RealWorld -> TestValue
checkAnySelfDestructed _ vm =
BoolValue $ null vm.tx.substate.selfdestructs
BoolValue $ null vm.tx.subState.selfdestructs

checkPanicEvent :: T.Text -> Events -> Bool
checkPanicEvent n = any (T.isPrefixOf ("Panic(" <> n <> ")"))
Expand Down
2 changes: 1 addition & 1 deletion lib/Echidna/Transaction.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import Echidna.Types.World (World(..))
import Echidna.Types.Campaign

hasSelfdestructed :: VM Concrete s -> Addr -> Bool
hasSelfdestructed vm addr = LitAddr addr `elem` vm.tx.substate.selfdestructs
hasSelfdestructed vm addr = LitAddr addr `elem` vm.tx.subState.selfdestructs

-- | If half a tuple is zero, make both halves zero. Useful for generating
-- delays, since block number only goes up with timestamp
Expand Down
6 changes: 5 additions & 1 deletion src/test/Tests/Cheat.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import Echidna.Types.Campaign (WorkerType(..))
cheatTests :: TestTree
cheatTests =
testGroup "Cheatcodes Tests"
[ testContract' "cheat/ffi.sol" (Just "TestFFI") (Just (> solcV (0,6,0))) (Just "cheat/ffi.yaml") False FuzzWorker
[ testContract' "cheat/ffi.sol" (Just "TestFFI") (Just (> solcV (0,5,0))) (Just "cheat/ffi.yaml") False FuzzWorker
[ ("echidna_ffi passed", solved "echidna_ffi") ]
, testContract' "cheat/ffi2.sol" (Just "TestFFI") (Just (> solcV (0,5,0))) (Just "cheat/ffi.yaml") False FuzzWorker
[ ("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") ]
]
2 changes: 1 addition & 1 deletion stack.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ packages:

extra-deps:
- git: https://github.com/ethereum/hevm.git
commit: c779777d18c8ff60867f009d434b44ce08188e01
commit: f1f45d3c0d9767a38df04f398d1eab8b66dbe7fc

- smt2-parser-0.1.0.1@sha256:1e1a4565915ed851c13d1e6b8bb5185cf5d454da3b43170825d53e221f753d77,1421
- spawn-0.3@sha256:b91e01d8f2b076841410ae284b32046f91471943dc799c1af77d666c72101f02,1162
Expand Down
31 changes: 31 additions & 0 deletions tests/solidity/cheat/ffi2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
pragma experimental ABIEncoderV2;

interface Hevm {
function setEnv(string calldata, string calldata) external;
function ffi(string[] calldata) external returns (bytes memory);
}

contract TestFFI {
address constant HEVM_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;

bytes32 hehe;

function foo(int x) external {
// ABI encoded "gm", as a string
Hevm(HEVM_ADDRESS).setEnv("ECHIDNA_FOO_BAR", "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002676d000000000000000000000000000000000000000000000000000000000000");

string[] memory inputs = new string[](3);
inputs[0] = "sh";
inputs[1] = "-c";
inputs[2] = "printf '%s' \"$ECHIDNA_FOO_BAR\"";

bytes memory res = Hevm(HEVM_ADDRESS).ffi(inputs);

(string memory output) = abi.decode(res, (string));
hehe = keccak256(bytes(output));
}

function echidna_ffi() public returns (bool){
return hehe != keccak256("gm");
}
}
46 changes: 46 additions & 0 deletions tests/solidity/cheat/gas.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
pragma experimental ABIEncoderV2;

interface Hevm {
function assume(bool) external;
}

contract C {
address public calledContract;

constructor() public {
calledContract = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;
}

function foo() public returns (uint256) {
assembly {
mstore(0x80, 0x4c63e562)
mstore(0xa0, 1)

let addr := sload(0 /* calledContract.slot */)
let beforegas := gas()
let success := callcode(gas(), addr, 0, 0x9c, 0x24, 0, 0)
let aftergas := gas()
mstore(0x80, sub(beforegas, aftergas))
return(0x80, 0x20)
}
}
}

contract TestCheatGas {
uint256 spent = 123456;
C foo;

constructor() public {
foo = new C();
}

function bar() public {
spent = foo.foo();
}

function echidna_gas_zero() public returns (bool){
// 0x14 as measured from opcodes, but let's leave some leeway in case solc changes
// GAS PUSH1 0x0 DUP1 PUSH1 0x24 PUSH1 0x9C PUSH1 0x0 DUP7 GAS CALLCODE GAS
return spent > 0x20;
}
}
Loading