mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
Delete tests directory
This commit is contained in:
parent
52e2ded002
commit
9d267b279d
17 changed files with 0 additions and 2760 deletions
|
|
@ -1,93 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/rand"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBlockchain(t *testing.T) {
|
|
||||||
bt := new(testMatcher)
|
|
||||||
// General state tests are 'exported' as blockchain tests, but we can run them natively.
|
|
||||||
// For speedier CI-runs, the line below can be uncommented, so those are skipped.
|
|
||||||
// For now, in hardfork-times (Berlin), we run the tests both as StateTests and
|
|
||||||
// as blockchain tests, since the latter also covers things like receipt root
|
|
||||||
bt.skipLoad(`^GeneralStateTests/`)
|
|
||||||
|
|
||||||
// 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.*`)
|
|
||||||
|
|
||||||
bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) {
|
|
||||||
if runtime.GOARCH == "386" && runtime.GOOS == "windows" && rand.Int63()%2 == 0 {
|
|
||||||
t.Skip("test (randomly) skipped on 32-bit windows")
|
|
||||||
}
|
|
||||||
execBlockTest(t, bt, test)
|
|
||||||
})
|
|
||||||
// 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.
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestExecutionSpec runs the test fixtures from execution-spec-tests.
|
|
||||||
func TestExecutionSpec(t *testing.T) {
|
|
||||||
if !common.FileExist(executionSpecDir) {
|
|
||||||
t.Skipf("directory %s does not exist", executionSpecDir)
|
|
||||||
}
|
|
||||||
bt := new(testMatcher)
|
|
||||||
|
|
||||||
bt.walk(t, executionSpecDir, func(t *testing.T, name string, test *BlockTest) {
|
|
||||||
execBlockTest(t, bt, test)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func execBlockTest(t *testing.T, bt *testMatcher, test *BlockTest) {
|
|
||||||
if err := bt.checkFailure(t, test.Run(false, rawdb.HashScheme, nil, nil)); err != nil {
|
|
||||||
t.Errorf("test in hash mode without snapshotter failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := bt.checkFailure(t, test.Run(true, rawdb.HashScheme, nil, nil)); err != nil {
|
|
||||||
t.Errorf("test in hash mode with snapshotter failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := bt.checkFailure(t, test.Run(false, rawdb.PathScheme, nil, nil)); err != nil {
|
|
||||||
t.Errorf("test in path mode without snapshotter failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := bt.checkFailure(t, test.Run(true, rawdb.PathScheme, nil, nil)); err != nil {
|
|
||||||
t.Errorf("test in path mode with snapshotter failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,377 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// Package tests implements execution of Ethereum JSON tests.
|
|
||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
|
||||||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
|
||||||
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
|
||||||
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A BlockTest checks handling of entire blocks.
|
|
||||||
type BlockTest struct {
|
|
||||||
json btJSON
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON implements json.Unmarshaler interface.
|
|
||||||
func (t *BlockTest) UnmarshalJSON(in []byte) error {
|
|
||||||
return json.Unmarshal(in, &t.json)
|
|
||||||
}
|
|
||||||
|
|
||||||
type btJSON struct {
|
|
||||||
Blocks []btBlock `json:"blocks"`
|
|
||||||
Genesis btHeader `json:"genesisBlockHeader"`
|
|
||||||
Pre core.GenesisAlloc `json:"pre"`
|
|
||||||
Post core.GenesisAlloc `json:"postState"`
|
|
||||||
BestBlock common.UnprefixedHash `json:"lastblockhash"`
|
|
||||||
Network string `json:"network"`
|
|
||||||
SealEngine string `json:"sealEngine"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type btBlock struct {
|
|
||||||
BlockHeader *btHeader
|
|
||||||
ExpectException string
|
|
||||||
Rlp string
|
|
||||||
UncleHeaders []*btHeader
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type btHeader -field-override btHeaderMarshaling -out gen_btheader.go
|
|
||||||
|
|
||||||
type btHeader struct {
|
|
||||||
Bloom types.Bloom
|
|
||||||
Coinbase common.Address
|
|
||||||
MixHash common.Hash
|
|
||||||
Nonce types.BlockNonce
|
|
||||||
Number *big.Int
|
|
||||||
Hash common.Hash
|
|
||||||
ParentHash common.Hash
|
|
||||||
ReceiptTrie common.Hash
|
|
||||||
StateRoot common.Hash
|
|
||||||
TransactionsTrie common.Hash
|
|
||||||
UncleHash common.Hash
|
|
||||||
ExtraData []byte
|
|
||||||
Difficulty *big.Int
|
|
||||||
GasLimit uint64
|
|
||||||
GasUsed uint64
|
|
||||||
Timestamp uint64
|
|
||||||
BaseFeePerGas *big.Int
|
|
||||||
WithdrawalsRoot *common.Hash
|
|
||||||
BlobGasUsed *uint64
|
|
||||||
ExcessBlobGas *uint64
|
|
||||||
ParentBeaconBlockRoot *common.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
type btHeaderMarshaling struct {
|
|
||||||
ExtraData hexutil.Bytes
|
|
||||||
Number *math.HexOrDecimal256
|
|
||||||
Difficulty *math.HexOrDecimal256
|
|
||||||
GasLimit math.HexOrDecimal64
|
|
||||||
GasUsed math.HexOrDecimal64
|
|
||||||
Timestamp math.HexOrDecimal64
|
|
||||||
BaseFeePerGas *math.HexOrDecimal256
|
|
||||||
BlobGasUsed *math.HexOrDecimal64
|
|
||||||
ExcessBlobGas *math.HexOrDecimal64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *BlockTest) Run(snapshotter bool, scheme string, tracer vm.EVMLogger, 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
|
|
||||||
var (
|
|
||||||
db = rawdb.NewMemoryDatabase()
|
|
||||||
tconf = &trie.Config{
|
|
||||||
Preimages: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if scheme == rawdb.PathScheme {
|
|
||||||
tconf.PathDB = pathdb.Defaults
|
|
||||||
} else {
|
|
||||||
tconf.HashDB = hashdb.Defaults
|
|
||||||
}
|
|
||||||
// Commit genesis state
|
|
||||||
gspec := t.genesis(config)
|
|
||||||
triedb := trie.NewDatabase(db, tconf)
|
|
||||||
gblock, err := gspec.Commit(db, triedb)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
triedb.Close() // close the db to prevent memory leak
|
|
||||||
|
|
||||||
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])
|
|
||||||
}
|
|
||||||
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])
|
|
||||||
}
|
|
||||||
// Wrap the original engine within the beacon-engine
|
|
||||||
engine := beacon.New(ethash.NewFaker())
|
|
||||||
|
|
||||||
cache := &core.CacheConfig{TrieCleanLimit: 0, StateScheme: scheme, Preimages: true}
|
|
||||||
if snapshotter {
|
|
||||||
cache.SnapshotLimit = 1
|
|
||||||
cache.SnapshotWait = true
|
|
||||||
}
|
|
||||||
chain, err := core.NewBlockChain(db, cache, gspec, nil, engine, vm.Config{
|
|
||||||
Tracer: tracer,
|
|
||||||
}, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer chain.Stop()
|
|
||||||
|
|
||||||
validBlocks, err := t.insertBlocks(chain)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Import succeeded: regardless of whether the _test_ succeeds or not, schedule
|
|
||||||
// the post-check to run
|
|
||||||
if postCheck != nil {
|
|
||||||
defer postCheck(result, chain)
|
|
||||||
}
|
|
||||||
cmlast := chain.CurrentBlock().Hash()
|
|
||||||
if common.Hash(t.json.BestBlock) != cmlast {
|
|
||||||
return fmt.Errorf("last block hash validation mismatch: want: %x, have: %x", t.json.BestBlock, cmlast)
|
|
||||||
}
|
|
||||||
newDB, err := chain.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 err := chain.Snapshots().Verify(chain.CurrentBlock().Root); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return t.validateImportedHeaders(chain, validBlocks)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis {
|
|
||||||
return &core.Genesis{
|
|
||||||
Config: config,
|
|
||||||
Nonce: t.json.Genesis.Nonce.Uint64(),
|
|
||||||
Timestamp: t.json.Genesis.Timestamp,
|
|
||||||
ParentHash: t.json.Genesis.ParentHash,
|
|
||||||
ExtraData: t.json.Genesis.ExtraData,
|
|
||||||
GasLimit: t.json.Genesis.GasLimit,
|
|
||||||
GasUsed: t.json.Genesis.GasUsed,
|
|
||||||
Difficulty: t.json.Genesis.Difficulty,
|
|
||||||
Mixhash: t.json.Genesis.MixHash,
|
|
||||||
Coinbase: t.json.Genesis.Coinbase,
|
|
||||||
Alloc: t.json.Pre,
|
|
||||||
BaseFee: t.json.Genesis.BaseFeePerGas,
|
|
||||||
BlobGasUsed: t.json.Genesis.BlobGasUsed,
|
|
||||||
ExcessBlobGas: t.json.Genesis.ExcessBlobGas,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II
|
|
||||||
|
|
||||||
Whether a block is valid or not is a bit subtle, it's defined by presence of
|
|
||||||
blockHeader, transactions and uncleHeaders fields. If they are missing, the block is
|
|
||||||
invalid and we must verify that we do not accept it.
|
|
||||||
|
|
||||||
Since some tests mix valid and invalid blocks we need to check this for every block.
|
|
||||||
|
|
||||||
If a block is invalid it does not necessarily fail the test, if it's invalidness is
|
|
||||||
expected we are expected to ignore it and continue processing and then validate the
|
|
||||||
post state.
|
|
||||||
*/
|
|
||||||
func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) {
|
|
||||||
validBlocks := make([]btBlock, 0)
|
|
||||||
// insert the test blocks, which will execute all transactions
|
|
||||||
for bi, b := range t.json.Blocks {
|
|
||||||
cb, err := b.decode()
|
|
||||||
if err != nil {
|
|
||||||
if b.BlockHeader == nil {
|
|
||||||
continue // OK - block is supposed to be invalid, continue with next block
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("block RLP decoding failed when expected to succeed: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// RLP decoding worked, try to insert into chain:
|
|
||||||
blocks := types.Blocks{cb}
|
|
||||||
i, err := blockchain.InsertChain(blocks)
|
|
||||||
if err != nil {
|
|
||||||
if b.BlockHeader == nil {
|
|
||||||
continue // OK - block is supposed to be invalid, continue with next block
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("block #%v insertion into chain failed: %v", blocks[i].Number(), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if b.BlockHeader == nil {
|
|
||||||
if data, err := json.MarshalIndent(cb.Header(), "", " "); err == nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "block (index %d) insertion should have failed due to: %v:\n%v\n",
|
|
||||||
bi, b.ExpectException, string(data))
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("block (index %d) insertion should have failed due to: %v",
|
|
||||||
bi, b.ExpectException)
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate RLP decoding by checking all values against test file JSON
|
|
||||||
if err = validateHeader(b.BlockHeader, cb.Header()); err != nil {
|
|
||||||
return nil, fmt.Errorf("deserialised block header validation failed: %v", err)
|
|
||||||
}
|
|
||||||
validBlocks = append(validBlocks, b)
|
|
||||||
}
|
|
||||||
return validBlocks, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateHeader(h *btHeader, h2 *types.Header) error {
|
|
||||||
if h.Bloom != h2.Bloom {
|
|
||||||
return fmt.Errorf("bloom: want: %x have: %x", h.Bloom, h2.Bloom)
|
|
||||||
}
|
|
||||||
if h.Coinbase != h2.Coinbase {
|
|
||||||
return fmt.Errorf("coinbase: want: %x have: %x", h.Coinbase, h2.Coinbase)
|
|
||||||
}
|
|
||||||
if h.MixHash != h2.MixDigest {
|
|
||||||
return fmt.Errorf("MixHash: want: %x have: %x", h.MixHash, h2.MixDigest)
|
|
||||||
}
|
|
||||||
if h.Nonce != h2.Nonce {
|
|
||||||
return fmt.Errorf("nonce: want: %x have: %x", h.Nonce, h2.Nonce)
|
|
||||||
}
|
|
||||||
if h.Number.Cmp(h2.Number) != 0 {
|
|
||||||
return fmt.Errorf("number: want: %v have: %v", h.Number, h2.Number)
|
|
||||||
}
|
|
||||||
if h.ParentHash != h2.ParentHash {
|
|
||||||
return fmt.Errorf("parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash)
|
|
||||||
}
|
|
||||||
if h.ReceiptTrie != h2.ReceiptHash {
|
|
||||||
return fmt.Errorf("receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash)
|
|
||||||
}
|
|
||||||
if h.TransactionsTrie != h2.TxHash {
|
|
||||||
return fmt.Errorf("tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash)
|
|
||||||
}
|
|
||||||
if h.StateRoot != h2.Root {
|
|
||||||
return fmt.Errorf("state hash: want: %x have: %x", h.StateRoot, h2.Root)
|
|
||||||
}
|
|
||||||
if h.UncleHash != h2.UncleHash {
|
|
||||||
return fmt.Errorf("uncle hash: want: %x have: %x", h.UncleHash, h2.UncleHash)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(h.ExtraData, h2.Extra) {
|
|
||||||
return fmt.Errorf("extra data: want: %x have: %x", h.ExtraData, h2.Extra)
|
|
||||||
}
|
|
||||||
if h.Difficulty.Cmp(h2.Difficulty) != 0 {
|
|
||||||
return fmt.Errorf("difficulty: want: %v have: %v", h.Difficulty, h2.Difficulty)
|
|
||||||
}
|
|
||||||
if h.GasLimit != h2.GasLimit {
|
|
||||||
return fmt.Errorf("gasLimit: want: %d have: %d", h.GasLimit, h2.GasLimit)
|
|
||||||
}
|
|
||||||
if h.GasUsed != h2.GasUsed {
|
|
||||||
return fmt.Errorf("gasUsed: want: %d have: %d", h.GasUsed, h2.GasUsed)
|
|
||||||
}
|
|
||||||
if h.Timestamp != h2.Time {
|
|
||||||
return fmt.Errorf("timestamp: want: %v have: %v", h.Timestamp, h2.Time)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(h.BaseFeePerGas, h2.BaseFee) {
|
|
||||||
return fmt.Errorf("baseFeePerGas: want: %v have: %v", h.BaseFeePerGas, h2.BaseFee)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(h.WithdrawalsRoot, h2.WithdrawalsHash) {
|
|
||||||
return fmt.Errorf("withdrawalsRoot: want: %v have: %v", h.WithdrawalsRoot, h2.WithdrawalsHash)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(h.BlobGasUsed, h2.BlobGasUsed) {
|
|
||||||
return fmt.Errorf("blobGasUsed: want: %v have: %v", h.BlobGasUsed, h2.BlobGasUsed)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(h.ExcessBlobGas, h2.ExcessBlobGas) {
|
|
||||||
return fmt.Errorf("excessBlobGas: want: %v have: %v", h.ExcessBlobGas, h2.ExcessBlobGas)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(h.ParentBeaconBlockRoot, h2.ParentBeaconRoot) {
|
|
||||||
return fmt.Errorf("parentBeaconBlockRoot: want: %v have: %v", h.ParentBeaconBlockRoot, h2.ParentBeaconRoot)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *BlockTest) validatePostState(statedb *state.StateDB) error {
|
|
||||||
// validate post state accounts in test file against what we have in state db
|
|
||||||
for addr, acct := range t.json.Post {
|
|
||||||
// address is indirectly verified by the other fields, as it's the db key
|
|
||||||
code2 := statedb.GetCode(addr)
|
|
||||||
balance2 := statedb.GetBalance(addr)
|
|
||||||
nonce2 := statedb.GetNonce(addr)
|
|
||||||
if !bytes.Equal(code2, acct.Code) {
|
|
||||||
return fmt.Errorf("account code mismatch for addr: %s want: %v have: %s", addr, acct.Code, hex.EncodeToString(code2))
|
|
||||||
}
|
|
||||||
if balance2.Cmp(acct.Balance) != 0 {
|
|
||||||
return fmt.Errorf("account balance mismatch for addr: %s, want: %d, have: %d", addr, acct.Balance, balance2)
|
|
||||||
}
|
|
||||||
if nonce2 != acct.Nonce {
|
|
||||||
return fmt.Errorf("account nonce mismatch for addr: %s want: %d have: %d", addr, acct.Nonce, nonce2)
|
|
||||||
}
|
|
||||||
for k, v := range acct.Storage {
|
|
||||||
v2 := statedb.GetState(addr, k)
|
|
||||||
if v2 != v {
|
|
||||||
return fmt.Errorf("account storage mismatch for addr: %s, slot: %x, want: %x, have: %x", addr, k, v, v2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *BlockTest) validateImportedHeaders(cm *core.BlockChain, validBlocks []btBlock) error {
|
|
||||||
// to get constant lookup when verifying block headers by hash (some tests have many blocks)
|
|
||||||
bmap := make(map[common.Hash]btBlock, len(t.json.Blocks))
|
|
||||||
for _, b := range validBlocks {
|
|
||||||
bmap[b.BlockHeader.Hash] = b
|
|
||||||
}
|
|
||||||
// iterate over blocks backwards from HEAD and validate imported
|
|
||||||
// headers vs test file. some tests have reorgs, and we import
|
|
||||||
// block-by-block, so we can only validate imported headers after
|
|
||||||
// all blocks have been processed by BlockChain, as they may not
|
|
||||||
// be part of the longest chain until last block is imported.
|
|
||||||
for b := cm.CurrentBlock(); b != nil && b.Number.Uint64() != 0; b = cm.GetBlockByHash(b.ParentHash).Header() {
|
|
||||||
if err := validateHeader(bmap[b.Hash()].BlockHeader, b); err != nil {
|
|
||||||
return fmt.Errorf("imported block header validation failed: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bb *btBlock) decode() (*types.Block, error) {
|
|
||||||
data, err := hexutil.Decode(bb.Rlp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var b types.Block
|
|
||||||
err = rlp.DecodeBytes(data, &b)
|
|
||||||
return &b, err
|
|
||||||
}
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
// Copyright 2017 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
mainnetChainConfig = params.ChainConfig{
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(1150000),
|
|
||||||
DAOForkBlock: big.NewInt(1920000),
|
|
||||||
DAOForkSupport: true,
|
|
||||||
EIP150Block: big.NewInt(2463000),
|
|
||||||
EIP155Block: big.NewInt(2675000),
|
|
||||||
EIP158Block: big.NewInt(2675000),
|
|
||||||
ByzantiumBlock: big.NewInt(4370000),
|
|
||||||
}
|
|
||||||
|
|
||||||
ropstenChainConfig = params.ChainConfig{
|
|
||||||
ChainID: big.NewInt(3),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
DAOForkBlock: nil,
|
|
||||||
DAOForkSupport: true,
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(10),
|
|
||||||
EIP158Block: big.NewInt(10),
|
|
||||||
ByzantiumBlock: big.NewInt(1_700_000),
|
|
||||||
ConstantinopleBlock: big.NewInt(4_230_000),
|
|
||||||
PetersburgBlock: big.NewInt(4_939_394),
|
|
||||||
IstanbulBlock: big.NewInt(6_485_846),
|
|
||||||
MuirGlacierBlock: big.NewInt(7_117_117),
|
|
||||||
BerlinBlock: big.NewInt(9_812_189),
|
|
||||||
LondonBlock: big.NewInt(10_499_401),
|
|
||||||
TerminalTotalDifficulty: new(big.Int).SetUint64(50_000_000_000_000_000),
|
|
||||||
TerminalTotalDifficultyPassed: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDifficulty(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dt := new(testMatcher)
|
|
||||||
// Not difficulty-tests
|
|
||||||
dt.skipLoad("hexencodetest.*")
|
|
||||||
dt.skipLoad("crypto.*")
|
|
||||||
dt.skipLoad("blockgenesistest\\.json")
|
|
||||||
dt.skipLoad("genesishashestest\\.json")
|
|
||||||
dt.skipLoad("keyaddrtest\\.json")
|
|
||||||
dt.skipLoad("txtest\\.json")
|
|
||||||
|
|
||||||
// files are 2 years old, contains strange values
|
|
||||||
dt.skipLoad("difficultyCustomHomestead\\.json")
|
|
||||||
|
|
||||||
dt.config("Ropsten", ropstenChainConfig)
|
|
||||||
dt.config("Frontier", params.ChainConfig{})
|
|
||||||
|
|
||||||
dt.config("Homestead", params.ChainConfig{
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
})
|
|
||||||
|
|
||||||
dt.config("Byzantium", params.ChainConfig{
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
})
|
|
||||||
|
|
||||||
dt.config("Frontier", ropstenChainConfig)
|
|
||||||
dt.config("MainNetwork", mainnetChainConfig)
|
|
||||||
dt.config("CustomMainNetwork", mainnetChainConfig)
|
|
||||||
dt.config("Constantinople", params.ChainConfig{
|
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
|
||||||
})
|
|
||||||
dt.config("EIP2384", params.ChainConfig{
|
|
||||||
MuirGlacierBlock: big.NewInt(0),
|
|
||||||
})
|
|
||||||
dt.config("EIP4345", params.ChainConfig{
|
|
||||||
ArrowGlacierBlock: big.NewInt(0),
|
|
||||||
})
|
|
||||||
dt.config("EIP5133", params.ChainConfig{
|
|
||||||
GrayGlacierBlock: big.NewInt(0),
|
|
||||||
})
|
|
||||||
dt.config("difficulty.json", mainnetChainConfig)
|
|
||||||
|
|
||||||
dt.walk(t, difficultyTestDir, func(t *testing.T, name string, test *DifficultyTest) {
|
|
||||||
cfg := dt.findConfig(t)
|
|
||||||
if test.ParentDifficulty.Cmp(params.MinimumDifficulty) < 0 {
|
|
||||||
t.Skip("difficulty below minimum")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := dt.checkFailure(t, test.Run(cfg)); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
// Copyright 2017 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type DifficultyTest -field-override difficultyTestMarshaling -out gen_difficultytest.go
|
|
||||||
|
|
||||||
type DifficultyTest struct {
|
|
||||||
ParentTimestamp uint64 `json:"parentTimestamp"`
|
|
||||||
ParentDifficulty *big.Int `json:"parentDifficulty"`
|
|
||||||
UncleHash common.Hash `json:"parentUncles"`
|
|
||||||
CurrentTimestamp uint64 `json:"currentTimestamp"`
|
|
||||||
CurrentBlockNumber uint64 `json:"currentBlockNumber"`
|
|
||||||
CurrentDifficulty *big.Int `json:"currentDifficulty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type difficultyTestMarshaling struct {
|
|
||||||
ParentTimestamp math.HexOrDecimal64
|
|
||||||
ParentDifficulty *math.HexOrDecimal256
|
|
||||||
CurrentTimestamp math.HexOrDecimal64
|
|
||||||
CurrentDifficulty *math.HexOrDecimal256
|
|
||||||
UncleHash common.Hash
|
|
||||||
CurrentBlockNumber math.HexOrDecimal64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (test *DifficultyTest) Run(config *params.ChainConfig) error {
|
|
||||||
parentNumber := big.NewInt(int64(test.CurrentBlockNumber - 1))
|
|
||||||
parent := &types.Header{
|
|
||||||
Difficulty: test.ParentDifficulty,
|
|
||||||
Time: test.ParentTimestamp,
|
|
||||||
Number: parentNumber,
|
|
||||||
UncleHash: test.UncleHash,
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := ethash.CalcDifficulty(config, test.CurrentTimestamp, parent)
|
|
||||||
exp := test.CurrentDifficulty
|
|
||||||
|
|
||||||
if actual.Cmp(exp) != 0 {
|
|
||||||
return fmt.Errorf("parent[time %v diff %v unclehash:%x] child[time %v number %v] diff %v != expected %v",
|
|
||||||
test.ParentTimestamp, test.ParentDifficulty, test.UncleHash,
|
|
||||||
test.CurrentTimestamp, test.CurrentBlockNumber, actual, exp)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit d8b88f4046a87d6b902378cef752591f95427b43
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
|
||||||
|
|
||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ = (*btHeaderMarshaling)(nil)
|
|
||||||
|
|
||||||
// MarshalJSON marshals as JSON.
|
|
||||||
func (b btHeader) MarshalJSON() ([]byte, error) {
|
|
||||||
type btHeader struct {
|
|
||||||
Bloom types.Bloom
|
|
||||||
Coinbase common.Address
|
|
||||||
MixHash common.Hash
|
|
||||||
Nonce types.BlockNonce
|
|
||||||
Number *math.HexOrDecimal256
|
|
||||||
Hash common.Hash
|
|
||||||
ParentHash common.Hash
|
|
||||||
ReceiptTrie common.Hash
|
|
||||||
StateRoot common.Hash
|
|
||||||
TransactionsTrie common.Hash
|
|
||||||
UncleHash common.Hash
|
|
||||||
ExtraData hexutil.Bytes
|
|
||||||
Difficulty *math.HexOrDecimal256
|
|
||||||
GasLimit math.HexOrDecimal64
|
|
||||||
GasUsed math.HexOrDecimal64
|
|
||||||
Timestamp math.HexOrDecimal64
|
|
||||||
BaseFeePerGas *math.HexOrDecimal256
|
|
||||||
WithdrawalsRoot *common.Hash
|
|
||||||
BlobGasUsed *math.HexOrDecimal64
|
|
||||||
ExcessBlobGas *math.HexOrDecimal64
|
|
||||||
ParentBeaconBlockRoot *common.Hash
|
|
||||||
}
|
|
||||||
var enc btHeader
|
|
||||||
enc.Bloom = b.Bloom
|
|
||||||
enc.Coinbase = b.Coinbase
|
|
||||||
enc.MixHash = b.MixHash
|
|
||||||
enc.Nonce = b.Nonce
|
|
||||||
enc.Number = (*math.HexOrDecimal256)(b.Number)
|
|
||||||
enc.Hash = b.Hash
|
|
||||||
enc.ParentHash = b.ParentHash
|
|
||||||
enc.ReceiptTrie = b.ReceiptTrie
|
|
||||||
enc.StateRoot = b.StateRoot
|
|
||||||
enc.TransactionsTrie = b.TransactionsTrie
|
|
||||||
enc.UncleHash = b.UncleHash
|
|
||||||
enc.ExtraData = b.ExtraData
|
|
||||||
enc.Difficulty = (*math.HexOrDecimal256)(b.Difficulty)
|
|
||||||
enc.GasLimit = math.HexOrDecimal64(b.GasLimit)
|
|
||||||
enc.GasUsed = math.HexOrDecimal64(b.GasUsed)
|
|
||||||
enc.Timestamp = math.HexOrDecimal64(b.Timestamp)
|
|
||||||
enc.BaseFeePerGas = (*math.HexOrDecimal256)(b.BaseFeePerGas)
|
|
||||||
enc.WithdrawalsRoot = b.WithdrawalsRoot
|
|
||||||
enc.BlobGasUsed = (*math.HexOrDecimal64)(b.BlobGasUsed)
|
|
||||||
enc.ExcessBlobGas = (*math.HexOrDecimal64)(b.ExcessBlobGas)
|
|
||||||
enc.ParentBeaconBlockRoot = b.ParentBeaconBlockRoot
|
|
||||||
return json.Marshal(&enc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON unmarshals from JSON.
|
|
||||||
func (b *btHeader) UnmarshalJSON(input []byte) error {
|
|
||||||
type btHeader struct {
|
|
||||||
Bloom *types.Bloom
|
|
||||||
Coinbase *common.Address
|
|
||||||
MixHash *common.Hash
|
|
||||||
Nonce *types.BlockNonce
|
|
||||||
Number *math.HexOrDecimal256
|
|
||||||
Hash *common.Hash
|
|
||||||
ParentHash *common.Hash
|
|
||||||
ReceiptTrie *common.Hash
|
|
||||||
StateRoot *common.Hash
|
|
||||||
TransactionsTrie *common.Hash
|
|
||||||
UncleHash *common.Hash
|
|
||||||
ExtraData *hexutil.Bytes
|
|
||||||
Difficulty *math.HexOrDecimal256
|
|
||||||
GasLimit *math.HexOrDecimal64
|
|
||||||
GasUsed *math.HexOrDecimal64
|
|
||||||
Timestamp *math.HexOrDecimal64
|
|
||||||
BaseFeePerGas *math.HexOrDecimal256
|
|
||||||
WithdrawalsRoot *common.Hash
|
|
||||||
BlobGasUsed *math.HexOrDecimal64
|
|
||||||
ExcessBlobGas *math.HexOrDecimal64
|
|
||||||
ParentBeaconBlockRoot *common.Hash
|
|
||||||
}
|
|
||||||
var dec btHeader
|
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if dec.Bloom != nil {
|
|
||||||
b.Bloom = *dec.Bloom
|
|
||||||
}
|
|
||||||
if dec.Coinbase != nil {
|
|
||||||
b.Coinbase = *dec.Coinbase
|
|
||||||
}
|
|
||||||
if dec.MixHash != nil {
|
|
||||||
b.MixHash = *dec.MixHash
|
|
||||||
}
|
|
||||||
if dec.Nonce != nil {
|
|
||||||
b.Nonce = *dec.Nonce
|
|
||||||
}
|
|
||||||
if dec.Number != nil {
|
|
||||||
b.Number = (*big.Int)(dec.Number)
|
|
||||||
}
|
|
||||||
if dec.Hash != nil {
|
|
||||||
b.Hash = *dec.Hash
|
|
||||||
}
|
|
||||||
if dec.ParentHash != nil {
|
|
||||||
b.ParentHash = *dec.ParentHash
|
|
||||||
}
|
|
||||||
if dec.ReceiptTrie != nil {
|
|
||||||
b.ReceiptTrie = *dec.ReceiptTrie
|
|
||||||
}
|
|
||||||
if dec.StateRoot != nil {
|
|
||||||
b.StateRoot = *dec.StateRoot
|
|
||||||
}
|
|
||||||
if dec.TransactionsTrie != nil {
|
|
||||||
b.TransactionsTrie = *dec.TransactionsTrie
|
|
||||||
}
|
|
||||||
if dec.UncleHash != nil {
|
|
||||||
b.UncleHash = *dec.UncleHash
|
|
||||||
}
|
|
||||||
if dec.ExtraData != nil {
|
|
||||||
b.ExtraData = *dec.ExtraData
|
|
||||||
}
|
|
||||||
if dec.Difficulty != nil {
|
|
||||||
b.Difficulty = (*big.Int)(dec.Difficulty)
|
|
||||||
}
|
|
||||||
if dec.GasLimit != nil {
|
|
||||||
b.GasLimit = uint64(*dec.GasLimit)
|
|
||||||
}
|
|
||||||
if dec.GasUsed != nil {
|
|
||||||
b.GasUsed = uint64(*dec.GasUsed)
|
|
||||||
}
|
|
||||||
if dec.Timestamp != nil {
|
|
||||||
b.Timestamp = uint64(*dec.Timestamp)
|
|
||||||
}
|
|
||||||
if dec.BaseFeePerGas != nil {
|
|
||||||
b.BaseFeePerGas = (*big.Int)(dec.BaseFeePerGas)
|
|
||||||
}
|
|
||||||
if dec.WithdrawalsRoot != nil {
|
|
||||||
b.WithdrawalsRoot = dec.WithdrawalsRoot
|
|
||||||
}
|
|
||||||
if dec.BlobGasUsed != nil {
|
|
||||||
b.BlobGasUsed = (*uint64)(dec.BlobGasUsed)
|
|
||||||
}
|
|
||||||
if dec.ExcessBlobGas != nil {
|
|
||||||
b.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
|
|
||||||
}
|
|
||||||
if dec.ParentBeaconBlockRoot != nil {
|
|
||||||
b.ParentBeaconBlockRoot = dec.ParentBeaconBlockRoot
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
|
||||||
|
|
||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ = (*difficultyTestMarshaling)(nil)
|
|
||||||
|
|
||||||
// MarshalJSON marshals as JSON.
|
|
||||||
func (d DifficultyTest) MarshalJSON() ([]byte, error) {
|
|
||||||
type DifficultyTest struct {
|
|
||||||
ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp"`
|
|
||||||
ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"`
|
|
||||||
UncleHash common.Hash `json:"parentUncles"`
|
|
||||||
CurrentTimestamp math.HexOrDecimal64 `json:"currentTimestamp"`
|
|
||||||
CurrentBlockNumber math.HexOrDecimal64 `json:"currentBlockNumber"`
|
|
||||||
CurrentDifficulty *math.HexOrDecimal256 `json:"currentDifficulty"`
|
|
||||||
}
|
|
||||||
var enc DifficultyTest
|
|
||||||
enc.ParentTimestamp = math.HexOrDecimal64(d.ParentTimestamp)
|
|
||||||
enc.ParentDifficulty = (*math.HexOrDecimal256)(d.ParentDifficulty)
|
|
||||||
enc.UncleHash = d.UncleHash
|
|
||||||
enc.CurrentTimestamp = math.HexOrDecimal64(d.CurrentTimestamp)
|
|
||||||
enc.CurrentBlockNumber = math.HexOrDecimal64(d.CurrentBlockNumber)
|
|
||||||
enc.CurrentDifficulty = (*math.HexOrDecimal256)(d.CurrentDifficulty)
|
|
||||||
return json.Marshal(&enc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON unmarshals from JSON.
|
|
||||||
func (d *DifficultyTest) UnmarshalJSON(input []byte) error {
|
|
||||||
type DifficultyTest struct {
|
|
||||||
ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp"`
|
|
||||||
ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"`
|
|
||||||
UncleHash *common.Hash `json:"parentUncles"`
|
|
||||||
CurrentTimestamp *math.HexOrDecimal64 `json:"currentTimestamp"`
|
|
||||||
CurrentBlockNumber *math.HexOrDecimal64 `json:"currentBlockNumber"`
|
|
||||||
CurrentDifficulty *math.HexOrDecimal256 `json:"currentDifficulty"`
|
|
||||||
}
|
|
||||||
var dec DifficultyTest
|
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if dec.ParentTimestamp != nil {
|
|
||||||
d.ParentTimestamp = uint64(*dec.ParentTimestamp)
|
|
||||||
}
|
|
||||||
if dec.ParentDifficulty != nil {
|
|
||||||
d.ParentDifficulty = (*big.Int)(dec.ParentDifficulty)
|
|
||||||
}
|
|
||||||
if dec.UncleHash != nil {
|
|
||||||
d.UncleHash = *dec.UncleHash
|
|
||||||
}
|
|
||||||
if dec.CurrentTimestamp != nil {
|
|
||||||
d.CurrentTimestamp = uint64(*dec.CurrentTimestamp)
|
|
||||||
}
|
|
||||||
if dec.CurrentBlockNumber != nil {
|
|
||||||
d.CurrentBlockNumber = uint64(*dec.CurrentBlockNumber)
|
|
||||||
}
|
|
||||||
if dec.CurrentDifficulty != nil {
|
|
||||||
d.CurrentDifficulty = (*big.Int)(dec.CurrentDifficulty)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
|
||||||
|
|
||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ = (*stEnvMarshaling)(nil)
|
|
||||||
|
|
||||||
// MarshalJSON marshals as JSON.
|
|
||||||
func (s stEnv) MarshalJSON() ([]byte, error) {
|
|
||||||
type stEnv struct {
|
|
||||||
Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
|
|
||||||
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"optional"`
|
|
||||||
Random *math.HexOrDecimal256 `json:"currentRandom" gencodec:"optional"`
|
|
||||||
GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
|
|
||||||
Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
|
||||||
Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
|
||||||
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"`
|
|
||||||
ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas" gencodec:"optional"`
|
|
||||||
}
|
|
||||||
var enc stEnv
|
|
||||||
enc.Coinbase = common.UnprefixedAddress(s.Coinbase)
|
|
||||||
enc.Difficulty = (*math.HexOrDecimal256)(s.Difficulty)
|
|
||||||
enc.Random = (*math.HexOrDecimal256)(s.Random)
|
|
||||||
enc.GasLimit = math.HexOrDecimal64(s.GasLimit)
|
|
||||||
enc.Number = math.HexOrDecimal64(s.Number)
|
|
||||||
enc.Timestamp = math.HexOrDecimal64(s.Timestamp)
|
|
||||||
enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee)
|
|
||||||
enc.ExcessBlobGas = (*math.HexOrDecimal64)(s.ExcessBlobGas)
|
|
||||||
return json.Marshal(&enc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON unmarshals from JSON.
|
|
||||||
func (s *stEnv) UnmarshalJSON(input []byte) error {
|
|
||||||
type stEnv struct {
|
|
||||||
Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
|
|
||||||
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"optional"`
|
|
||||||
Random *math.HexOrDecimal256 `json:"currentRandom" gencodec:"optional"`
|
|
||||||
GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
|
|
||||||
Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
|
||||||
Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
|
||||||
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"`
|
|
||||||
ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas" gencodec:"optional"`
|
|
||||||
}
|
|
||||||
var dec stEnv
|
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if dec.Coinbase == nil {
|
|
||||||
return errors.New("missing required field 'currentCoinbase' for stEnv")
|
|
||||||
}
|
|
||||||
s.Coinbase = common.Address(*dec.Coinbase)
|
|
||||||
if dec.Difficulty != nil {
|
|
||||||
s.Difficulty = (*big.Int)(dec.Difficulty)
|
|
||||||
}
|
|
||||||
if dec.Random != nil {
|
|
||||||
s.Random = (*big.Int)(dec.Random)
|
|
||||||
}
|
|
||||||
if dec.GasLimit == nil {
|
|
||||||
return errors.New("missing required field 'currentGasLimit' for stEnv")
|
|
||||||
}
|
|
||||||
s.GasLimit = uint64(*dec.GasLimit)
|
|
||||||
if dec.Number == nil {
|
|
||||||
return errors.New("missing required field 'currentNumber' for stEnv")
|
|
||||||
}
|
|
||||||
s.Number = uint64(*dec.Number)
|
|
||||||
if dec.Timestamp == nil {
|
|
||||||
return errors.New("missing required field 'currentTimestamp' for stEnv")
|
|
||||||
}
|
|
||||||
s.Timestamp = uint64(*dec.Timestamp)
|
|
||||||
if dec.BaseFee != nil {
|
|
||||||
s.BaseFee = (*big.Int)(dec.BaseFee)
|
|
||||||
}
|
|
||||||
if dec.ExcessBlobGas != nil {
|
|
||||||
s.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
359
tests/init.go
359
tests/init.go
|
|
@ -1,359 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
func u64(val uint64) *uint64 { return &val }
|
|
||||||
|
|
||||||
// Forks table defines supported forks and their chain config.
|
|
||||||
var Forks = map[string]*params.ChainConfig{
|
|
||||||
"Frontier": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
},
|
|
||||||
"Homestead": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
},
|
|
||||||
"EIP150": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
},
|
|
||||||
"EIP158": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
},
|
|
||||||
"Byzantium": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
DAOForkBlock: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
},
|
|
||||||
"Constantinople": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
DAOForkBlock: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
|
||||||
PetersburgBlock: big.NewInt(10000000),
|
|
||||||
},
|
|
||||||
"ConstantinopleFix": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
DAOForkBlock: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
|
||||||
PetersburgBlock: big.NewInt(0),
|
|
||||||
},
|
|
||||||
"Istanbul": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
DAOForkBlock: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
|
||||||
PetersburgBlock: big.NewInt(0),
|
|
||||||
IstanbulBlock: big.NewInt(0),
|
|
||||||
},
|
|
||||||
"MuirGlacier": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
DAOForkBlock: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
|
||||||
PetersburgBlock: big.NewInt(0),
|
|
||||||
IstanbulBlock: big.NewInt(0),
|
|
||||||
MuirGlacierBlock: big.NewInt(0),
|
|
||||||
},
|
|
||||||
"FrontierToHomesteadAt5": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(5),
|
|
||||||
},
|
|
||||||
"HomesteadToEIP150At5": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(5),
|
|
||||||
},
|
|
||||||
"HomesteadToDaoAt5": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
DAOForkBlock: big.NewInt(5),
|
|
||||||
DAOForkSupport: true,
|
|
||||||
},
|
|
||||||
"EIP158ToByzantiumAt5": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(5),
|
|
||||||
},
|
|
||||||
"ByzantiumToConstantinopleAt5": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(5),
|
|
||||||
},
|
|
||||||
"ByzantiumToConstantinopleFixAt5": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(5),
|
|
||||||
PetersburgBlock: big.NewInt(5),
|
|
||||||
},
|
|
||||||
"ConstantinopleFixToIstanbulAt5": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
|
||||||
PetersburgBlock: big.NewInt(0),
|
|
||||||
IstanbulBlock: big.NewInt(5),
|
|
||||||
},
|
|
||||||
"Berlin": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
|
||||||
PetersburgBlock: big.NewInt(0),
|
|
||||||
IstanbulBlock: big.NewInt(0),
|
|
||||||
MuirGlacierBlock: big.NewInt(0),
|
|
||||||
BerlinBlock: big.NewInt(0),
|
|
||||||
},
|
|
||||||
"BerlinToLondonAt5": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
|
||||||
PetersburgBlock: big.NewInt(0),
|
|
||||||
IstanbulBlock: big.NewInt(0),
|
|
||||||
MuirGlacierBlock: big.NewInt(0),
|
|
||||||
BerlinBlock: big.NewInt(0),
|
|
||||||
LondonBlock: big.NewInt(5),
|
|
||||||
},
|
|
||||||
"London": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
|
||||||
PetersburgBlock: big.NewInt(0),
|
|
||||||
IstanbulBlock: big.NewInt(0),
|
|
||||||
MuirGlacierBlock: big.NewInt(0),
|
|
||||||
BerlinBlock: big.NewInt(0),
|
|
||||||
LondonBlock: big.NewInt(0),
|
|
||||||
},
|
|
||||||
"ArrowGlacier": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
|
||||||
PetersburgBlock: big.NewInt(0),
|
|
||||||
IstanbulBlock: big.NewInt(0),
|
|
||||||
MuirGlacierBlock: big.NewInt(0),
|
|
||||||
BerlinBlock: big.NewInt(0),
|
|
||||||
LondonBlock: big.NewInt(0),
|
|
||||||
ArrowGlacierBlock: big.NewInt(0),
|
|
||||||
},
|
|
||||||
"ArrowGlacierToMergeAtDiffC0000": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
|
||||||
PetersburgBlock: big.NewInt(0),
|
|
||||||
IstanbulBlock: big.NewInt(0),
|
|
||||||
MuirGlacierBlock: big.NewInt(0),
|
|
||||||
BerlinBlock: big.NewInt(0),
|
|
||||||
LondonBlock: big.NewInt(0),
|
|
||||||
ArrowGlacierBlock: big.NewInt(0),
|
|
||||||
GrayGlacierBlock: big.NewInt(0),
|
|
||||||
MergeNetsplitBlock: big.NewInt(0),
|
|
||||||
TerminalTotalDifficulty: big.NewInt(0xC0000),
|
|
||||||
},
|
|
||||||
"GrayGlacier": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
|
||||||
PetersburgBlock: big.NewInt(0),
|
|
||||||
IstanbulBlock: big.NewInt(0),
|
|
||||||
MuirGlacierBlock: big.NewInt(0),
|
|
||||||
BerlinBlock: big.NewInt(0),
|
|
||||||
LondonBlock: big.NewInt(0),
|
|
||||||
ArrowGlacierBlock: big.NewInt(0),
|
|
||||||
GrayGlacierBlock: big.NewInt(0),
|
|
||||||
},
|
|
||||||
"Merge": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
|
||||||
PetersburgBlock: big.NewInt(0),
|
|
||||||
IstanbulBlock: big.NewInt(0),
|
|
||||||
MuirGlacierBlock: big.NewInt(0),
|
|
||||||
BerlinBlock: big.NewInt(0),
|
|
||||||
LondonBlock: big.NewInt(0),
|
|
||||||
ArrowGlacierBlock: big.NewInt(0),
|
|
||||||
MergeNetsplitBlock: big.NewInt(0),
|
|
||||||
TerminalTotalDifficulty: big.NewInt(0),
|
|
||||||
},
|
|
||||||
"Shanghai": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
|
||||||
PetersburgBlock: big.NewInt(0),
|
|
||||||
IstanbulBlock: big.NewInt(0),
|
|
||||||
MuirGlacierBlock: big.NewInt(0),
|
|
||||||
BerlinBlock: big.NewInt(0),
|
|
||||||
LondonBlock: big.NewInt(0),
|
|
||||||
ArrowGlacierBlock: big.NewInt(0),
|
|
||||||
MergeNetsplitBlock: big.NewInt(0),
|
|
||||||
TerminalTotalDifficulty: big.NewInt(0),
|
|
||||||
ShanghaiTime: u64(0),
|
|
||||||
},
|
|
||||||
"MergeToShanghaiAtTime15k": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
|
||||||
PetersburgBlock: big.NewInt(0),
|
|
||||||
IstanbulBlock: big.NewInt(0),
|
|
||||||
MuirGlacierBlock: big.NewInt(0),
|
|
||||||
BerlinBlock: big.NewInt(0),
|
|
||||||
LondonBlock: big.NewInt(0),
|
|
||||||
ArrowGlacierBlock: big.NewInt(0),
|
|
||||||
MergeNetsplitBlock: big.NewInt(0),
|
|
||||||
TerminalTotalDifficulty: big.NewInt(0),
|
|
||||||
ShanghaiTime: u64(15_000),
|
|
||||||
},
|
|
||||||
"Cancun": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
|
||||||
PetersburgBlock: big.NewInt(0),
|
|
||||||
IstanbulBlock: big.NewInt(0),
|
|
||||||
MuirGlacierBlock: big.NewInt(0),
|
|
||||||
BerlinBlock: big.NewInt(0),
|
|
||||||
LondonBlock: big.NewInt(0),
|
|
||||||
ArrowGlacierBlock: big.NewInt(0),
|
|
||||||
MergeNetsplitBlock: big.NewInt(0),
|
|
||||||
TerminalTotalDifficulty: big.NewInt(0),
|
|
||||||
ShanghaiTime: u64(0),
|
|
||||||
CancunTime: u64(0),
|
|
||||||
},
|
|
||||||
"ShanghaiToCancunAtTime15k": {
|
|
||||||
ChainID: big.NewInt(1),
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
EIP150Block: big.NewInt(0),
|
|
||||||
EIP155Block: big.NewInt(0),
|
|
||||||
EIP158Block: big.NewInt(0),
|
|
||||||
ByzantiumBlock: big.NewInt(0),
|
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
|
||||||
PetersburgBlock: big.NewInt(0),
|
|
||||||
IstanbulBlock: big.NewInt(0),
|
|
||||||
MuirGlacierBlock: big.NewInt(0),
|
|
||||||
BerlinBlock: big.NewInt(0),
|
|
||||||
LondonBlock: big.NewInt(0),
|
|
||||||
ArrowGlacierBlock: big.NewInt(0),
|
|
||||||
MergeNetsplitBlock: big.NewInt(0),
|
|
||||||
TerminalTotalDifficulty: big.NewInt(0),
|
|
||||||
ShanghaiTime: u64(0),
|
|
||||||
CancunTime: u64(15_000),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// AvailableForks returns the set of defined fork names
|
|
||||||
func AvailableForks() []string {
|
|
||||||
var availableForks []string
|
|
||||||
for k := range Forks {
|
|
||||||
availableForks = append(availableForks, k)
|
|
||||||
}
|
|
||||||
sort.Strings(availableForks)
|
|
||||||
return availableForks
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnsupportedForkError is returned when a test requests a fork that isn't implemented.
|
|
||||||
type UnsupportedForkError struct {
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e UnsupportedForkError) Error() string {
|
|
||||||
return fmt.Sprintf("unsupported fork %q", e.Name)
|
|
||||||
}
|
|
||||||
|
|
@ -1,291 +0,0 @@
|
||||||
// Copyright 2017 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"runtime"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
baseDir = filepath.Join(".", "testdata")
|
|
||||||
blockTestDir = filepath.Join(baseDir, "BlockchainTests")
|
|
||||||
stateTestDir = filepath.Join(baseDir, "GeneralStateTests")
|
|
||||||
legacyStateTestDir = filepath.Join(baseDir, "LegacyTests", "Constantinople", "GeneralStateTests")
|
|
||||||
transactionTestDir = filepath.Join(baseDir, "TransactionTests")
|
|
||||||
rlpTestDir = filepath.Join(baseDir, "RLPTests")
|
|
||||||
difficultyTestDir = filepath.Join(baseDir, "BasicTests")
|
|
||||||
executionSpecDir = filepath.Join(".", "spec-tests", "fixtures")
|
|
||||||
benchmarksDir = filepath.Join(".", "evm-benchmarks", "benchmarks")
|
|
||||||
)
|
|
||||||
|
|
||||||
func readJSON(reader io.Reader, value interface{}) error {
|
|
||||||
data, err := io.ReadAll(reader)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error reading JSON file: %v", err)
|
|
||||||
}
|
|
||||||
if err = json.Unmarshal(data, &value); err != nil {
|
|
||||||
if syntaxerr, ok := err.(*json.SyntaxError); ok {
|
|
||||||
line := findLine(data, syntaxerr.Offset)
|
|
||||||
return fmt.Errorf("JSON syntax error at line %v: %v", line, err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readJSONFile(fn string, value interface{}) error {
|
|
||||||
file, err := os.Open(fn)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
err = readJSON(file, value)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s in file %s", err.Error(), fn)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// findLine returns the line number for the given offset into data.
|
|
||||||
func findLine(data []byte, offset int64) (line int) {
|
|
||||||
line = 1
|
|
||||||
for i, r := range string(data) {
|
|
||||||
if int64(i) >= offset {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if r == '\n' {
|
|
||||||
line++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// testMatcher controls skipping and chain config assignment to tests.
|
|
||||||
type testMatcher struct {
|
|
||||||
configpat []testConfig
|
|
||||||
failpat []testFailure
|
|
||||||
skiploadpat []*regexp.Regexp
|
|
||||||
slowpat []*regexp.Regexp
|
|
||||||
runonlylistpat *regexp.Regexp
|
|
||||||
}
|
|
||||||
|
|
||||||
type testConfig struct {
|
|
||||||
p *regexp.Regexp
|
|
||||||
config params.ChainConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
type testFailure struct {
|
|
||||||
p *regexp.Regexp
|
|
||||||
reason string
|
|
||||||
}
|
|
||||||
|
|
||||||
// skipShortMode skips tests matching when the -short flag is used.
|
|
||||||
func (tm *testMatcher) slow(pattern string) {
|
|
||||||
tm.slowpat = append(tm.slowpat, regexp.MustCompile(pattern))
|
|
||||||
}
|
|
||||||
|
|
||||||
// skipLoad skips JSON loading of tests matching the pattern.
|
|
||||||
func (tm *testMatcher) skipLoad(pattern string) {
|
|
||||||
tm.skiploadpat = append(tm.skiploadpat, regexp.MustCompile(pattern))
|
|
||||||
}
|
|
||||||
|
|
||||||
// fails adds an expected failure for tests matching the pattern.
|
|
||||||
//
|
|
||||||
//nolint:unused
|
|
||||||
func (tm *testMatcher) fails(pattern string, reason string) {
|
|
||||||
if reason == "" {
|
|
||||||
panic("empty fail reason")
|
|
||||||
}
|
|
||||||
tm.failpat = append(tm.failpat, testFailure{regexp.MustCompile(pattern), reason})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *testMatcher) runonly(pattern string) {
|
|
||||||
tm.runonlylistpat = regexp.MustCompile(pattern)
|
|
||||||
}
|
|
||||||
|
|
||||||
// config defines chain config for tests matching the pattern.
|
|
||||||
func (tm *testMatcher) config(pattern string, cfg params.ChainConfig) {
|
|
||||||
tm.configpat = append(tm.configpat, testConfig{regexp.MustCompile(pattern), cfg})
|
|
||||||
}
|
|
||||||
|
|
||||||
// findSkip matches name against test skip patterns.
|
|
||||||
func (tm *testMatcher) findSkip(name string) (reason string, skipload bool) {
|
|
||||||
isWin32 := runtime.GOARCH == "386" && runtime.GOOS == "windows"
|
|
||||||
for _, re := range tm.slowpat {
|
|
||||||
if re.MatchString(name) {
|
|
||||||
if testing.Short() {
|
|
||||||
return "skipped in -short mode", false
|
|
||||||
}
|
|
||||||
if isWin32 {
|
|
||||||
return "skipped on 32bit windows", false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, re := range tm.skiploadpat {
|
|
||||||
if re.MatchString(name) {
|
|
||||||
return "skipped by skipLoad", true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
// findConfig returns the chain config matching defined patterns.
|
|
||||||
func (tm *testMatcher) findConfig(t *testing.T) *params.ChainConfig {
|
|
||||||
for _, m := range tm.configpat {
|
|
||||||
if m.p.MatchString(t.Name()) {
|
|
||||||
return &m.config
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new(params.ChainConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkFailure checks whether a failure is expected.
|
|
||||||
func (tm *testMatcher) checkFailure(t *testing.T, err error) error {
|
|
||||||
failReason := ""
|
|
||||||
for _, m := range tm.failpat {
|
|
||||||
if m.p.MatchString(t.Name()) {
|
|
||||||
failReason = m.reason
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if failReason != "" {
|
|
||||||
t.Logf("expected failure: %s", failReason)
|
|
||||||
if err != nil {
|
|
||||||
t.Logf("error: %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return errors.New("test succeeded unexpectedly")
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// walk invokes its runTest argument for all subtests in the given directory.
|
|
||||||
//
|
|
||||||
// runTest should be a function of type func(t *testing.T, name string, x <TestType>),
|
|
||||||
// where TestType is the type of the test contained in test files.
|
|
||||||
func (tm *testMatcher) walk(t *testing.T, dir string, runTest interface{}) {
|
|
||||||
// Walk the directory.
|
|
||||||
dirinfo, err := os.Stat(dir)
|
|
||||||
if os.IsNotExist(err) || !dirinfo.IsDir() {
|
|
||||||
fmt.Fprintf(os.Stderr, "can't find test files in %s, did you clone the tests submodule?\n", dir)
|
|
||||||
t.Skip("missing test files")
|
|
||||||
}
|
|
||||||
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
|
||||||
name := filepath.ToSlash(strings.TrimPrefix(path, dir+string(filepath.Separator)))
|
|
||||||
if info.IsDir() {
|
|
||||||
if _, skipload := tm.findSkip(name + "/"); skipload {
|
|
||||||
return filepath.SkipDir
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if filepath.Ext(path) == ".json" {
|
|
||||||
t.Run(name, func(t *testing.T) { tm.runTestFile(t, path, name, runTest) })
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *testMatcher) runTestFile(t *testing.T, path, name string, runTest interface{}) {
|
|
||||||
if r, _ := tm.findSkip(name); r != "" {
|
|
||||||
t.Skip(r)
|
|
||||||
}
|
|
||||||
if tm.runonlylistpat != nil {
|
|
||||||
if !tm.runonlylistpat.MatchString(name) {
|
|
||||||
t.Skip("Skipped by runonly")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
// Load the file as map[string]<testType>.
|
|
||||||
m := makeMapFromTestFunc(runTest)
|
|
||||||
if err := readJSONFile(path, m.Addr().Interface()); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run all tests from the map. Don't wrap in a subtest if there is only one test in the file.
|
|
||||||
keys := sortedMapKeys(m)
|
|
||||||
if len(keys) == 1 {
|
|
||||||
runTestFunc(runTest, t, name, m, keys[0])
|
|
||||||
} else {
|
|
||||||
for _, key := range keys {
|
|
||||||
name := name + "/" + key
|
|
||||||
t.Run(key, func(t *testing.T) {
|
|
||||||
if r, _ := tm.findSkip(name); r != "" {
|
|
||||||
t.Skip(r)
|
|
||||||
}
|
|
||||||
runTestFunc(runTest, t, name, m, key)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeMapFromTestFunc(f interface{}) reflect.Value {
|
|
||||||
stringT := reflect.TypeOf("")
|
|
||||||
testingT := reflect.TypeOf((*testing.T)(nil))
|
|
||||||
ftyp := reflect.TypeOf(f)
|
|
||||||
if ftyp.Kind() != reflect.Func || ftyp.NumIn() != 3 || ftyp.NumOut() != 0 || ftyp.In(0) != testingT || ftyp.In(1) != stringT {
|
|
||||||
panic(fmt.Sprintf("bad test function type: want func(*testing.T, string, <TestType>), have %s", ftyp))
|
|
||||||
}
|
|
||||||
testType := ftyp.In(2)
|
|
||||||
mp := reflect.New(reflect.MapOf(stringT, testType))
|
|
||||||
return mp.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
func sortedMapKeys(m reflect.Value) []string {
|
|
||||||
keys := make([]string, m.Len())
|
|
||||||
for i, k := range m.MapKeys() {
|
|
||||||
keys[i] = k.String()
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
return keys
|
|
||||||
}
|
|
||||||
|
|
||||||
func runTestFunc(runTest interface{}, t *testing.T, name string, m reflect.Value, key string) {
|
|
||||||
reflect.ValueOf(runTest).Call([]reflect.Value{
|
|
||||||
reflect.ValueOf(t),
|
|
||||||
reflect.ValueOf(name),
|
|
||||||
m.MapIndex(reflect.ValueOf(key)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMatcherRunonlylist(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
tm := new(testMatcher)
|
|
||||||
tm.runonly("invalid*")
|
|
||||||
tm.walk(t, rlpTestDir, func(t *testing.T, name string, test *RLPTest) {
|
|
||||||
if name[:len("invalidRLPTest.json")] != "invalidRLPTest.json" {
|
|
||||||
t.Fatalf("invalid test found: %s != invalidRLPTest.json", name)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRLP(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
tm := new(testMatcher)
|
|
||||||
tm.walk(t, rlpTestDir, func(t *testing.T, name string, test *RLPTest) {
|
|
||||||
if err := tm.checkFailure(t, test.Run()); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,172 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RLPTest is the JSON structure of a single RLP test.
|
|
||||||
type RLPTest struct {
|
|
||||||
// If the value of In is "INVALID" or "VALID", the test
|
|
||||||
// checks whether Out can be decoded into a value of
|
|
||||||
// type interface{}.
|
|
||||||
//
|
|
||||||
// For other JSON values, In is treated as a driver for
|
|
||||||
// calls to rlp.Stream. The test also verifies that encoding
|
|
||||||
// In produces the bytes in Out.
|
|
||||||
In interface{}
|
|
||||||
|
|
||||||
// Out is a hex-encoded RLP value.
|
|
||||||
Out string
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromHex returns the bytes represented by the hexadecimal string s.
|
|
||||||
// s may be prefixed with "0x".
|
|
||||||
// This is copy-pasted from bytes.go, which does not return the error
|
|
||||||
func FromHex(s string) ([]byte, error) {
|
|
||||||
if len(s) > 1 && (s[0:2] == "0x" || s[0:2] == "0X") {
|
|
||||||
s = s[2:]
|
|
||||||
}
|
|
||||||
if len(s)%2 == 1 {
|
|
||||||
s = "0" + s
|
|
||||||
}
|
|
||||||
return hex.DecodeString(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run executes the test.
|
|
||||||
func (t *RLPTest) Run() error {
|
|
||||||
outb, err := FromHex(t.Out)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("invalid hex in Out")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle simple decoding tests with no actual In value.
|
|
||||||
if t.In == "VALID" || t.In == "INVALID" {
|
|
||||||
return checkDecodeInterface(outb, t.In == "VALID")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether encoding the value produces the same bytes.
|
|
||||||
in := translateJSON(t.In)
|
|
||||||
b, err := rlp.EncodeToBytes(in)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("encode failed: %v", err)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(b, outb) {
|
|
||||||
return fmt.Errorf("encode produced %x, want %x", b, outb)
|
|
||||||
}
|
|
||||||
// Test stream decoding.
|
|
||||||
s := rlp.NewStream(bytes.NewReader(outb), 0)
|
|
||||||
return checkDecodeFromJSON(s, in)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkDecodeInterface(b []byte, isValid bool) error {
|
|
||||||
err := rlp.DecodeBytes(b, new(interface{}))
|
|
||||||
switch {
|
|
||||||
case isValid && err != nil:
|
|
||||||
return fmt.Errorf("decoding failed: %v", err)
|
|
||||||
case !isValid && err == nil:
|
|
||||||
return errors.New("decoding of invalid value succeeded")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// translateJSON makes test json values encodable with RLP.
|
|
||||||
func translateJSON(v interface{}) interface{} {
|
|
||||||
switch v := v.(type) {
|
|
||||||
case float64:
|
|
||||||
return uint64(v)
|
|
||||||
case string:
|
|
||||||
if len(v) > 0 && v[0] == '#' { // # starts a faux big int.
|
|
||||||
big, ok := new(big.Int).SetString(v[1:], 10)
|
|
||||||
if !ok {
|
|
||||||
panic(fmt.Errorf("bad test: bad big int: %q", v))
|
|
||||||
}
|
|
||||||
return big
|
|
||||||
}
|
|
||||||
return []byte(v)
|
|
||||||
case []interface{}:
|
|
||||||
new := make([]interface{}, len(v))
|
|
||||||
for i := range v {
|
|
||||||
new[i] = translateJSON(v[i])
|
|
||||||
}
|
|
||||||
return new
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("can't handle %T", v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkDecodeFromJSON decodes from s guided by exp. exp drives the
|
|
||||||
// Stream by invoking decoding operations (Uint, Big, List, ...) based
|
|
||||||
// on the type of each value. The value decoded from the RLP stream
|
|
||||||
// must match the JSON value.
|
|
||||||
func checkDecodeFromJSON(s *rlp.Stream, exp interface{}) error {
|
|
||||||
switch exp := exp.(type) {
|
|
||||||
case uint64:
|
|
||||||
i, err := s.Uint64()
|
|
||||||
if err != nil {
|
|
||||||
return addStack("Uint", exp, err)
|
|
||||||
}
|
|
||||||
if i != exp {
|
|
||||||
return addStack("Uint", exp, fmt.Errorf("result mismatch: got %d", i))
|
|
||||||
}
|
|
||||||
case *big.Int:
|
|
||||||
big := new(big.Int)
|
|
||||||
if err := s.Decode(&big); err != nil {
|
|
||||||
return addStack("Big", exp, err)
|
|
||||||
}
|
|
||||||
if big.Cmp(exp) != 0 {
|
|
||||||
return addStack("Big", exp, fmt.Errorf("result mismatch: got %d", big))
|
|
||||||
}
|
|
||||||
case []byte:
|
|
||||||
b, err := s.Bytes()
|
|
||||||
if err != nil {
|
|
||||||
return addStack("Bytes", exp, err)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(b, exp) {
|
|
||||||
return addStack("Bytes", exp, fmt.Errorf("result mismatch: got %x", b))
|
|
||||||
}
|
|
||||||
case []interface{}:
|
|
||||||
if _, err := s.List(); err != nil {
|
|
||||||
return addStack("List", exp, err)
|
|
||||||
}
|
|
||||||
for i, v := range exp {
|
|
||||||
if err := checkDecodeFromJSON(s, v); err != nil {
|
|
||||||
return addStack(fmt.Sprintf("[%d]", i), exp, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := s.ListEnd(); err != nil {
|
|
||||||
return addStack("ListEnd", exp, err)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("unhandled type: %T", exp))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func addStack(op string, val interface{}, err error) error {
|
|
||||||
lines := strings.Split(err.Error(), "\n")
|
|
||||||
lines = append(lines, fmt.Sprintf("\t%s: %v", op, val))
|
|
||||||
return errors.New(strings.Join(lines, "\n"))
|
|
||||||
}
|
|
||||||
|
|
@ -1,311 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"math/rand"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestState(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
st := new(testMatcher)
|
|
||||||
// Long tests:
|
|
||||||
st.slow(`^stAttackTest/ContractCreationSpam`)
|
|
||||||
st.slow(`^stBadOpcode/badOpcodes`)
|
|
||||||
st.slow(`^stPreCompiledContracts/modexp`)
|
|
||||||
st.slow(`^stQuadraticComplexityTest/`)
|
|
||||||
st.slow(`^stStaticCall/static_Call50000`)
|
|
||||||
st.slow(`^stStaticCall/static_Return50000`)
|
|
||||||
st.slow(`^stSystemOperationsTest/CallRecursiveBomb`)
|
|
||||||
st.slow(`^stTransactionTest/Opcodes_TransactionInit`)
|
|
||||||
// Very time consuming
|
|
||||||
st.skipLoad(`^stTimeConsuming/`)
|
|
||||||
st.skipLoad(`.*vmPerformance/loop.*`)
|
|
||||||
// Uses 1GB RAM per tested fork
|
|
||||||
st.skipLoad(`^stStaticCall/static_Call1MB`)
|
|
||||||
|
|
||||||
// Broken tests:
|
|
||||||
// EOF is not part of cancun
|
|
||||||
st.skipLoad(`^stEOF/`)
|
|
||||||
|
|
||||||
// EIP-4844 tests need to be regenerated due to the data-to-blob rename
|
|
||||||
st.skipLoad(`^stEIP4844-blobtransactions/`)
|
|
||||||
|
|
||||||
// Expected failures:
|
|
||||||
// These EIP-4844 tests need to be regenerated.
|
|
||||||
st.fails(`stEIP4844-blobtransactions/opcodeBlobhashOutOfRange.json`, "test has incorrect state root")
|
|
||||||
st.fails(`stEIP4844-blobtransactions/opcodeBlobhBounds.json`, "test has incorrect state root")
|
|
||||||
|
|
||||||
// For Istanbul, older tests were moved into LegacyTests
|
|
||||||
for _, dir := range []string{
|
|
||||||
filepath.Join(baseDir, "EIPTests", "StateTests"),
|
|
||||||
stateTestDir,
|
|
||||||
legacyStateTestDir,
|
|
||||||
benchmarksDir,
|
|
||||||
} {
|
|
||||||
st.walk(t, dir, func(t *testing.T, name string, test *StateTest) {
|
|
||||||
if runtime.GOARCH == "386" && runtime.GOOS == "windows" && rand.Int63()%2 == 0 {
|
|
||||||
t.Skip("test (randomly) skipped on 32-bit windows")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, subtest := range test.Subtests() {
|
|
||||||
subtest := subtest
|
|
||||||
key := fmt.Sprintf("%s/%d", subtest.Fork, subtest.Index)
|
|
||||||
|
|
||||||
t.Run(key+"/hash/trie", func(t *testing.T) {
|
|
||||||
withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error {
|
|
||||||
var result error
|
|
||||||
test.Run(subtest, vmconfig, false, rawdb.HashScheme, func(err error, snaps *snapshot.Tree, state *state.StateDB) {
|
|
||||||
result = st.checkFailure(t, err)
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
})
|
|
||||||
t.Run(key+"/hash/snap", func(t *testing.T) {
|
|
||||||
withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error {
|
|
||||||
var result error
|
|
||||||
test.Run(subtest, vmconfig, true, rawdb.HashScheme, func(err error, snaps *snapshot.Tree, state *state.StateDB) {
|
|
||||||
if snaps != nil && state != nil {
|
|
||||||
if _, err := snaps.Journal(state.IntermediateRoot(false)); err != nil {
|
|
||||||
result = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = st.checkFailure(t, err)
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
})
|
|
||||||
t.Run(key+"/path/trie", func(t *testing.T) {
|
|
||||||
withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error {
|
|
||||||
var result error
|
|
||||||
test.Run(subtest, vmconfig, false, rawdb.PathScheme, func(err error, snaps *snapshot.Tree, state *state.StateDB) {
|
|
||||||
result = st.checkFailure(t, err)
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
})
|
|
||||||
t.Run(key+"/path/snap", func(t *testing.T) {
|
|
||||||
withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error {
|
|
||||||
var result error
|
|
||||||
test.Run(subtest, vmconfig, true, rawdb.PathScheme, func(err error, snaps *snapshot.Tree, state *state.StateDB) {
|
|
||||||
if snaps != nil && state != nil {
|
|
||||||
if _, err := snaps.Journal(state.IntermediateRoot(false)); err != nil {
|
|
||||||
result = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = st.checkFailure(t, err)
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transactions with gasLimit above this value will not get a VM trace on failure.
|
|
||||||
const traceErrorLimit = 400000
|
|
||||||
|
|
||||||
func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) {
|
|
||||||
// Use config from command line arguments.
|
|
||||||
config := vm.Config{}
|
|
||||||
err := test(config)
|
|
||||||
if err == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test failed, re-run with tracing enabled.
|
|
||||||
t.Error(err)
|
|
||||||
if gasLimit > traceErrorLimit {
|
|
||||||
t.Log("gas limit too high for EVM trace")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
w := bufio.NewWriter(buf)
|
|
||||||
config.Tracer = logger.NewJSONLogger(&logger.Config{}, w)
|
|
||||||
err2 := test(config)
|
|
||||||
if !reflect.DeepEqual(err, err2) {
|
|
||||||
t.Errorf("different error for second run: %v", err2)
|
|
||||||
}
|
|
||||||
w.Flush()
|
|
||||||
if buf.Len() == 0 {
|
|
||||||
t.Log("no EVM operation logs generated")
|
|
||||||
} else {
|
|
||||||
t.Log("EVM operation log:\n" + buf.String())
|
|
||||||
}
|
|
||||||
// t.Logf("EVM output: 0x%x", tracer.Output())
|
|
||||||
// t.Logf("EVM error: %v", tracer.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkEVM(b *testing.B) {
|
|
||||||
// Walk the directory.
|
|
||||||
dir := benchmarksDir
|
|
||||||
dirinfo, err := os.Stat(dir)
|
|
||||||
if os.IsNotExist(err) || !dirinfo.IsDir() {
|
|
||||||
fmt.Fprintf(os.Stderr, "can't find test files in %s, did you clone the evm-benchmarks submodule?\n", dir)
|
|
||||||
b.Skip("missing test files")
|
|
||||||
}
|
|
||||||
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if info.IsDir() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if ext := filepath.Ext(path); ext == ".json" {
|
|
||||||
name := filepath.ToSlash(strings.TrimPrefix(strings.TrimSuffix(path, ext), dir+string(filepath.Separator)))
|
|
||||||
b.Run(name, func(b *testing.B) { runBenchmarkFile(b, path) })
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runBenchmarkFile(b *testing.B, path string) {
|
|
||||||
m := make(map[string]StateTest)
|
|
||||||
if err := readJSONFile(path, &m); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(m) != 1 {
|
|
||||||
b.Fatal("expected single benchmark in a file")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, t := range m {
|
|
||||||
t := t
|
|
||||||
runBenchmark(b, &t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runBenchmark(b *testing.B, t *StateTest) {
|
|
||||||
for _, subtest := range t.Subtests() {
|
|
||||||
subtest := subtest
|
|
||||||
key := fmt.Sprintf("%s/%d", subtest.Fork, subtest.Index)
|
|
||||||
|
|
||||||
b.Run(key, func(b *testing.B) {
|
|
||||||
vmconfig := vm.Config{}
|
|
||||||
|
|
||||||
config, eips, err := GetChainConfig(subtest.Fork)
|
|
||||||
if err != nil {
|
|
||||||
b.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var rules = config.Rules(new(big.Int), false, 0)
|
|
||||||
|
|
||||||
vmconfig.ExtraEips = eips
|
|
||||||
block := t.genesis(config).ToBlock()
|
|
||||||
triedb, _, statedb := MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, false, rawdb.HashScheme)
|
|
||||||
defer triedb.Close()
|
|
||||||
|
|
||||||
var baseFee *big.Int
|
|
||||||
if rules.IsLondon {
|
|
||||||
baseFee = t.json.Env.BaseFee
|
|
||||||
if baseFee == nil {
|
|
||||||
// Retesteth uses `0x10` for genesis baseFee. Therefore, it defaults to
|
|
||||||
// parent - 2 : 0xa as the basefee for 'this' context.
|
|
||||||
baseFee = big.NewInt(0x0a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
post := t.json.Post[subtest.Fork][subtest.Index]
|
|
||||||
msg, err := t.json.Tx.toMessage(post, baseFee)
|
|
||||||
if err != nil {
|
|
||||||
b.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to recover tx with current signer
|
|
||||||
if len(post.TxBytes) != 0 {
|
|
||||||
var ttx types.Transaction
|
|
||||||
err := ttx.UnmarshalBinary(post.TxBytes)
|
|
||||||
if err != nil {
|
|
||||||
b.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := types.Sender(types.LatestSigner(config), &ttx); err != nil {
|
|
||||||
b.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare the EVM.
|
|
||||||
txContext := core.NewEVMTxContext(msg)
|
|
||||||
context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase)
|
|
||||||
context.GetHash = vmTestBlockHash
|
|
||||||
context.BaseFee = baseFee
|
|
||||||
evm := vm.NewEVM(context, txContext, statedb, config, vmconfig)
|
|
||||||
|
|
||||||
// Create "contract" for sender to cache code analysis.
|
|
||||||
sender := vm.NewContract(vm.AccountRef(msg.From), vm.AccountRef(msg.From),
|
|
||||||
nil, 0)
|
|
||||||
|
|
||||||
var (
|
|
||||||
gasUsed uint64
|
|
||||||
elapsed uint64
|
|
||||||
refund uint64
|
|
||||||
)
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
snapshot := statedb.Snapshot()
|
|
||||||
statedb.Prepare(rules, msg.From, context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
|
|
||||||
b.StartTimer()
|
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
// Execute the message.
|
|
||||||
_, leftOverGas, err := evm.Call(sender, *msg.To, msg.Data, msg.GasLimit, msg.Value)
|
|
||||||
if err != nil {
|
|
||||||
b.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
b.StopTimer()
|
|
||||||
elapsed += uint64(time.Since(start))
|
|
||||||
refund += statedb.GetRefund()
|
|
||||||
gasUsed += msg.GasLimit - leftOverGas
|
|
||||||
|
|
||||||
statedb.RevertToSnapshot(snapshot)
|
|
||||||
}
|
|
||||||
if elapsed < 1 {
|
|
||||||
elapsed = 1
|
|
||||||
}
|
|
||||||
// Keep it as uint64, multiply 100 to get two digit float later
|
|
||||||
mgasps := (100 * 1000 * (gasUsed - refund)) / elapsed
|
|
||||||
b.ReportMetric(float64(mgasps)/100, "mgas/s")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,468 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
|
||||||
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
|
||||||
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
|
|
||||||
"golang.org/x/crypto/sha3"
|
|
||||||
)
|
|
||||||
|
|
||||||
// StateTest checks transaction processing without block context.
|
|
||||||
// See https://github.com/ethereum/EIPs/issues/176 for the test format specification.
|
|
||||||
type StateTest struct {
|
|
||||||
json stJSON
|
|
||||||
}
|
|
||||||
|
|
||||||
// StateSubtest selects a specific configuration of a General State Test.
|
|
||||||
type StateSubtest struct {
|
|
||||||
Fork string
|
|
||||||
Index int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *StateTest) UnmarshalJSON(in []byte) error {
|
|
||||||
return json.Unmarshal(in, &t.json)
|
|
||||||
}
|
|
||||||
|
|
||||||
type stJSON struct {
|
|
||||||
Env stEnv `json:"env"`
|
|
||||||
Pre core.GenesisAlloc `json:"pre"`
|
|
||||||
Tx stTransaction `json:"transaction"`
|
|
||||||
Out hexutil.Bytes `json:"out"`
|
|
||||||
Post map[string][]stPostState `json:"post"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type stPostState struct {
|
|
||||||
Root common.UnprefixedHash `json:"hash"`
|
|
||||||
Logs common.UnprefixedHash `json:"logs"`
|
|
||||||
TxBytes hexutil.Bytes `json:"txbytes"`
|
|
||||||
ExpectException string `json:"expectException"`
|
|
||||||
Indexes struct {
|
|
||||||
Data int `json:"data"`
|
|
||||||
Gas int `json:"gas"`
|
|
||||||
Value int `json:"value"`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
|
|
||||||
|
|
||||||
type stEnv struct {
|
|
||||||
Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
|
|
||||||
Difficulty *big.Int `json:"currentDifficulty" gencodec:"optional"`
|
|
||||||
Random *big.Int `json:"currentRandom" gencodec:"optional"`
|
|
||||||
GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
|
|
||||||
Number uint64 `json:"currentNumber" gencodec:"required"`
|
|
||||||
Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
|
|
||||||
BaseFee *big.Int `json:"currentBaseFee" gencodec:"optional"`
|
|
||||||
ExcessBlobGas *uint64 `json:"currentExcessBlobGas" gencodec:"optional"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type stEnvMarshaling struct {
|
|
||||||
Coinbase common.UnprefixedAddress
|
|
||||||
Difficulty *math.HexOrDecimal256
|
|
||||||
Random *math.HexOrDecimal256
|
|
||||||
GasLimit math.HexOrDecimal64
|
|
||||||
Number math.HexOrDecimal64
|
|
||||||
Timestamp math.HexOrDecimal64
|
|
||||||
BaseFee *math.HexOrDecimal256
|
|
||||||
ExcessBlobGas *math.HexOrDecimal64
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type stTransaction -field-override stTransactionMarshaling -out gen_sttransaction.go
|
|
||||||
|
|
||||||
type stTransaction struct {
|
|
||||||
GasPrice *big.Int `json:"gasPrice"`
|
|
||||||
MaxFeePerGas *big.Int `json:"maxFeePerGas"`
|
|
||||||
MaxPriorityFeePerGas *big.Int `json:"maxPriorityFeePerGas"`
|
|
||||||
Nonce uint64 `json:"nonce"`
|
|
||||||
To string `json:"to"`
|
|
||||||
Data []string `json:"data"`
|
|
||||||
AccessLists []*types.AccessList `json:"accessLists,omitempty"`
|
|
||||||
GasLimit []uint64 `json:"gasLimit"`
|
|
||||||
Value []string `json:"value"`
|
|
||||||
PrivateKey []byte `json:"secretKey"`
|
|
||||||
Sender *common.Address `json:"sender"`
|
|
||||||
BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
|
|
||||||
BlobGasFeeCap *big.Int `json:"maxFeePerBlobGas,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type stTransactionMarshaling struct {
|
|
||||||
GasPrice *math.HexOrDecimal256
|
|
||||||
MaxFeePerGas *math.HexOrDecimal256
|
|
||||||
MaxPriorityFeePerGas *math.HexOrDecimal256
|
|
||||||
Nonce math.HexOrDecimal64
|
|
||||||
GasLimit []math.HexOrDecimal64
|
|
||||||
PrivateKey hexutil.Bytes
|
|
||||||
BlobGasFeeCap *math.HexOrDecimal256
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetChainConfig takes a fork definition and returns a chain config.
|
|
||||||
// The fork definition can be
|
|
||||||
// - a plain forkname, e.g. `Byzantium`,
|
|
||||||
// - a fork basename, and a list of EIPs to enable; e.g. `Byzantium+1884+1283`.
|
|
||||||
func GetChainConfig(forkString string) (baseConfig *params.ChainConfig, eips []int, err error) {
|
|
||||||
var (
|
|
||||||
splitForks = strings.Split(forkString, "+")
|
|
||||||
ok bool
|
|
||||||
baseName, eipsStrings = splitForks[0], splitForks[1:]
|
|
||||||
)
|
|
||||||
if baseConfig, ok = Forks[baseName]; !ok {
|
|
||||||
return nil, nil, UnsupportedForkError{baseName}
|
|
||||||
}
|
|
||||||
for _, eip := range eipsStrings {
|
|
||||||
if eipNum, err := strconv.Atoi(eip); err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("syntax error, invalid eip number %v", eipNum)
|
|
||||||
} else {
|
|
||||||
if !vm.ValidEip(eipNum) {
|
|
||||||
return nil, nil, fmt.Errorf("syntax error, invalid eip number %v", eipNum)
|
|
||||||
}
|
|
||||||
eips = append(eips, eipNum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return baseConfig, eips, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subtests returns all valid subtests of the test.
|
|
||||||
func (t *StateTest) Subtests() []StateSubtest {
|
|
||||||
var sub []StateSubtest
|
|
||||||
for fork, pss := range t.json.Post {
|
|
||||||
for i := range pss {
|
|
||||||
sub = append(sub, StateSubtest{fork, i})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sub
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkError checks if the error returned by the state transition matches any expected error.
|
|
||||||
// A failing expectation returns a wrapped version of the original error, if any,
|
|
||||||
// or a new error detailing the failing expectation.
|
|
||||||
// This function does not return or modify the original error, it only evaluates and returns expectations for the error.
|
|
||||||
func (t *StateTest) checkError(subtest StateSubtest, err error) error {
|
|
||||||
expectedError := t.json.Post[subtest.Fork][subtest.Index].ExpectException
|
|
||||||
if err == nil && expectedError == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err == nil && expectedError != "" {
|
|
||||||
return fmt.Errorf("expected error %q, got no error", expectedError)
|
|
||||||
}
|
|
||||||
if err != nil && expectedError == "" {
|
|
||||||
return fmt.Errorf("unexpected error: %w", err)
|
|
||||||
}
|
|
||||||
if err != nil && expectedError != "" {
|
|
||||||
// Ignore expected errors (TODO MariusVanDerWijden check error string)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run executes a specific subtest and verifies the post-state and logs
|
|
||||||
func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string, postCheck func(err error, snaps *snapshot.Tree, state *state.StateDB)) (result error) {
|
|
||||||
triedb, snaps, statedb, root, err := t.RunNoVerify(subtest, vmconfig, snapshotter, scheme)
|
|
||||||
|
|
||||||
// Invoke the callback at the end of function for further analysis.
|
|
||||||
defer func() {
|
|
||||||
postCheck(result, snaps, statedb)
|
|
||||||
|
|
||||||
if triedb != nil {
|
|
||||||
triedb.Close()
|
|
||||||
}
|
|
||||||
if snaps != nil {
|
|
||||||
snaps.Release()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
checkedErr := t.checkError(subtest, err)
|
|
||||||
if checkedErr != nil {
|
|
||||||
return checkedErr
|
|
||||||
}
|
|
||||||
// The error has been checked; if it was unexpected, it's already returned.
|
|
||||||
if err != nil {
|
|
||||||
// Here, an error exists but it was expected.
|
|
||||||
// We do not check the post state or logs.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
post := t.json.Post[subtest.Fork][subtest.Index]
|
|
||||||
// N.B: We need to do this in a two-step process, because the first Commit takes care
|
|
||||||
// of self-destructs, and we need to touch the coinbase _after_ it has potentially self-destructed.
|
|
||||||
if root != common.Hash(post.Root) {
|
|
||||||
return fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root)
|
|
||||||
}
|
|
||||||
if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) {
|
|
||||||
return fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, post.Logs)
|
|
||||||
}
|
|
||||||
statedb, _ = state.New(root, statedb.Database(), snaps)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunNoVerify runs a specific subtest and returns the statedb and post-state root
|
|
||||||
func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string) (*trie.Database, *snapshot.Tree, *state.StateDB, common.Hash, error) {
|
|
||||||
config, eips, err := GetChainConfig(subtest.Fork)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, nil, common.Hash{}, UnsupportedForkError{subtest.Fork}
|
|
||||||
}
|
|
||||||
vmconfig.ExtraEips = eips
|
|
||||||
|
|
||||||
block := t.genesis(config).ToBlock()
|
|
||||||
triedb, snaps, statedb := MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, snapshotter, scheme)
|
|
||||||
|
|
||||||
var baseFee *big.Int
|
|
||||||
if config.IsLondon(new(big.Int)) {
|
|
||||||
baseFee = t.json.Env.BaseFee
|
|
||||||
if baseFee == nil {
|
|
||||||
// Retesteth uses `0x10` for genesis baseFee. Therefore, it defaults to
|
|
||||||
// parent - 2 : 0xa as the basefee for 'this' context.
|
|
||||||
baseFee = big.NewInt(0x0a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
post := t.json.Post[subtest.Fork][subtest.Index]
|
|
||||||
msg, err := t.json.Tx.toMessage(post, baseFee)
|
|
||||||
if err != nil {
|
|
||||||
triedb.Close()
|
|
||||||
return nil, nil, nil, common.Hash{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to recover tx with current signer
|
|
||||||
if len(post.TxBytes) != 0 {
|
|
||||||
var ttx types.Transaction
|
|
||||||
err := ttx.UnmarshalBinary(post.TxBytes)
|
|
||||||
if err != nil {
|
|
||||||
triedb.Close()
|
|
||||||
return nil, nil, nil, common.Hash{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := types.Sender(types.LatestSigner(config), &ttx); err != nil {
|
|
||||||
triedb.Close()
|
|
||||||
return nil, nil, nil, common.Hash{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare the EVM.
|
|
||||||
txContext := core.NewEVMTxContext(msg)
|
|
||||||
context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase)
|
|
||||||
context.GetHash = vmTestBlockHash
|
|
||||||
context.BaseFee = baseFee
|
|
||||||
context.Random = nil
|
|
||||||
if t.json.Env.Difficulty != nil {
|
|
||||||
context.Difficulty = new(big.Int).Set(t.json.Env.Difficulty)
|
|
||||||
}
|
|
||||||
if config.IsLondon(new(big.Int)) && t.json.Env.Random != nil {
|
|
||||||
rnd := common.BigToHash(t.json.Env.Random)
|
|
||||||
context.Random = &rnd
|
|
||||||
context.Difficulty = big.NewInt(0)
|
|
||||||
}
|
|
||||||
if config.IsCancun(new(big.Int), block.Time()) && t.json.Env.ExcessBlobGas != nil {
|
|
||||||
context.BlobBaseFee = eip4844.CalcBlobFee(*t.json.Env.ExcessBlobGas)
|
|
||||||
}
|
|
||||||
evm := vm.NewEVM(context, txContext, statedb, config, vmconfig)
|
|
||||||
|
|
||||||
// Execute the message.
|
|
||||||
snapshot := statedb.Snapshot()
|
|
||||||
gaspool := new(core.GasPool)
|
|
||||||
gaspool.AddGas(block.GasLimit())
|
|
||||||
_, err = core.ApplyMessage(evm, msg, gaspool)
|
|
||||||
if err != nil {
|
|
||||||
statedb.RevertToSnapshot(snapshot)
|
|
||||||
}
|
|
||||||
// Add 0-value mining reward. This only makes a difference in the cases
|
|
||||||
// where
|
|
||||||
// - the coinbase self-destructed, or
|
|
||||||
// - there are only 'bad' transactions, which aren't executed. In those cases,
|
|
||||||
// the coinbase gets no txfee, so isn't created, and thus needs to be touched
|
|
||||||
statedb.AddBalance(block.Coinbase(), new(big.Int))
|
|
||||||
|
|
||||||
// Commit state mutations into database.
|
|
||||||
root, _ := statedb.Commit(block.NumberU64(), config.IsEIP158(block.Number()))
|
|
||||||
return triedb, snaps, statedb, root, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *StateTest) gasLimit(subtest StateSubtest) uint64 {
|
|
||||||
return t.json.Tx.GasLimit[t.json.Post[subtest.Fork][subtest.Index].Indexes.Gas]
|
|
||||||
}
|
|
||||||
|
|
||||||
func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter bool, scheme string) (*trie.Database, *snapshot.Tree, *state.StateDB) {
|
|
||||||
tconf := &trie.Config{Preimages: true}
|
|
||||||
if scheme == rawdb.HashScheme {
|
|
||||||
tconf.HashDB = hashdb.Defaults
|
|
||||||
} else {
|
|
||||||
tconf.PathDB = pathdb.Defaults
|
|
||||||
}
|
|
||||||
triedb := trie.NewDatabase(db, tconf)
|
|
||||||
sdb := state.NewDatabaseWithNodeDB(db, triedb)
|
|
||||||
statedb, _ := state.New(types.EmptyRootHash, sdb, nil)
|
|
||||||
for addr, a := range accounts {
|
|
||||||
statedb.SetCode(addr, a.Code)
|
|
||||||
statedb.SetNonce(addr, a.Nonce)
|
|
||||||
statedb.SetBalance(addr, a.Balance)
|
|
||||||
for k, v := range a.Storage {
|
|
||||||
statedb.SetState(addr, k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Commit and re-open to start with a clean state.
|
|
||||||
root, _ := statedb.Commit(0, false)
|
|
||||||
|
|
||||||
var snaps *snapshot.Tree
|
|
||||||
if snapshotter {
|
|
||||||
snapconfig := snapshot.Config{
|
|
||||||
CacheSize: 1,
|
|
||||||
Recovery: false,
|
|
||||||
NoBuild: false,
|
|
||||||
AsyncBuild: false,
|
|
||||||
}
|
|
||||||
snaps, _ = snapshot.New(snapconfig, db, triedb, root)
|
|
||||||
}
|
|
||||||
statedb, _ = state.New(root, sdb, snaps)
|
|
||||||
return triedb, snaps, statedb
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis {
|
|
||||||
genesis := &core.Genesis{
|
|
||||||
Config: config,
|
|
||||||
Coinbase: t.json.Env.Coinbase,
|
|
||||||
Difficulty: t.json.Env.Difficulty,
|
|
||||||
GasLimit: t.json.Env.GasLimit,
|
|
||||||
Number: t.json.Env.Number,
|
|
||||||
Timestamp: t.json.Env.Timestamp,
|
|
||||||
Alloc: t.json.Pre,
|
|
||||||
}
|
|
||||||
if t.json.Env.Random != nil {
|
|
||||||
// Post-Merge
|
|
||||||
genesis.Mixhash = common.BigToHash(t.json.Env.Random)
|
|
||||||
genesis.Difficulty = big.NewInt(0)
|
|
||||||
}
|
|
||||||
return genesis
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Message, error) {
|
|
||||||
var from common.Address
|
|
||||||
// If 'sender' field is present, use that
|
|
||||||
if tx.Sender != nil {
|
|
||||||
from = *tx.Sender
|
|
||||||
} else if len(tx.PrivateKey) > 0 {
|
|
||||||
// Derive sender from private key if needed.
|
|
||||||
key, err := crypto.ToECDSA(tx.PrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid private key: %v", err)
|
|
||||||
}
|
|
||||||
from = crypto.PubkeyToAddress(key.PublicKey)
|
|
||||||
}
|
|
||||||
// Parse recipient if present.
|
|
||||||
var to *common.Address
|
|
||||||
if tx.To != "" {
|
|
||||||
to = new(common.Address)
|
|
||||||
if err := to.UnmarshalText([]byte(tx.To)); err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid to address: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get values specific to this post state.
|
|
||||||
if ps.Indexes.Data > len(tx.Data) {
|
|
||||||
return nil, fmt.Errorf("tx data index %d out of bounds", ps.Indexes.Data)
|
|
||||||
}
|
|
||||||
if ps.Indexes.Value > len(tx.Value) {
|
|
||||||
return nil, fmt.Errorf("tx value index %d out of bounds", ps.Indexes.Value)
|
|
||||||
}
|
|
||||||
if ps.Indexes.Gas > len(tx.GasLimit) {
|
|
||||||
return nil, fmt.Errorf("tx gas limit index %d out of bounds", ps.Indexes.Gas)
|
|
||||||
}
|
|
||||||
dataHex := tx.Data[ps.Indexes.Data]
|
|
||||||
valueHex := tx.Value[ps.Indexes.Value]
|
|
||||||
gasLimit := tx.GasLimit[ps.Indexes.Gas]
|
|
||||||
// Value, Data hex encoding is messy: https://github.com/ethereum/tests/issues/203
|
|
||||||
value := new(big.Int)
|
|
||||||
if valueHex != "0x" {
|
|
||||||
v, ok := math.ParseBig256(valueHex)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("invalid tx value %q", valueHex)
|
|
||||||
}
|
|
||||||
value = v
|
|
||||||
}
|
|
||||||
data, err := hex.DecodeString(strings.TrimPrefix(dataHex, "0x"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid tx data %q", dataHex)
|
|
||||||
}
|
|
||||||
var accessList types.AccessList
|
|
||||||
if tx.AccessLists != nil && tx.AccessLists[ps.Indexes.Data] != nil {
|
|
||||||
accessList = *tx.AccessLists[ps.Indexes.Data]
|
|
||||||
}
|
|
||||||
// If baseFee provided, set gasPrice to effectiveGasPrice.
|
|
||||||
gasPrice := tx.GasPrice
|
|
||||||
if baseFee != nil {
|
|
||||||
if tx.MaxFeePerGas == nil {
|
|
||||||
tx.MaxFeePerGas = gasPrice
|
|
||||||
}
|
|
||||||
if tx.MaxFeePerGas == nil {
|
|
||||||
tx.MaxFeePerGas = new(big.Int)
|
|
||||||
}
|
|
||||||
if tx.MaxPriorityFeePerGas == nil {
|
|
||||||
tx.MaxPriorityFeePerGas = tx.MaxFeePerGas
|
|
||||||
}
|
|
||||||
gasPrice = math.BigMin(new(big.Int).Add(tx.MaxPriorityFeePerGas, baseFee),
|
|
||||||
tx.MaxFeePerGas)
|
|
||||||
}
|
|
||||||
if gasPrice == nil {
|
|
||||||
return nil, errors.New("no gas price provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := &core.Message{
|
|
||||||
From: from,
|
|
||||||
To: to,
|
|
||||||
Nonce: tx.Nonce,
|
|
||||||
Value: value,
|
|
||||||
GasLimit: gasLimit,
|
|
||||||
GasPrice: gasPrice,
|
|
||||||
GasFeeCap: tx.MaxFeePerGas,
|
|
||||||
GasTipCap: tx.MaxPriorityFeePerGas,
|
|
||||||
Data: data,
|
|
||||||
AccessList: accessList,
|
|
||||||
BlobHashes: tx.BlobVersionedHashes,
|
|
||||||
BlobGasFeeCap: tx.BlobGasFeeCap,
|
|
||||||
}
|
|
||||||
return msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func rlpHash(x interface{}) (h common.Hash) {
|
|
||||||
hw := sha3.NewLegacyKeccak256()
|
|
||||||
rlp.Encode(hw, x)
|
|
||||||
hw.Sum(h[:0])
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func vmTestBlockHash(n uint64) common.Hash {
|
|
||||||
return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit ee3fa4c86d05f99f2717f83a6ad08008490ddf07
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTransaction(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
txt := new(testMatcher)
|
|
||||||
// These can't be parsed, invalid hex in RLP
|
|
||||||
txt.skipLoad("^ttWrongRLP/.*")
|
|
||||||
// We don't allow more than uint64 in gas amount
|
|
||||||
// This is a pseudo-consensus vulnerability, but not in practice
|
|
||||||
// because of the gas limit
|
|
||||||
txt.skipLoad("^ttGasLimit/TransactionWithGasLimitxPriceOverflow.json")
|
|
||||||
// We _do_ allow more than uint64 in gas price, as opposed to the tests
|
|
||||||
// This is also not a concern, as long as tx.Cost() uses big.Int for
|
|
||||||
// calculating the final cozt
|
|
||||||
txt.skipLoad(".*TransactionWithGasPriceOverflow.*")
|
|
||||||
|
|
||||||
// The nonce is too large for uint64. Not a concern, it means geth won't
|
|
||||||
// accept transactions at a certain point in the distant future
|
|
||||||
txt.skipLoad("^ttNonce/TransactionWithHighNonce256.json")
|
|
||||||
|
|
||||||
// The value is larger than uint64, which according to the test is invalid.
|
|
||||||
// Geth accepts it, which is not a consensus issue since we use big.Int's
|
|
||||||
// internally to calculate the cost
|
|
||||||
txt.skipLoad("^ttValue/TransactionWithHighValueOverflow.json")
|
|
||||||
txt.walk(t, transactionTestDir, func(t *testing.T, name string, test *TransactionTest) {
|
|
||||||
cfg := params.MainnetChainConfig
|
|
||||||
if err := txt.checkFailure(t, test.Run(cfg)); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,110 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TransactionTest checks RLP decoding and sender derivation of transactions.
|
|
||||||
type TransactionTest struct {
|
|
||||||
RLP hexutil.Bytes `json:"rlp"`
|
|
||||||
Byzantium ttFork
|
|
||||||
Constantinople ttFork
|
|
||||||
Istanbul ttFork
|
|
||||||
EIP150 ttFork
|
|
||||||
EIP158 ttFork
|
|
||||||
Frontier ttFork
|
|
||||||
Homestead ttFork
|
|
||||||
}
|
|
||||||
|
|
||||||
type ttFork struct {
|
|
||||||
Sender common.UnprefixedAddress `json:"sender"`
|
|
||||||
Hash common.UnprefixedHash `json:"hash"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tt *TransactionTest) Run(config *params.ChainConfig) error {
|
|
||||||
validateTx := func(rlpData hexutil.Bytes, signer types.Signer, isHomestead bool, isIstanbul bool) (*common.Address, *common.Hash, error) {
|
|
||||||
tx := new(types.Transaction)
|
|
||||||
if err := rlp.DecodeBytes(rlpData, tx); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
sender, err := types.Sender(signer, tx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
// Intrinsic gas
|
|
||||||
requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, isHomestead, isIstanbul, false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if requiredGas > tx.Gas() {
|
|
||||||
return nil, nil, fmt.Errorf("insufficient gas ( %d < %d )", tx.Gas(), requiredGas)
|
|
||||||
}
|
|
||||||
h := tx.Hash()
|
|
||||||
return &sender, &h, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, testcase := range []struct {
|
|
||||||
name string
|
|
||||||
signer types.Signer
|
|
||||||
fork ttFork
|
|
||||||
isHomestead bool
|
|
||||||
isIstanbul bool
|
|
||||||
}{
|
|
||||||
{"Frontier", types.FrontierSigner{}, tt.Frontier, false, false},
|
|
||||||
{"Homestead", types.HomesteadSigner{}, tt.Homestead, true, false},
|
|
||||||
{"EIP150", types.HomesteadSigner{}, tt.EIP150, true, false},
|
|
||||||
{"EIP158", types.NewEIP155Signer(config.ChainID), tt.EIP158, true, false},
|
|
||||||
{"Byzantium", types.NewEIP155Signer(config.ChainID), tt.Byzantium, true, false},
|
|
||||||
{"Constantinople", types.NewEIP155Signer(config.ChainID), tt.Constantinople, true, false},
|
|
||||||
{"Istanbul", types.NewEIP155Signer(config.ChainID), tt.Istanbul, true, true},
|
|
||||||
} {
|
|
||||||
sender, txhash, err := validateTx(tt.RLP, testcase.signer, testcase.isHomestead, testcase.isIstanbul)
|
|
||||||
|
|
||||||
if testcase.fork.Sender == (common.UnprefixedAddress{}) {
|
|
||||||
if err == nil {
|
|
||||||
return fmt.Errorf("expected error, got none (address %v)[%v]", sender.String(), testcase.name)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Should resolve the right address
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("got error, expected none: %v", err)
|
|
||||||
}
|
|
||||||
if sender == nil {
|
|
||||||
return fmt.Errorf("sender was nil, should be %x", common.Address(testcase.fork.Sender))
|
|
||||||
}
|
|
||||||
if *sender != common.Address(testcase.fork.Sender) {
|
|
||||||
return fmt.Errorf("sender mismatch: got %x, want %x", sender, testcase.fork.Sender)
|
|
||||||
}
|
|
||||||
if txhash == nil {
|
|
||||||
return fmt.Errorf("txhash was nil, should be %x", common.Hash(testcase.fork.Hash))
|
|
||||||
}
|
|
||||||
if *txhash != common.Hash(testcase.fork.Hash) {
|
|
||||||
return fmt.Errorf("hash mismatch: got %x, want %x", *txhash, testcase.fork.Hash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue