Skip to content

Commit

Permalink
Upgrade hevm to upstream main
Browse files Browse the repository at this point in the history
Commit f1f45d3c0d9767a38df04f398d1eab8b66dbe7fc

Includes fixes and new cheatcodes.
  • Loading branch information
elopez committed Oct 7, 2024
1 parent b54afcd commit 3279e05
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 9 deletions.
4 changes: 2 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -52,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
4 changes: 4 additions & 0 deletions src/test/Tests/Cheat.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@ cheatTests =
testGroup "Cheatcodes Tests"
[ testContract' "cheat/ffi.sol" (Just "TestFFI") (Just (> solcV (0,6,0))) (Just "cheat/ffi.yaml") False FuzzWorker
[ ("echidna_ffi passed", solved "echidna_ffi") ]
, testContract' "cheat/ffi2.sol" (Just "TestFFI") (Just (> solcV (0,6,0))) (Just "cheat/ffi.yaml") False FuzzWorker
[ ("echidna_ffi passed", solved "echidna_ffi") ]
, testContract' "cheat/gas.sol" (Just "TestCheatGas") (Just (> solcV (0,6,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;
}
}

0 comments on commit 3279e05

Please sign in to comment.