Skip to content

Commit 57d8196

Browse files
authored
Merge pull request #988 from argotorg/ethereum-tests
Move from `ethereum/tests` to `ethereum/execution-spec-tests`
2 parents d606ef0 + 5757300 commit 57d8196

File tree

7 files changed

+195
-117
lines changed

7 files changed

+195
-117
lines changed

.github/workflows/build.yml

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -224,12 +224,9 @@ jobs:
224224
path: ethereum-solidity
225225
persist-credentials: false
226226

227-
- uses: actions/checkout@v4
228-
with:
229-
repository: ethereum/tests
230-
ref: v13
231-
path: ethereum-tests
232-
persist-credentials: false
227+
- name: Download execution-spec-tests fixtures
228+
run: |
229+
curl --retry 5 -fsSL https://github.com/ethereum/execution-spec-tests/releases/download/v5.4.0/fixtures_stable.tar.gz | tar xz
233230
234231
- uses: actions/checkout@v4
235232
with:
@@ -257,7 +254,7 @@ jobs:
257254
echo DAPP_SOLC_PATH="$PWD/solc/" >> "$GITHUB_ENV"
258255
# repos
259256
echo HEVM_SOLIDITY_REPO="$PWD/ethereum-solidity" >> "$GITHUB_ENV"
260-
echo HEVM_ETHEREUM_TESTS_REPO="$PWD/ethereum-tests" >> "$GITHUB_ENV"
257+
echo HEVM_ETHEREUM_TESTS_REPO="$PWD/fixtures/blockchain_tests" >> "$GITHUB_ENV"
261258
echo HEVM_FORGE_STD_REPO="$PWD/forge-std" >> "$GITHUB_ENV"
262259
263260
- name: run tests

bench/bench.hs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,8 @@ blockchainTests ts = bench "blockchain-tests" $ nfIO $ do
4343
tests <- ts
4444
putStrLn " executing blockchain tests"
4545
let cases = concat . Map.elems . (fmap Map.toList) $ tests
46-
ignored = Map.keys BCTests.problematicTests
4746
foldM (\acc (n, c) ->
48-
if n `elem` ignored
47+
if isJust (BCTests.findIgnoreReason n)
4948
then pure True
5049
else do
5150
res <- runApp $ runBCTest c

flake.lock

Lines changed: 0 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@
99
url = "github:argotorg/solidity/8a97fa7a1db1ec509221ead6fea6802c684ee887";
1010
flake = false;
1111
};
12-
ethereum-tests = {
13-
url = "github:ethereum/tests/v13";
14-
flake = false;
15-
};
1612
forge-std = {
1713
url = "github:foundry-rs/forge-std";
1814
flake = false;
@@ -27,9 +23,13 @@
2723
};
2824
};
2925

30-
outputs = { nixpkgs, flake-utils, solidity, empty-smt-solver, forge-std, ethereum-tests, foundry, solc-pkgs, ... }:
26+
outputs = { nixpkgs, flake-utils, solidity, empty-smt-solver, forge-std, foundry, solc-pkgs, ... }:
3127
flake-utils.lib.eachDefaultSystem (system:
3228
let
29+
execution-spec-tests-fixtures = builtins.fetchTarball {
30+
url = "https://github.com/ethereum/execution-spec-tests/releases/download/v5.4.0/fixtures_stable.tar.gz";
31+
sha256 = "0a0i20f611aghvh1vghvzcxilbh80bd4174n7ih7iarn5x71aq1p";
32+
};
3333
pkgs = (import nixpkgs {
3434
inherit system;
3535
overlays = [solc-pkgs.overlay];
@@ -71,7 +71,7 @@
7171
secp256k1 = ps.secp256k1;
7272
}).overrideAttrs(final: prev: {
7373
HEVM_SOLIDITY_REPO = solidity;
74-
HEVM_ETHEREUM_TESTS_REPO = ethereum-tests;
74+
HEVM_ETHEREUM_TESTS_REPO = "${execution-spec-tests-fixtures}/blockchain_tests";
7575
HEVM_FORGE_STD_REPO = forge-std;
7676
DAPP_SOLC = "${solc}/bin/solc";
7777
}))
@@ -207,7 +207,7 @@
207207
# hevm tests expect these to be set
208208
HEVM_SOLIDITY_REPO = solidity;
209209
DAPP_SOLC = "${solc}/bin/solc";
210-
HEVM_ETHEREUM_TESTS_REPO = ethereum-tests;
210+
HEVM_ETHEREUM_TESTS_REPO = "${execution-spec-tests-fixtures}/blockchain_tests";
211211
HEVM_FORGE_STD_REPO = forge-std;
212212

213213
# point cabal repl to system deps

src/EVM.hs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1525,8 +1525,20 @@ finalize = do
15251525
-- deposit the code from a creation tx
15261526
creation <- use (#tx % #isCreate)
15271527
createe <- use (#state % #contract)
1528-
createeExists <- (Map.member createe) <$> use (#env % #contracts)
1529-
when (creation && createeExists) $
1528+
createeContract <- preuse (#env % #contracts % ix createe)
1529+
-- Check if this is a collision case (target has RuntimeCode instead of InitCode)
1530+
let isCollision = creation && case createeContract of
1531+
Just c -> case c.code of
1532+
InitCode _ _ -> False
1533+
RuntimeCode _ -> True -- collision: existing contract has RuntimeCode
1534+
UnknownCode _ -> internalError "cannot determine collision with unknown code"
1535+
Nothing -> internalError "create transaction but no code found"
1536+
-- For collision: burn all gas (failed CREATE consumes all gas)
1537+
when isCollision $ assign (#state % #gas) initialGas
1538+
-- Only replace code if this is a creation tx, the contract exists,
1539+
-- and it has InitCode (not RuntimeCode from a collision)
1540+
let shouldReplaceCode = creation && not isCollision
1541+
when shouldReplaceCode $
15301542
case output of
15311543
ConcreteBuf bs -> replaceCode createe (RuntimeCode (ConcreteRuntimeCode bs))
15321544
_ ->
@@ -2207,14 +2219,21 @@ delegateCall this gasGiven xTo xContext xValue xInOffset xInSize xOutOffset xOut
22072219

22082220
-- -- * Contract creation
22092221

2210-
-- EIP 684
2222+
-- EIP-684 and EIP-7610: collision if nonce != 0, code is non-empty, or storage is non-empty
22112223
collision :: Maybe Contract -> Bool
22122224
collision c' = case c' of
2213-
Just c -> c.nonce /= Just 0 || case c.code of
2225+
Just c -> c.nonce /= Just 0 || not (isStorageEmpty c.storage) || case c.code of
22142226
RuntimeCode (ConcreteRuntimeCode "") -> False
22152227
RuntimeCode (SymbolicRuntimeCode b) -> not $ null b
22162228
_ -> True
22172229
Nothing -> False
2230+
where
2231+
isStorageEmpty :: Expr Storage -> Bool
2232+
isStorageEmpty = \case
2233+
ConcreteStore m -> Map.null m
2234+
AbstractStore _ _ -> True -- empty symbolic store
2235+
SStore _ _ _ -> False -- has writes, so non-empty
2236+
GVar _ -> internalError "unexpected global variable"
22182237

22192238
create :: forall t. (?op :: Word8, ?conf::Config, VMOps t)
22202239
=> Expr EAddr -> Contract

src/EVM/Transaction.hs

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module EVM.Transaction where
22

3-
import EVM (initialContract, ceilDiv)
3+
import EVM (initialContract, ceilDiv, collision)
44
import EVM.Expr qualified as Expr
55
import EVM.FeeSchedule
66
import EVM.Format (hexText)
@@ -12,6 +12,7 @@ import Optics.Core hiding (cons)
1212

1313
import Data.Aeson (FromJSON (..))
1414
import Data.Aeson qualified as JSON
15+
import Data.Function (applyWhen)
1516
import Data.Aeson.Types qualified as JSON
1617
import Data.ByteString (ByteString, cons)
1718
import Data.ByteString qualified as BS
@@ -34,10 +35,14 @@ data TxType
3435
= LegacyTransaction
3536
| AccessListTransaction
3637
| EIP1559Transaction
38+
| EIP4844Transaction
39+
| EIP7702Transaction
3740
deriving (Show, Eq, Generic)
3841

3942
instance JSON.ToJSON TxType where
4043
toJSON t = case t of
44+
EIP7702Transaction -> "0x4" -- permanently sets the code for an EOA
45+
EIP4844Transaction -> "0x3" -- Proto-Danksharding
4146
EIP1559Transaction -> "0x2" -- EIP1559
4247
LegacyTransaction -> "0x1" -- EIP2718
4348
AccessListTransaction -> "0x1" -- EIP2930
@@ -124,6 +129,8 @@ signingData tx =
124129
else normalData
125130
AccessListTransaction -> eip2930Data
126131
EIP1559Transaction -> eip1559Data
132+
EIP4844Transaction -> eip4844Data
133+
EIP7702Transaction -> eip7702Data
127134
where v = tx.v
128135
to' = case tx.toAddr of
129136
Just a -> BS $ word160Bytes a
@@ -166,7 +173,6 @@ signingData tx =
166173
, BS tx.txdata
167174
, rlpAccessList
168175
]
169-
170176
eip2930Data = cons 0x01 $ rlpList
171177
[ rlpWord256 tx.chainId
172178
, rlpWord256 tx.nonce
@@ -177,6 +183,31 @@ signingData tx =
177183
, BS tx.txdata
178184
, rlpAccessList
179185
]
186+
eip4844Data = cons 0x03 $ rlpList
187+
[ rlpWord256 tx.chainId
188+
, rlpWord256 tx.nonce
189+
, rlpWord256 maxPrio
190+
, rlpWord256 maxFee
191+
, rlpWord256 (into tx.gasLimit)
192+
, to'
193+
, rlpWord256 tx.value
194+
, BS tx.txdata
195+
, rlpAccessList
196+
, rlpWord256 $ undefined -- TODO EIP4844 max_fee_per_blob_gas
197+
, undefined -- TODO EIP4844 blob_versioned_hashes
198+
]
199+
eip7702Data = cons 0x04 $ rlpList
200+
[ rlpWord256 tx.chainId
201+
, rlpWord256 tx.nonce
202+
, rlpWord256 maxPrio
203+
, rlpWord256 maxFee
204+
, rlpWord256 (into tx.gasLimit)
205+
, to'
206+
, rlpWord256 tx.value
207+
, BS tx.txdata
208+
, rlpAccessList
209+
, undefined -- TODO EIP4844 authorization_list
210+
]
180211

181212
accessListPrice :: FeeSchedule Word64 -> [AccessListEntry] -> Word64
182213
accessListPrice fs al =
@@ -220,7 +251,7 @@ instance FromJSON Transaction where
220251
toAddr <- addrFieldMaybe val "to"
221252
v <- wordField val "v"
222253
value <- wordField val "value"
223-
txType <- fmap (read :: String -> Int) <$> (val JSON..:? "type")
254+
txType <- fmap (read :: String -> Int) <$> val JSON..:? "type"
224255
case txType of
225256
Just 0x00 -> pure $ Transaction tdata gasLimit gasPrice nonce r s toAddr v value LegacyTransaction [] Nothing Nothing 1
226257
Just 0x01 -> do
@@ -229,6 +260,14 @@ instance FromJSON Transaction where
229260
Just 0x02 -> do
230261
accessListEntries <- (val JSON..: "accessList") >>= parseJSONList
231262
pure $ Transaction tdata gasLimit gasPrice nonce r s toAddr v value EIP1559Transaction accessListEntries maxPrio maxFee 1
263+
Just 0x03 -> do
264+
accessListEntries <- (val JSON..: "accessList") >>= parseJSONList
265+
-- TODO: capture max_fee_per_blob_gas and blob_versioned_hashes EIP4844
266+
pure $ Transaction tdata gasLimit gasPrice nonce r s toAddr v value EIP4844Transaction accessListEntries maxPrio maxFee 1
267+
Just 0x04 -> do
268+
accessListEntries <- (val JSON..: "accessList") >>= parseJSONList
269+
-- TODO: capture authorization_list EIP7702
270+
pure $ Transaction tdata gasLimit gasPrice nonce r s toAddr v value EIP7702Transaction accessListEntries maxPrio maxFee 1
232271
Just _ -> fail "unrecognized custom transaction type"
233272
Nothing -> pure $ Transaction tdata gasLimit gasPrice nonce r s toAddr v value LegacyTransaction [] Nothing Nothing 1
234273
parseJSON invalid =
@@ -268,13 +307,20 @@ initTx vm =
268307
preState = setupTx origin coinbase gasPrice gasLimit vm.env.contracts
269308
oldBalance = view (accountAt toAddr % #balance) preState
270309
creation = vm.tx.isCreate
271-
initState =
272-
((Map.adjust (over #balance (`Expr.sub` value))) origin)
273-
. (Map.adjust (over #balance (Expr.add value))) toAddr
274-
. (if creation
275-
then Map.insert toAddr (toContract & (set #balance oldBalance))
276-
else touchAccount toAddr)
277-
$ preState
310+
-- Check for collision at target address for CREATE transactions
311+
hasCollision = creation && collision (Map.lookup toAddr preState)
312+
-- For collision: don't transfer value, don't create contract
313+
initState = if hasCollision
314+
then touchAccount toAddr preState
315+
else ((Map.adjust (over #balance (`Expr.sub` value))) origin)
316+
. (Map.adjust (over #balance (Expr.add value))) toAddr
317+
. (if creation
318+
then Map.insert toAddr (toContract & (set #balance oldBalance))
319+
else touchAccount toAddr)
320+
$ preState
278321
in
279322
vm & #env % #contracts .~ initState
280323
& #tx % #txReversion .~ preState
324+
-- For collision: set code to empty so exec1 immediately stops and calls finalize
325+
-- (don't set #result directly, as that bypasses finalize which handles gas payment)
326+
& applyWhen hasCollision (#state % #code .~ RuntimeCode (ConcreteRuntimeCode ""))

0 commit comments

Comments
 (0)