Skip to content

Commit

Permalink
Merge pull request #503 from ethereum/better-cheatcode-error-texts
Browse files Browse the repository at this point in the history
Better cheatcode error texts
  • Loading branch information
msooseth authored Oct 23, 2024
2 parents dc55c7b + 0f5821b commit fb7bcc6
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 54 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Skip over SMT generation issues due to e.g. CopySlice with symbolic arguments, and return
partial results instead of erroring out
- Fix interpreter's MCOPY handling so that it doesn't error out on symbolic arguments
- More desciptive errors in case of a cheatcode issue

## Fixed
- `concat` is a 2-ary, not an n-ary function in SMT2LIB, declare-const does not exist in QF_AUFBV, replacing
Expand Down
100 changes: 53 additions & 47 deletions src/EVM.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1695,10 +1695,8 @@ cheat gas (inOffset, inSize) (outOffset, outSize) xs = do
Nothing -> partial $ UnexpectedSymbolicArg vm.state.pc (getOpName vm.state) "symbolic cheatcode selector" (wrap [abi])
Just (unsafeInto -> abi') ->
case Map.lookup abi' cheatActions of
Nothing ->
vmError (BadCheatCode abi')
Just action ->
action input
Nothing -> vmError (BadCheatCode "cannot understand cheatcode, maybe cheatcode not supported?" abi')
Just action -> action input

type CheatAction t s = Expr Buf -> EVM t s ()

Expand All @@ -1709,17 +1707,19 @@ cheatActions = Map.fromList
vm <- get
if vm.config.allowFFI then
case decodeBuf [AbiArrayDynamicType AbiStringType] input of
CAbi [AbiArrayDynamic AbiStringType strsV] ->
let
cmd = fmap
(\case
(AbiString a) -> toString a
_ -> "")
(V.toList strsV)
cont bs = continueOnce $ do
frameReturnBuf bs
in query (PleaseDoFFI cmd vm.osEnv cont)
_ -> vmError (BadCheatCode sig)
CAbi valsArr -> case valsArr of
[AbiArrayDynamic AbiStringType strsV] ->
let
cmd = fmap
(\case
(AbiString a) -> toString a
_ -> "")
(V.toList strsV)
cont bs = continueOnce $ do
frameReturnBuf bs
in query (PleaseDoFFI cmd vm.osEnv cont)
_ -> vmError (BadCheatCode "ffi(string[]) decoding of string failed" sig)
_ -> vmError (BadCheatCode "ffi(string[]) parameter decoding failed" sig)
else
let msg = "ffi disabled: run again with --ffi if you want to allow tests to call external scripts"
in partial $ UnexpectedSymbolicArg vm.state.pc (getOpName vm.state) msg []
Expand All @@ -1729,7 +1729,7 @@ cheatActions = Map.fromList
[x] -> do
assign (#block % #timestamp) x
doStop
_ -> vmError (BadCheatCode sig)
_ -> vmError (BadCheatCode "warp(uint256) parameter decoding failed" sig)

, action "deal(address,uint256)" $
\sig input -> case decodeStaticArgs 0 2 input of
Expand All @@ -1738,30 +1738,30 @@ cheatActions = Map.fromList
fetchAccount usr $ \_ -> do
assign (#env % #contracts % ix usr % #balance) amt
doStop
_ -> vmError (BadCheatCode sig)
_ -> vmError (BadCheatCode "deal(address,uint256) parameter decoding failed" sig)

, action "assume(bool)" $
\sig input -> case decodeStaticArgs 0 1 input of
[c] -> do
modifying #constraints ((:) (PEq (Lit 1) c))
doStop
_ -> vmError (BadCheatCode sig)
_ -> vmError (BadCheatCode "assume(bool) parameter decoding failed." sig)

, action "roll(uint256)" $
\sig input -> case decodeStaticArgs 0 1 input of
[x] -> forceConcrete x "cannot roll to a symbolic block number" $ \block -> do
assign (#block % #number) block
doStop
_ -> vmError (BadCheatCode sig)
_ -> vmError (BadCheatCode "roll(uint256) parameter decoding failed" sig)

, action "store(address,bytes32,bytes32)" $
\sig input -> case decodeStaticArgs 0 3 input of
[a, slot, new] -> case wordToAddr a of
Just a'@(LitAddr _) -> fetchAccount a' $ \_ -> do
modifying (#env % #contracts % ix a' % #storage) (writeStorage slot new)
doStop
_ -> vmError (BadCheatCode sig)
_ -> vmError (BadCheatCode sig)
_ -> vmError (BadCheatCode "store(address,bytes32,bytes32) issue, address provided may not be an address?" sig)
_ -> vmError (BadCheatCode "store(address,bytes32,bytes32) parameter decoding failed" sig)

, action "load(address,bytes32)" $
\sig input -> case decodeStaticArgs 0 2 input of
Expand All @@ -1770,8 +1770,8 @@ cheatActions = Map.fromList
accessStorage a' slot $ \res -> do
let buf = writeWord (Lit 0) res (ConcreteBuf "")
frameReturnExpr buf -- TODO reivew
_ -> vmError (BadCheatCode sig)
_ -> vmError (BadCheatCode sig)
_ -> vmError (BadCheatCode "load(address,bytes32) issue, maybe the address provided is not correct?" sig)
_ -> vmError (BadCheatCode "load(address,bytes32) parameter decoding failed" sig)

, action "sign(uint256,bytes32)" $
\sig input -> case decodeStaticArgs 0 2 input of
Expand All @@ -1785,24 +1785,24 @@ cheatActions = Map.fromList
, AbiBytes 32 (word256Bytes s)
]
frameReturn result -- TODO ??
_ -> vmError (BadCheatCode sig)
_ -> vmError (BadCheatCode "sign(uint256,bytes32) parameter decoding failed" sig)

, action "addr(uint256)" $
\sig input -> case decodeStaticArgs 0 1 input of
[sk] -> forceConcrete sk "cannot derive address for a symbolic key" $ \sk' -> do
case EVM.Sign.deriveAddr $ into sk' of
Nothing -> vmError (BadCheatCode sig)
Nothing -> vmError (BadCheatCode "addr(uint256) could not derive address" sig)
Just address -> do frameReturnBuf $ word256Bytes (into address) -- TODO ??
_ -> vmError (BadCheatCode sig)
_ -> vmError (BadCheatCode "addr(uint256) parameter decoding failed" sig)

, action "prank(address)" $
\sig input -> case decodeStaticArgs 0 1 input of
[addr] -> case wordToAddr addr of
Just a -> do
assign (#config % #overrideCaller) (Just a)
doStop
Nothing -> vmError (BadCheatCode sig)
_ -> vmError (BadCheatCode sig)
Nothing -> vmError (BadCheatCode "prank(address), could not decode address provided" sig)
_ -> vmError (BadCheatCode "prank(address) parameter decoding failed" sig)

, action "startPrank(address)" $
\sig input -> case decodeStaticArgs 0 1 input of
Expand All @@ -1811,8 +1811,8 @@ cheatActions = Map.fromList
assign (#config % #overrideCaller) (Just a)
assign (#config % #resetCaller) False
doStop
Nothing -> vmError (BadCheatCode sig)
_ -> vmError (BadCheatCode sig)
Nothing -> vmError (BadCheatCode "startPrank(address), could not decode address provided" sig)
_ -> vmError (BadCheatCode "startPrank(address) parameter decoding failed" sig)

, action "stopPrank()" $
\_ _ -> do
Expand All @@ -1822,17 +1822,19 @@ cheatActions = Map.fromList

, action "createFork(string)" $
\sig input -> case decodeBuf [AbiStringType] input of
CAbi [AbiString bytes] -> do
forkId <- length <$> gets (.forks)
let urlOrAlias = Char8.unpack bytes
modify' $ \vm -> vm { forks = vm.forks Seq.|> ForkState vm.env vm.block vm.cache urlOrAlias }
frameReturn $ AbiUInt 256 (fromIntegral forkId)
_ -> vmError (BadCheatCode sig)
CAbi valsArr -> case valsArr of
[AbiString bytes] -> do
forkId <- length <$> gets (.forks)
let urlOrAlias = Char8.unpack bytes
modify' $ \vm -> vm { forks = vm.forks Seq.|> ForkState vm.env vm.block vm.cache urlOrAlias }
frameReturn $ AbiUInt 256 (fromIntegral forkId)
_ -> vmError (BadCheatCode "createFork(string) string provided may be incorrect?" sig)
_ -> vmError (BadCheatCode "createFork(string) parameter decoding failed" sig)

, action "selectFork(uint256)" $
\sig input -> case decodeStaticArgs 0 1 input of
[forkId] ->
forceConcrete forkId "forkId must be concrete" $ \(fromIntegral -> forkId') -> do
forceConcrete forkId "forkId of 'selectFork' must be concrete" $ \(fromIntegral -> forkId') -> do
saved <- Seq.lookup forkId' <$> gets (.forks)
case saved of
Just forkState -> do
Expand All @@ -1858,7 +1860,7 @@ cheatActions = Map.fromList
doStop
Nothing ->
vmError (NonexistentFork forkId')
_ -> vmError (BadCheatCode sig)
_ -> vmError (BadCheatCode "selectFork(uint256) parameter decoding failed" sig)

, action "activeFork()" $
\_ _ -> do
Expand All @@ -1867,18 +1869,22 @@ cheatActions = Map.fromList

, action "label(address,string)" $
\sig input -> case decodeBuf [AbiAddressType, AbiStringType] input of
CAbi [AbiAddress addr, AbiString label] -> do
#labels %= Map.insert addr (decodeUtf8 label)
doStop
_ -> vmError (BadCheatCode sig)
CAbi valsArr -> case valsArr of
[AbiAddress addr, AbiString label] -> do
#labels %= Map.insert addr (decodeUtf8 label)
doStop
_ -> vmError (BadCheatCode "label(address,string) address decoding failed" sig)
_ -> vmError (BadCheatCode "label(address,string) parameter decoding failed" sig)

, action "setEnv(string,string)" $
\sig input -> case decodeBuf [AbiStringType, AbiStringType] input of
CAbi [AbiString variable, AbiString value] -> do
let (varStr, varVal) = (toString variable, toString value)
#osEnv %= Map.insert varStr varVal
doStop
_ -> vmError (BadCheatCode sig)
CAbi valsArr -> case valsArr of
[AbiString variable, AbiString value] -> do
let (varStr, varVal) = (toString variable, toString value)
#osEnv %= Map.insert varStr varVal
doStop
_ -> vmError (BadCheatCode "setEnv(string,string) address decoding failed" sig)
_ -> vmError (BadCheatCode "setEnv(string,string) parameter decoding failed" sig)

-- Single-value environment read cheat actions
, $(envReadSingleCheat "envBool(string)") AbiBool stringToBool
Expand Down
8 changes: 4 additions & 4 deletions src/EVM/CheatsTH.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
module EVM.CheatsTH where

import EVM.ABI
import EVM.Types (internalError)

import Data.ByteString.Char8 (pack)
import Data.Map.Strict qualified as Map
Expand All @@ -11,7 +12,6 @@ import Data.Vector qualified as V
import Language.Haskell.TH
import Language.Haskell.TH.Syntax


liftByteString :: String -> Q Exp
liftByteString txt = AppE (VarE 'pack) <$> lift txt

Expand All @@ -23,7 +23,7 @@ liftAbiType AbiAddressType = [| AbiAddress |]
liftAbiType (AbiBytesType n) = [| AbiBytes $(lift n) |]
liftAbiType AbiStringType = [| AbiString |]
liftAbiType AbiBytesDynamicType = [| AbiBytesDynamic |]
liftAbiType _ = error "unimplemented"
liftAbiType _ = internalError "unimplemented"

envReadSingleCheat :: String -> Q Exp
envReadSingleCheat sigString = [|
Expand All @@ -40,7 +40,7 @@ envReadSingleCheat sigString = [|
case Map.lookup varStr vm.osEnv of
Just v -> cont v
Nothing -> query (PleaseReadEnv varStr cont)
_ -> vmError (BadCheatCode sig)
_ -> vmError (BadCheatCode (sigString <> " parameter decoding failed") sig)
|]
where
sigL = liftByteString sigString
Expand All @@ -64,7 +64,7 @@ envReadMultipleCheat sigString arrType = [|
case Map.lookup varStr vm.osEnv of
Just v -> cont v
Nothing -> query (PleaseReadEnv varStr cont)
_ -> vmError (BadCheatCode sig)
_ -> vmError (BadCheatCode (sigString <> " parameter decoding failed") sig)
|]
where
sigL = liftByteString sigString
Expand Down
2 changes: 1 addition & 1 deletion src/EVM/Format.hs
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ prettyError = \case
PrecompileFailure -> "Precompile failure"
ReturnDataOutOfBounds -> "Return data out of bounds"
NonceOverflow -> "Nonce overflow"
BadCheatCode a -> "Bad cheat code: sig: " <> show a
BadCheatCode reason a -> "Bad cheat code: " <> reason <> " sig: " <> show a
NonexistentFork a -> "Fork ID does not exist: " <> show a

prettyvmresult :: Expr End -> String
Expand Down
2 changes: 1 addition & 1 deletion src/EVM/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ data EvmError
| PrecompileFailure
| ReturnDataOutOfBounds
| NonceOverflow
| BadCheatCode FunctionSelector
| BadCheatCode String FunctionSelector
| NonexistentFork Int
deriving (Show, Eq, Ord)

Expand Down
2 changes: 1 addition & 1 deletion test/test.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4501,7 +4501,7 @@ checkBadCheatCode sig _ = \case
where
findBadCheatCode :: Trace -> Maybe FunctionSelector
findBadCheatCode Trace { tracedata = td } = case td of
ErrorTrace (BadCheatCode s) -> Just s
ErrorTrace (BadCheatCode _ s) -> Just s
_ -> Nothing

allBranchesFail :: App m => ByteString -> Maybe Sig -> m (Either [SMTCex] (Expr End))
Expand Down

0 comments on commit fb7bcc6

Please sign in to comment.