packages feed

hevm 0.51.0 → 0.51.1

raw patch · 13 files changed

+103/−66 lines, 13 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

+ EVM.ABI: instance Data.Data.Data EVM.ABI.AbiType
+ EVM.FeeSchedule: [$sel:g_initcodeword:FeeSchedule] :: FeeSchedule n -> n
+ EVM.Op: OpPush0 :: GenericOp a
+ EVM.Types: MaxInitCodeSizeExceeded :: W256 -> W256 -> EvmError
+ EVM.Types: OpPush0 :: GenericOp a
- EVM: costOfCreate :: FeeSchedule Word64 -> Word64 -> W256 -> (Word64, Word64)
+ EVM: costOfCreate :: FeeSchedule Word64 -> Word64 -> W256 -> Bool -> (Word64, Word64)
- EVM: create :: (?op :: Word8) => Addr -> Contract -> Word64 -> W256 -> [Expr EWord] -> Addr -> Expr Buf -> EVM ()
+ EVM: create :: (?op :: Word8) => Addr -> Contract -> W256 -> Word64 -> W256 -> [Expr EWord] -> Addr -> Expr Buf -> EVM ()
- EVM.FeeSchedule: FeeSchedule :: n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> FeeSchedule n
+ EVM.FeeSchedule: FeeSchedule :: n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> n -> FeeSchedule n

Files

CHANGELOG.md view
@@ -5,6 +5,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.51.1] - 2023-06-02++## Fixed++- hevm now gracefully handles missing `out` directories+- Constraints are correctly propogated to the final output expression during symbolic execution++## Changed++- HEVM is now fully compliant with the Shanghai hard fork+ ## [0.51.0] - 2023-04-27  ## Added
hevm-cli/hevm-cli.hs view
@@ -43,7 +43,8 @@ import Data.Version               (showVersion) import Data.DoubleWord            (Word256) import System.IO                  (stderr)-import System.Directory           (withCurrentDirectory, getCurrentDirectory)+import System.Directory           (withCurrentDirectory, getCurrentDirectory, doesDirectoryExist)+import System.FilePath            ((</>)) import System.Exit                (exitFailure, exitWith, ExitCode(..))  import qualified Data.ByteString        as ByteString@@ -337,10 +338,14 @@ getSrcInfo cmd = do   root <- getRoot cmd   withCurrentDirectory root $ do-    buildOutput <- readBuildOutput root (getProjectType cmd)-    case buildOutput of-      Left _ -> pure emptyDapp-      Right o -> pure $ dappInfo root o+    outExists <- doesDirectoryExist (root </> "out")+    if outExists+    then do+      buildOutput <- readBuildOutput root (getProjectType cmd)+      case buildOutput of+        Left _ -> pure emptyDapp+        Right o -> pure $ dappInfo root o+    else pure emptyDapp  getProjectType :: Command Options.Unwrapped -> ProjectType getProjectType cmd = fromMaybe Foundry cmd.projectType
hevm.cabal view
@@ -2,7 +2,7 @@ name:   hevm version:-  0.51.0+  0.51.1 synopsis:   Ethereum virtual machine evaluator description:
src/EVM.hs view
@@ -98,7 +98,7 @@   let txaccessList = o.txAccessList       txorigin = o.origin       txtoAddr = o.address-      initialAccessedAddrs = fromList $ [txorigin, txtoAddr] ++ [1..9] ++ (Map.keys txaccessList)+      initialAccessedAddrs = fromList $ [txorigin, txtoAddr, o.coinbase] ++ [1..9] ++ (Map.keys txaccessList)       initialAccessedStorageKeys = fromList $ foldMap (uncurry (map . (,))) (Map.toList txaccessList)       touched = if o.create then [txorigin] else [txorigin, txtoAddr]   in@@ -238,6 +238,12 @@        case getOp(?op) of +        OpPush0 -> do+          limitStack 1 $+            burn g_base $ do+              next+              pushSym (Lit 0)+         OpPush n' -> do           let n = fromIntegral n'               !xs = case vm.state.code of@@ -621,9 +627,9 @@                                 refund (g_sreset + g_access_list_storage_key)                             else do                               when (original /= 0) $-                                if new' == 0-                                then refund (g_sreset + g_access_list_storage_key)-                                else unRefund (g_sreset + g_access_list_storage_key)+                                if current' == 0+                                then unRefund (g_sreset + g_access_list_storage_key)+                                else when (new' == 0) $ refund (g_sreset + g_access_list_storage_key)                               when (original == new') $                                 if original == 0                                 then refund (g_sset - g_sload)@@ -695,14 +701,11 @@                   availableGas <- use (#state % #gas)                   let                     newAddr = createAddress self this.nonce-                    (cost, gas') = costOfCreate fees availableGas 0+                    (cost, gas') = costOfCreate fees availableGas xSize False                   _ <- accessAccountForGas newAddr-                  burn (cost - gas') $ do-                    -- unfortunately we have to apply some (pretty hacky)-                    -- heuristics here to parse the unstructured buffer read-                    -- from memory into a code and data section+                  burn cost $ do                     let initCode = readMemory xOffset' xSize' vm-                    create self this (num gas') xValue xs newAddr initCode+                    create self this xSize (num gas') xValue xs newAddr initCode             _ -> underrun          OpCall ->@@ -794,9 +797,10 @@                     \initCode -> do                       let                         newAddr  = create2Address self xSalt initCode-                        (cost, gas') = costOfCreate fees availableGas xSize+                        (cost, gas') = costOfCreate fees availableGas xSize True                       _ <- accessAccountForGas newAddr-                      burn (cost - gas') $ create self this gas' xValue xs newAddr (ConcreteBuf initCode)+                      burn cost $+                        create self this xSize gas' xValue xs newAddr (ConcreteBuf initCode)             _ -> underrun          OpStaticcall ->@@ -1254,7 +1258,6 @@    let     sumRefunds   = sum (snd <$> tx.substate.refunds)-    blockReward  = num (block.schedule.r_block)     gasUsed      = tx.gaslimit - gasRemaining     cappedRefund = min (quot gasUsed 5) (num sumRefunds)     originPay    = (num $ gasRemaining + cappedRefund) * tx.gasprice@@ -1266,14 +1269,6 @@      (Map.adjust (over #balance (+ minerPay)) block.coinbase)   touchAccount block.coinbase -  -- pay out the block reward, recreating the miner if necessary-  preuse (#env % #contracts % ix block.coinbase) >>= \case-    Nothing -> modifying (#env % #contracts)-      (Map.insert block.coinbase (initialContract (RuntimeCode (ConcreteRuntimeCode ""))))-    Just _  -> noop-  modifying (#env % #contracts)-    (Map.adjust (over #balance (+ blockReward)) block.coinbase)-   -- perform state trie clearing (EIP 161), of selfdestructs   -- and touched accounts. addresses are cleared if they have   --    a) selfdestructed, or@@ -1627,8 +1622,8 @@  create :: (?op :: Word8)   => Addr -> Contract-  -> Word64 -> W256 -> [Expr EWord] -> Addr -> Expr Buf -> EVM ()-create self this xGas' xValue xs newAddr initCode = do+  -> W256 -> Word64 -> W256 -> [Expr EWord] -> Addr -> Expr Buf -> EVM ()+create self this xSize xGas' xValue xs newAddr initCode = do   vm0 <- get   let xGas = num xGas'   if this.nonce == num (maxBound :: Word64)@@ -1643,6 +1638,11 @@     assign (#state % #returndata) mempty     pushTrace $ ErrorTrace $ BalanceTooLow xValue this.balance     next+  else if xSize > vm0.block.maxCodeSize * 2+  then do+    assign (#state % #stack) (Lit 0 : xs)+    assign (#state % #returndata) mempty+    vmError $ MaxInitCodeSizeExceeded (vm0.block.maxCodeSize * 2) xSize   else if length vm0.frames >= 1024   then do     assign (#state % #stack) (Lit 0 : xs)@@ -1662,7 +1662,6 @@     -- unfortunately we have to apply some (pretty hacky)     -- heuristics here to parse the unstructured buffer read     -- from memory into a code and data section-    -- TODO: comment explaining whats going on here     let contract' = do           prefixLen <- Expr.concPrefix initCode           prefix <- Expr.toList $ Expr.take (num prefixLen) initCode@@ -2207,12 +2206,12 @@ -- Gas cost of create, including hash cost if needed costOfCreate   :: FeeSchedule Word64-  -> Word64 -> W256 -> (Word64, Word64)-costOfCreate (FeeSchedule {..}) availableGas hashSize =-  (createCost + initGas, initGas)+  -> Word64 -> W256 -> Bool -> (Word64, Word64)+costOfCreate (FeeSchedule {..}) availableGas size hashNeeded = (createCost, initGas)   where-    createCost = g_create + hashCost-    hashCost   = g_sha3word * ceilDiv (num hashSize) 32+    byteCost   = if hashNeeded then g_sha3word + g_initcodeword else g_initcodeword+    createCost = g_create + codeCost+    codeCost   = byteCost * (ceilDiv (num size) 32)     initGas    = allButOne64th (availableGas - createCost)  concreteModexpGasFee :: ByteString -> Word64
src/EVM/ABI.hs view
@@ -66,6 +66,7 @@ import Data.Bits          (shiftL, shiftR, (.&.)) import Data.ByteString    (ByteString) import Data.Char          (isHexDigit)+import Data.Data          (Data) import Data.DoubleWord    (Word256, Int256, signedWord) import Data.Functor       (($>)) import Data.Text          (Text)@@ -134,7 +135,7 @@   | AbiArrayType        Int AbiType   | AbiTupleType        (Vector AbiType)   | AbiFunctionType-  deriving (Read, Eq, Ord, Generic)+  deriving (Read, Eq, Ord, Generic, Data)  instance Show AbiType where   show = Text.unpack . abiTypeSolidity
src/EVM/Assembler.hs view
@@ -106,4 +106,5 @@         else error $ "Internal Error: invalid argument to OpLog: " <> show n       -- we just always assemble OpPush into PUSH32       OpPush wrd -> (LitByte 0x7f) : [Expr.indexWord (Lit i) wrd | i <- [0..31]]+      OpPush0 -> [LitByte 0x5f]       OpUnknown o -> [LitByte o]
src/EVM/FeeSchedule.hs view
@@ -35,6 +35,7 @@   , g_logtopic :: n   , g_sha3 :: n   , g_sha3word :: n+  , g_initcodeword :: n   , g_copy :: n   , g_blockhash :: n   , g_extcodehash :: n@@ -110,6 +111,7 @@   , g_logtopic = 375   , g_sha3 = 30   , g_sha3word = 6+  , g_initcodeword = 2   , g_copy = 3   , g_blockhash = 20   , g_extcodehash = 400@@ -173,8 +175,8 @@   -- <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2929.md> eip2929 :: EIP n eip2929 fees = fees-  { g_sload = 100 -  , g_sreset = 5000 - 2100 +  { g_sload = 100+  , g_sreset = 5000 - 2100   , g_call = 2600   , g_balance = 2600   , g_extcode = 2600
src/EVM/Format.hs view
@@ -385,6 +385,7 @@   StateChangeWhileStatic -> "State change while static"   CallDepthLimitReached -> "Call depth limit reached"   MaxCodeSizeExceeded a b -> "Max code size exceeded: max: " <> show a <> " actual: " <> show b+  MaxInitCodeSizeExceeded a b -> "Max init code size exceeded: max: " <> show a <> " actual: " <> show b   InvalidFormat -> "Invalid Format"   PrecompileFailure -> "Precompile failure"   ReturnDataOutOfBounds -> "Return data out of bounds"
src/EVM/Op.hs view
@@ -86,6 +86,7 @@    0x5a -> "GAS"    0x5b -> "JUMPDEST"    --+   0x5f -> "PUSH0"    0x60 -> "PUSH1"    0x61 -> "PUSH2"    0x62 -> "PUSH3"@@ -250,6 +251,7 @@   OpDup x -> "DUP" ++ show x   OpSwap x -> "SWAP" ++ show x   OpLog x -> "LOG" ++ show x+  OpPush0  -> "PUSH0"   OpPush x -> case x of     Lit x' -> "PUSH 0x" ++ (showHex x' "")     _ -> "PUSH " ++ show x@@ -332,6 +334,7 @@   0x59 -> OpMsize   0x5a -> OpGas   0x5b -> OpJumpdest+  0x5f -> OpPush0   0xf0 -> OpCreate   0xf1 -> OpCall   0xf2 -> OpCallcode
src/EVM/SymExec.hs view
@@ -442,7 +442,7 @@ runExpr :: Stepper.Stepper (Expr End) runExpr = do   vm <- Stepper.runFully-  let asserts = vm.keccakEqs+  let asserts = vm.keccakEqs <> vm.constraints   pure $ case vm.result of     Just (VMSuccess buf) -> Success asserts buf vm.env.storage     Just (VMFailure e) -> Failure asserts e
src/EVM/Transaction.hs view
@@ -2,7 +2,7 @@  import Prelude hiding (Word) -import EVM (initialContract)+import EVM (initialContract, ceilDiv) import EVM.FeeSchedule import EVM.RLP import EVM.Types@@ -185,10 +185,11 @@       zeroBytes    = BS.count 0 calldata       nonZeroBytes = BS.length calldata - zeroBytes       baseCost     = fs.g_transaction-        + (if isNothing tx.toAddr then fs.g_txcreate else 0)+        + (if isNothing tx.toAddr then fs.g_txcreate + initcodeCost else 0)         + (accessListPrice fs tx.accessList )       zeroCost     = fs.g_txdatazero       nonZeroCost  = fs.g_txdatanonzero+      initcodeCost = fs.g_initcodeword * num (ceilDiv (BS.length calldata) 32)   in baseCost + zeroCost * (fromIntegral zeroBytes) + nonZeroCost * (fromIntegral nonZeroBytes)  instance FromJSON AccessListEntry where
src/EVM/Types.hs view
@@ -502,6 +502,7 @@   | InvalidMemoryAccess   | CallDepthLimitReached   | MaxCodeSizeExceeded W256 W256+  | MaxInitCodeSizeExceeded W256 W256   | InvalidFormat   | PrecompileFailure   | ReturnDataOutOfBounds@@ -931,6 +932,7 @@   | OpDup !Word8   | OpSwap !Word8   | OpLog !Word8+  | OpPush0   | OpPush a   | OpUnknown Word8   deriving (Show, Eq, Functor)
test/EVM/Test/BlockchainTests.hs view
@@ -45,6 +45,7 @@ data Block = Block   { coinbase    :: Addr   , difficulty  :: W256+  , mixHash     :: W256   , gasLimit    :: Word64   , baseFee     :: W256   , number      :: W256@@ -91,7 +92,7 @@    Left "No cases to check." -> pure [] -- error "no-cases ok"    Left _err -> pure [] -- error err    Right allTests -> pure $-     (\(name, x) -> testCase' name $ runVMTest False (name, x)) <$> Map.toList allTests+     (\(name, x) -> testCase' name $ runVMTest True (name, x)) <$> Map.toList allTests   where   testCase' name assertion =     case Map.lookup name problematicTests of@@ -104,29 +105,29 @@  commonProblematicTests :: Map String (TestTree -> TestTree) commonProblematicTests = Map.fromList-  [ ("loopMul_d0g0v0_London", ignoreTestBecause "hevm is too slow")-  , ("loopMul_d1g0v0_London", ignoreTestBecause "hevm is too slow")-  , ("loopMul_d2g0v0_London", ignoreTestBecause "hevm is too slow")-  , ("CALLBlake2f_MaxRounds_d0g0v0_London", ignoreTestBecause "very slow, bypasses timeout due time spent in FFI")+  [ ("loopMul_d0g0v0_Shanghai", ignoreTestBecause "hevm is too slow")+  , ("loopMul_d1g0v0_Shanghai", ignoreTestBecause "hevm is too slow")+  , ("loopMul_d2g0v0_Shanghai", ignoreTestBecause "hevm is too slow")+  , ("CALLBlake2f_MaxRounds_d0g0v0_Shanghai", ignoreTestBecause "very slow, bypasses timeout due time spent in FFI")   ]  ciProblematicTests :: Map String (TestTree -> TestTree) ciProblematicTests = Map.fromList-  [ ("Return50000_d0g1v0_London", ignoreTest)-  , ("Return50000_2_d0g1v0_London", ignoreTest)-  , ("randomStatetest177_d0g0v0_London", ignoreTest)-  , ("static_Call50000_d0g0v0_London", ignoreTest)-  , ("static_Call50000_d1g0v0_London", ignoreTest)-  , ("static_Call50000bytesContract50_1_d1g0v0_London", ignoreTest)-  , ("static_Call50000bytesContract50_2_d1g0v0_London", ignoreTest)-  , ("static_Return50000_2_d0g0v0_London", ignoreTest)-  , ("loopExp_d10g0v0_London", ignoreTest)-  , ("loopExp_d11g0v0_London", ignoreTest)-  , ("loopExp_d12g0v0_London", ignoreTest)-  , ("loopExp_d13g0v0_London", ignoreTest)-  , ("loopExp_d14g0v0_London", ignoreTest)-  , ("loopExp_d8g0v0_London", ignoreTest)-  , ("loopExp_d9g0v0_London", ignoreTest)+  [ ("Return50000_d0g1v0_Shanghai", ignoreTest)+  , ("Return50000_2_d0g1v0_Shanghai", ignoreTest)+  , ("randomStatetest177_d0g0v0_Shanghai", ignoreTest)+  , ("static_Call50000_d0g0v0_Shanghai", ignoreTest)+  , ("static_Call50000_d1g0v0_Shanghai", ignoreTest)+  , ("static_Call50000bytesContract50_1_d1g0v0_Shanghai", ignoreTest)+  , ("static_Call50000bytesContract50_2_d1g0v0_Shanghai", ignoreTest)+  , ("static_Return50000_2_d0g0v0_Shanghai", ignoreTest)+  , ("loopExp_d10g0v0_Shanghai", ignoreTest)+  , ("loopExp_d11g0v0_Shanghai", ignoreTest)+  , ("loopExp_d12g0v0_Shanghai", ignoreTest)+  , ("loopExp_d13g0v0_Shanghai", ignoreTest)+  , ("loopExp_d14g0v0_Shanghai", ignoreTest)+  , ("loopExp_d8g0v0_Shanghai", ignoreTest)+  , ("loopExp_d9g0v0_Shanghai", ignoreTest)   ]  runVMTest :: Bool -> (String, Case) -> IO ()@@ -284,7 +285,8 @@     number     <- wordField v' "number"     baseFee    <- fmap read <$> v' .:? "baseFeePerGas"     timestamp  <- wordField v' "timestamp"-    return $ Block coinbase difficulty gasLimit (fromMaybe 0 baseFee) number timestamp txs+    mixHash    <- wordField v' "mixHash"+    return $ Block coinbase difficulty mixHash gasLimit (fromMaybe 0 baseFee) number timestamp txs   parseJSON invalid =     JSON.typeMismatch "Block" invalid @@ -331,13 +333,16 @@ fromBlockchainCase :: BlockchainCase -> Either BlockchainError Case fromBlockchainCase (BlockchainCase blocks preState postState network) =   case (blocks, network) of-    ([block], "London") -> case block.txs of+    ([block], "Shanghai") -> case block.txs of       [tx] -> fromBlockchainCase' block tx preState postState       []        -> Left NoTxs       _         -> Left TooManyTxs     ([_], _) -> Left OldNetwork     (_, _)   -> Left TooManyBlocks +maxCodeSize :: W256+maxCodeSize = 24576+ fromBlockchainCase' :: Block -> Transaction                        -> Map Addr (Contract, Storage) -> Map Addr (Contract, Storage)                        -> Either BlockchainError Case@@ -362,8 +367,8 @@          , number        = block.number          , timestamp     = Lit block.timestamp          , coinbase      = block.coinbase-         , prevRandao    = block.difficulty-         , maxCodeSize   = 24576+         , prevRandao    = block.mixHash+         , maxCodeSize   = maxCodeSize          , blockGaslimit = block.gasLimit          , gasprice      = effectiveGasPrice          , schedule      = feeSchedule@@ -429,7 +434,13 @@       toAddr      = fromMaybe (EVM.createAddress origin senderNonce) tx.toAddr       prevCode    = view (accountAt toAddr % #contractcode) (Map.map fst prestate)       prevNonce   = view (accountAt toAddr % #nonce) (Map.map fst prestate)-  if isCreate && ((case prevCode of {RuntimeCode (ConcreteRuntimeCode b) -> not (BS.null b); _ -> True}) || (prevNonce /= 0))++      nonEmptyAccount = case prevCode of+                        RuntimeCode (ConcreteRuntimeCode b) -> not (BS.null b)+                        _ -> True+      badNonce = prevNonce /= 0+      initCodeSizeExceeded = BS.length tx.txdata > (num maxCodeSize * 2)+  if isCreate && (badNonce || nonEmptyAccount || initCodeSizeExceeded)   then mzero   else     return prestate