Upgrade `hevm` to upstream `main`

Commit f1f45d3c0d9767a38df04f398d1eab8b66dbe7fc

Includes fixes and new cheatcodes.
Emilio López 1 month ago
parent b54afcda5f
commit 298150fffc
  1. 4
      flake.nix
  2. 15
      lib/Echidna/Exec.hs
  3. 1
      lib/Echidna/SymExec.hs
  4. 4
      lib/Echidna/Test.hs
  5. 2
      lib/Echidna/Transaction.hs
  6. 4
      src/test/Tests/Cheat.hs
  7. 2
      stack.yaml
  8. 31
      tests/solidity/cheat/ffi2.sol
  9. 46
      tests/solidity/cheat/gas.sol

@ -52,8 +52,8 @@
hevm = pkgs: pkgs.lib.pipe ((hsPkgs pkgs).callCabal2nix "hevm" (pkgs.fetchFromGitHub { hevm = pkgs: pkgs.lib.pipe ((hsPkgs pkgs).callCabal2nix "hevm" (pkgs.fetchFromGitHub {
owner = "ethereum"; owner = "ethereum";
repo = "hevm"; repo = "hevm";
rev = "c779777d18c8ff60867f009d434b44ce08188e01"; rev = "f1f45d3c0d9767a38df04f398d1eab8b66dbe7fc";
sha256 = "sha256-JnJUZ9AxhxTP+TBMThksh0D4R6KFdzjgu1+fBeBERws="; sha256 = "sha256-3zEUwcZm4uZZLecvFTgVTV5CAm4qMfKPbLdwO88LnrY=";
}) { secp256k1 = pkgs.secp256k1; }) }) { secp256k1 = pkgs.secp256k1; })
([ ([
pkgs.haskell.lib.compose.dontCheck pkgs.haskell.lib.compose.dontCheck

@ -20,7 +20,8 @@ import Data.Maybe (fromMaybe, fromJust)
import Data.Text qualified as T import Data.Text qualified as T
import Data.Vector qualified as V import Data.Vector qualified as V
import Data.Vector.Unboxed.Mutable qualified as VMut 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 (bytecode, replaceCodeOfSelf, loadContract, exec1, vmOpIx, clearTStorages)
import EVM.ABI import EVM.ABI
@ -178,13 +179,21 @@ execTxWith executeTx tx = do
runFully -- resume execution runFully -- resume execution
-- Execute a FFI call -- Execute a FFI call
Just (PleaseDoFFI (cmd : args) continuation) -> do Just (PleaseDoFFI (cmd : args) envs continuation) -> do
(_, stdout, _) <- liftIO $ readProcessWithExitCode cmd args "" 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 $ let encodedResponse = encodeAbiValue $
AbiTuple (V.fromList [AbiBytesDynamic . hexText . T.pack $ stdout]) AbiTuple (V.fromList [AbiBytesDynamic . hexText . T.pack $ stdout])
fromEVM (continuation encodedResponse) fromEVM (continuation encodedResponse)
runFully 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 -- No queries to answer, the tx is fully executed and the result is final
_ -> pure vmResult _ -> pure vmResult

@ -136,6 +136,7 @@ vmMakeSymbolic vm
, forks = vm.forks , forks = vm.forks
, currentFork = vm.currentFork , currentFork = vm.currentFork
, labels = vm.labels , labels = vm.labels
, osEnv = vm.osEnv
} }
frameStateMakeSymbolic :: FrameState Concrete s -> FrameState Symbolic s frameStateMakeSymbolic :: FrameState Concrete s -> FrameState Symbolic s

@ -271,12 +271,12 @@ checkAssertionEvent = any (T.isPrefixOf "AssertionFailed(")
checkSelfDestructedTarget :: Addr -> DappInfo -> VM Concrete RealWorld -> TestValue checkSelfDestructedTarget :: Addr -> DappInfo -> VM Concrete RealWorld -> TestValue
checkSelfDestructedTarget addr _ vm = checkSelfDestructedTarget addr _ vm =
let selfdestructs' = vm.tx.substate.selfdestructs let selfdestructs' = vm.tx.subState.selfdestructs
in BoolValue $ LitAddr addr `notElem` selfdestructs' in BoolValue $ LitAddr addr `notElem` selfdestructs'
checkAnySelfDestructed :: DappInfo -> VM Concrete RealWorld -> TestValue checkAnySelfDestructed :: DappInfo -> VM Concrete RealWorld -> TestValue
checkAnySelfDestructed _ vm = checkAnySelfDestructed _ vm =
BoolValue $ null vm.tx.substate.selfdestructs BoolValue $ null vm.tx.subState.selfdestructs
checkPanicEvent :: T.Text -> Events -> Bool checkPanicEvent :: T.Text -> Events -> Bool
checkPanicEvent n = any (T.isPrefixOf ("Panic(" <> n <> ")")) checkPanicEvent n = any (T.isPrefixOf ("Panic(" <> n <> ")"))

@ -36,7 +36,7 @@ import Echidna.Types.World (World(..))
import Echidna.Types.Campaign import Echidna.Types.Campaign
hasSelfdestructed :: VM Concrete s -> Addr -> Bool 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 -- | If half a tuple is zero, make both halves zero. Useful for generating
-- delays, since block number only goes up with timestamp -- delays, since block number only goes up with timestamp

@ -11,4 +11,8 @@ cheatTests =
testGroup "Cheatcodes Tests" 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,6,0))) (Just "cheat/ffi.yaml") False FuzzWorker
[ ("echidna_ffi passed", solved "echidna_ffi") ] [ ("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") ]
] ]

@ -5,7 +5,7 @@ packages:
extra-deps: extra-deps:
- git: https://github.com/ethereum/hevm.git - git: https://github.com/ethereum/hevm.git
commit: c779777d18c8ff60867f009d434b44ce08188e01 commit: f1f45d3c0d9767a38df04f398d1eab8b66dbe7fc
- smt2-parser-0.1.0.1@sha256:1e1a4565915ed851c13d1e6b8bb5185cf5d454da3b43170825d53e221f753d77,1421 - smt2-parser-0.1.0.1@sha256:1e1a4565915ed851c13d1e6b8bb5185cf5d454da3b43170825d53e221f753d77,1421
- spawn-0.3@sha256:b91e01d8f2b076841410ae284b32046f91471943dc799c1af77d666c72101f02,1162 - spawn-0.3@sha256:b91e01d8f2b076841410ae284b32046f91471943dc799c1af77d666c72101f02,1162

@ -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");
}
}

@ -0,0 +1,46 @@
pragma experimental ABIEncoderV2;
interface Hevm {
function assume(bool) external;
}
contract C {
address public calledContract;
constructor() {
calledContract = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;
}
function foo() public returns (uint256) {
assembly {
mstore(0x80, 0x4c63e562)
mstore(0xa0, 1)
let addr := sload(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() {
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…
Cancel
Save