tests: add back devnet spec test runner

This commit is contained in:
Jared Wasinger 2026-04-25 09:03:01 +02:00 committed by MariusVanDerWijden
parent a0f2390745
commit 6f8fc82d38
3 changed files with 178 additions and 49 deletions

View file

@ -24,6 +24,65 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
) )
func TestBlockchainBAL(t *testing.T) {
bt := new(testMatcher)
// We are running most of GeneralStatetests to tests witness support, even
// though they are ran as state tests too. Still, the performance tests are
// less about state andmore about EVM number crunching, so skip those.
bt.skipLoad(`^GeneralStateTests/VMTests/vmPerformance`)
// Skip random failures due to selfish mining test
bt.skipLoad(`.*bcForgedTest/bcForkUncle\.json`)
// Slow tests
bt.slow(`.*bcExploitTest/DelegateCallSpam.json`)
bt.slow(`.*bcExploitTest/ShanghaiLove.json`)
bt.slow(`.*bcExploitTest/SuicideIssue.json`)
bt.slow(`.*/bcForkStressTest/`)
bt.slow(`.*/bcGasPricerTest/RPC_API_Test.json`)
bt.slow(`.*/bcWalletTest/`)
// Very slow test
bt.skipLoad(`.*/stTimeConsuming/.*`)
// test takes a lot for time and goes easily OOM because of sha3 calculation on a huge range,
// using 4.6 TGas
bt.skipLoad(`.*randomStatetest94.json.*`)
// After the merge we would accept side chains as canonical even if they have lower td
bt.skipLoad(`.*bcMultiChainTest/ChainAtoChainB_difficultyB.json`)
bt.skipLoad(`.*bcMultiChainTest/CallContractFromNotBestBlock.json`)
bt.skipLoad(`.*bcTotalDifficultyTest/uncleBlockAtBlock3afterBlock4.json`)
bt.skipLoad(`.*bcTotalDifficultyTest/lotsOfBranchesOverrideAtTheMiddle.json`)
bt.skipLoad(`.*bcTotalDifficultyTest/sideChainWithMoreTransactions.json`)
bt.skipLoad(`.*bcForkStressTest/ForkStressTest.json`)
bt.skipLoad(`.*bcMultiChainTest/lotsOfLeafs.json`)
bt.skipLoad(`.*bcFrontierToHomestead/blockChainFrontierWithLargerTDvsHomesteadBlockchain.json`)
bt.skipLoad(`.*bcFrontierToHomestead/blockChainFrontierWithLargerTDvsHomesteadBlockchain2.json`)
// With chain history removal, TDs become unavailable, this transition tests based on TTD are unrunnable
bt.skipLoad(`.*bcArrowGlacierToParis/powToPosBlockRejection.json`)
// This directory contains no test.
bt.skipLoad(`.*\.meta/.*`)
bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) {
config, ok := Forks[test.json.Network]
if !ok {
t.Fatalf("unsupported fork: %s\n", test.json.Network)
}
gspec := test.genesis(config)
// skip any tests which are not past the cancun fork (selfdestruct removal)
if gspec.Config.CancunTime == nil || *gspec.Config.CancunTime != 0 {
return
}
execBlockTest(t, bt, test, true)
})
// There is also a LegacyTests folder, containing blockchain tests generated
// prior to Istanbul. However, they are all derived from GeneralStateTests,
// which run natively, so there's no reason to run them here.
}
func TestBlockchain(t *testing.T) { func TestBlockchain(t *testing.T) {
bt := new(testMatcher) bt := new(testMatcher)
@ -66,24 +125,17 @@ func TestBlockchain(t *testing.T) {
// This directory contains no test. // This directory contains no test.
bt.skipLoad(`.*\.meta/.*`) bt.skipLoad(`.*\.meta/.*`)
// Broken tests
bt.skipLoad(`RevertInCreateInInit`)
bt.skipLoad(`InitCollisionParis`)
bt.skipLoad(`dynamicAccountOverwriteEmpty_Paris`)
bt.skipLoad(`create2collisionStorageParis`)
bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) { bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) {
execBlockTest(t, bt, test) execBlockTest(t, bt, test, false)
}) })
// There is also a LegacyTests folder, containing blockchain tests generated // There is also a LegacyTests folder, containing blockchain tests generated
// prior to Istanbul. However, they are all derived from GeneralStateTests, // prior to Istanbul. However, they are all derived from GeneralStateTests,
// which run natively, so there's no reason to run them here. // which run natively, so there's no reason to run them here.
} }
// TestExecutionSpecBlocktests runs the test fixtures from execution-spec-tests. func testExecutionSpecBlocktests(t *testing.T, testDir string) {
func TestExecutionSpecBlocktests(t *testing.T) { if !common.FileExist(testDir) {
if !common.FileExist(executionSpecBlockchainTestDir) { t.Skipf("directory %s does not exist", testDir)
t.Skipf("directory %s does not exist", executionSpecBlockchainTestDir)
} }
bt := new(testMatcher) bt := new(testMatcher)
@ -91,18 +143,24 @@ func TestExecutionSpecBlocktests(t *testing.T) {
bt.skipLoad(".*prague/eip7251_consolidations/test_system_contract_deployment.json") bt.skipLoad(".*prague/eip7251_consolidations/test_system_contract_deployment.json")
bt.skipLoad(".*prague/eip7002_el_triggerable_withdrawals/test_system_contract_deployment.json") bt.skipLoad(".*prague/eip7002_el_triggerable_withdrawals/test_system_contract_deployment.json")
// Broken tests bt.walk(t, testDir, func(t *testing.T, name string, test *BlockTest) {
bt.skipLoad(`RevertInCreateInInit`) execBlockTest(t, bt, test, true)
bt.skipLoad(`InitCollisionParis`)
bt.skipLoad(`dynamicAccountOverwriteEmpty_Paris`)
bt.skipLoad(`create2collisionStorageParis`)
bt.walk(t, executionSpecBlockchainTestDir, func(t *testing.T, name string, test *BlockTest) {
execBlockTest(t, bt, test)
}) })
} }
func execBlockTest(t *testing.T, bt *testMatcher, test *BlockTest) { // TestExecutionSpecBlocktests runs the test fixtures from execution-spec-tests.
func TestExecutionSpecBlocktests(t *testing.T) {
testExecutionSpecBlocktests(t, executionSpecBlockchainTestDir)
}
// TestExecutionSpecBlocktestsBAL runs the BAL release test fixtures from execution-spec-tests.
func TestExecutionSpecBlocktestsBAL(t *testing.T) {
testExecutionSpecBlocktests(t, executionSpecBALBlockchainTestDir)
}
var failures = 0
func execBlockTest(t *testing.T, bt *testMatcher, test *BlockTest, buildAndVerifyBAL bool) {
// Define all the different flag combinations we should run the tests with, // Define all the different flag combinations we should run the tests with,
// picking only one for short tests. // picking only one for short tests.
// //
@ -118,7 +176,13 @@ func execBlockTest(t *testing.T, bt *testMatcher, test *BlockTest) {
} }
for _, snapshot := range snapshotConf { for _, snapshot := range snapshotConf {
for _, dbscheme := range dbschemeConf { for _, dbscheme := range dbschemeConf {
if err := bt.checkFailure(t, test.Run(snapshot, dbscheme, true, nil, nil)); err != nil { if err := bt.checkFailure(t, test.Run(snapshot, dbscheme, true, buildAndVerifyBAL, nil, nil)); err != nil {
failures++
/*
if failures > 10 {
panic("adsf")
}
*/
t.Errorf("test with config {snapshotter:%v, scheme:%v} failed: %v", snapshot, dbscheme, err) t.Errorf("test with config {snapshotter:%v, scheme:%v} failed: %v", snapshot, dbscheme, err)
return return
} }

View file

@ -26,6 +26,7 @@ import (
"math/big" "math/big"
"os" "os"
"reflect" "reflect"
"strings"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
@ -37,6 +38,7 @@ import (
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types/bal"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -71,6 +73,7 @@ type btBlock struct {
ExpectException string ExpectException string
Rlp string Rlp string
UncleHeaders []*btHeader UncleHeaders []*btHeader
AccessList *bal.BlockAccessList `json:"blockAccessList,omitempty"`
} }
//go:generate go run github.com/fjl/gencodec -type btHeader -field-override btHeaderMarshaling -out gen_btheader.go //go:generate go run github.com/fjl/gencodec -type btHeader -field-override btHeaderMarshaling -out gen_btheader.go
@ -97,8 +100,8 @@ type btHeader struct {
BlobGasUsed *uint64 BlobGasUsed *uint64
ExcessBlobGas *uint64 ExcessBlobGas *uint64
ParentBeaconBlockRoot *common.Hash ParentBeaconBlockRoot *common.Hash
SlotNumber *uint64
BlockAccessListHash *common.Hash BlockAccessListHash *common.Hash
SlotNumber *uint64
} }
type btHeaderMarshaling struct { type btHeaderMarshaling struct {
@ -114,27 +117,20 @@ type btHeaderMarshaling struct {
SlotNumber *math.HexOrDecimal64 SlotNumber *math.HexOrDecimal64
} }
func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *tracing.Hooks, postCheck func(error, *core.BlockChain)) (result error) { func (t *BlockTest) createTestBlockChain(config *params.ChainConfig, snapshotter bool, scheme string, witness, createAndVerifyBAL bool, tracer *tracing.Hooks) (*core.BlockChain, error) {
config, ok := Forks[t.json.Network]
if !ok {
return UnsupportedForkError{t.json.Network}
}
// import pre accounts & construct test genesis block & state root // import pre accounts & construct test genesis block & state root
// Commit genesis state
var ( var (
gspec = t.genesis(config)
db = rawdb.NewMemoryDatabase() db = rawdb.NewMemoryDatabase()
tconf = &triedb.Config{ tconf = &triedb.Config{
Preimages: true, Preimages: true,
IsUBT: gspec.Config.UBTTime != nil && *gspec.Config.UBTTime <= gspec.Timestamp,
} }
) )
if scheme == rawdb.PathScheme || tconf.IsUBT { if scheme == rawdb.PathScheme {
tconf.PathDB = pathdb.Defaults tconf.PathDB = pathdb.Defaults
} else { } else {
tconf.HashDB = hashdb.Defaults tconf.HashDB = hashdb.Defaults
} }
gspec := t.genesis(config)
// if ttd is not specified, set an arbitrary huge value // if ttd is not specified, set an arbitrary huge value
if gspec.Config.TerminalTotalDifficulty == nil { if gspec.Config.TerminalTotalDifficulty == nil {
@ -143,15 +139,15 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t
triedb := triedb.NewDatabase(db, tconf) triedb := triedb.NewDatabase(db, tconf)
gblock, err := gspec.Commit(db, triedb, nil) gblock, err := gspec.Commit(db, triedb, nil)
if err != nil { if err != nil {
return err return nil, err
} }
triedb.Close() // close the db to prevent memory leak triedb.Close() // close the db to prevent memory leak
if gblock.Hash() != t.json.Genesis.Hash { if gblock.Hash() != t.json.Genesis.Hash {
return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x", gblock.Hash().Bytes()[:6], t.json.Genesis.Hash[:6]) return nil, fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x", gblock.Hash().Bytes()[:6], t.json.Genesis.Hash[:6])
} }
if gblock.Root() != t.json.Genesis.StateRoot { if gblock.Root() != t.json.Genesis.StateRoot {
return fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.json.Genesis.StateRoot[:6]) return nil, fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.json.Genesis.StateRoot[:6])
} }
// Wrap the original engine within the beacon-engine // Wrap the original engine within the beacon-engine
engine := beacon.New(ethash.NewFaker()) engine := beacon.New(ethash.NewFaker())
@ -165,12 +161,27 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t
Tracer: tracer, Tracer: tracer,
}, },
StatelessSelfValidation: witness, StatelessSelfValidation: witness,
NoPrefetch: true,
} }
if snapshotter { if snapshotter {
options.SnapshotLimit = 1 options.SnapshotLimit = 1
options.SnapshotWait = true options.SnapshotWait = true
} }
chain, err := core.NewBlockChain(db, gspec, engine, options) chain, err := core.NewBlockChain(db, gspec, engine, options)
if err != nil {
return nil, err
}
return chain, nil
}
func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, createAndVerifyBAL bool, tracer *tracing.Hooks, postCheck func(error, *core.BlockChain)) (result error) {
config, ok := Forks[t.json.Network]
if !ok {
return UnsupportedForkError{t.json.Network}
}
// import pre accounts & construct test genesis block & state root
chain, err := t.createTestBlockChain(config, snapshotter, scheme, witness, createAndVerifyBAL, tracer)
if err != nil { if err != nil {
return err return err
} }
@ -204,7 +215,50 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t
} }
} }
} }
return t.validateImportedHeaders(chain, validBlocks) err = t.validateImportedHeaders(chain, validBlocks)
if err != nil {
return err
}
if createAndVerifyBAL {
newChain, _ := t.createTestBlockChain(config, snapshotter, scheme, witness, createAndVerifyBAL, tracer)
defer newChain.Stop()
var blocksWithBAL types.Blocks
for i := uint64(1); i <= chain.CurrentBlock().Number.Uint64(); i++ {
block := chain.GetBlockByNumber(i)
if chain.Config().IsAmsterdam(block.Number(), block.Time()) && block.AccessList() == nil {
return fmt.Errorf("block %d missing BAL", block.NumberU64())
}
blocksWithBAL = append(blocksWithBAL, block)
}
amt, err := newChain.InsertChain(blocksWithBAL)
if err != nil {
return err
}
_ = amt
newDB, err := newChain.State()
if err != nil {
return err
}
if err = t.validatePostState(newDB); err != nil {
return fmt.Errorf("post state validation failed: %v", err)
}
// Cross-check the snapshot-to-hash against the trie hash
if snapshotter {
if newChain.Snapshots() != nil {
if err := chain.Snapshots().Verify(chain.CurrentBlock().Root); err != nil {
return err
}
}
}
err = t.validateImportedHeaders(newChain, validBlocks)
if err != nil {
return err
}
}
return nil
} }
// Network returns the network/fork name for this test. // Network returns the network/fork name for this test.
@ -228,8 +282,8 @@ func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis {
BaseFee: t.json.Genesis.BaseFeePerGas, BaseFee: t.json.Genesis.BaseFeePerGas,
BlobGasUsed: t.json.Genesis.BlobGasUsed, BlobGasUsed: t.json.Genesis.BlobGasUsed,
ExcessBlobGas: t.json.Genesis.ExcessBlobGas, ExcessBlobGas: t.json.Genesis.ExcessBlobGas,
SlotNumber: t.json.Genesis.SlotNumber,
BlockAccessListHash: t.json.Genesis.BlockAccessListHash, BlockAccessListHash: t.json.Genesis.BlockAccessListHash,
SlotNumber: t.json.Genesis.SlotNumber,
} }
} }
@ -259,6 +313,16 @@ func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error)
return nil, fmt.Errorf("block RLP decoding failed when expected to succeed: %v", err) return nil, fmt.Errorf("block RLP decoding failed when expected to succeed: %v", err)
} }
} }
// check that if we encode the same block, it will result in the same RLP
var enc bytes.Buffer
if err := rlp.Encode(&enc, cb); err != nil {
return nil, err
}
expected := common.Hex2Bytes(strings.TrimLeft(b.Rlp, "0x"))
if !bytes.Equal(enc.Bytes(), expected) {
return nil, fmt.Errorf("mismatch. expected\n%s\ngot\n%x\n", expected, enc.Bytes())
}
// RLP decoding worked, try to insert into chain: // RLP decoding worked, try to insert into chain:
blocks := types.Blocks{cb} blocks := types.Blocks{cb}
i, err := blockchain.InsertChain(blocks) i, err := blockchain.InsertChain(blocks)
@ -271,7 +335,7 @@ func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error)
} }
if b.BlockHeader == nil { if b.BlockHeader == nil {
if data, err := json.MarshalIndent(cb.Header(), "", " "); err == nil { if data, err := json.MarshalIndent(cb.Header(), "", " "); err == nil {
fmt.Fprintf(os.Stdout, "block (index %d) insertion should have failed due to: %v:\n%v\n", fmt.Fprintf(os.Stderr, "block (index %d) insertion should have failed due to: %v:\n%v\n",
bi, b.ExpectException, string(data)) bi, b.ExpectException, string(data))
} }
return nil, fmt.Errorf("block (index %d) insertion should have failed due to: %v", return nil, fmt.Errorf("block (index %d) insertion should have failed due to: %v",

View file

@ -34,17 +34,18 @@ import (
) )
var ( var (
baseDir = filepath.Join(".", "testdata") baseDir = filepath.Join(".", "testdata")
blockTestDir = filepath.Join(baseDir, "BlockchainTests") blockTestDir = filepath.Join(baseDir, "BlockchainTests")
stateTestDir = filepath.Join(baseDir, "GeneralStateTests") stateTestDir = filepath.Join(baseDir, "GeneralStateTests")
legacyStateTestDir = filepath.Join(baseDir, "LegacyTests", "Constantinople", "GeneralStateTests") legacyStateTestDir = filepath.Join(baseDir, "LegacyTests", "Constantinople", "GeneralStateTests")
transactionTestDir = filepath.Join(baseDir, "TransactionTests") transactionTestDir = filepath.Join(baseDir, "TransactionTests")
rlpTestDir = filepath.Join(baseDir, "RLPTests") rlpTestDir = filepath.Join(baseDir, "RLPTests")
difficultyTestDir = filepath.Join(baseDir, "BasicTests") difficultyTestDir = filepath.Join(baseDir, "BasicTests")
executionSpecBlockchainTestDir = filepath.Join(".", "spec-tests", "fixtures", "blockchain_tests") executionSpecBlockchainTestDir = filepath.Join(".", "spec-tests", "fixtures", "blockchain_tests")
executionSpecStateTestDir = filepath.Join(".", "spec-tests", "fixtures", "state_tests") executionSpecStateTestDir = filepath.Join(".", "spec-tests", "fixtures", "state_tests")
executionSpecTransactionTestDir = filepath.Join(".", "spec-tests", "fixtures", "transaction_tests") executionSpecTransactionTestDir = filepath.Join(".", "spec-tests", "fixtures", "transaction_tests")
benchmarksDir = filepath.Join(".", "evm-benchmarks", "benchmarks") benchmarksDir = filepath.Join(".", "evm-benchmarks", "benchmarks")
executionSpecBALBlockchainTestDir = filepath.Join(".", "spec-tests-bal", "fixtures", "blockchain_tests")
) )
func readJSON(reader io.Reader, value interface{}) error { func readJSON(reader io.Reader, value interface{}) error {