From 9af60fa075e79655ec2f70da2c69f7a4749b0b43 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 29 Jun 2017 11:10:34 +0200 Subject: [PATCH] tests: update tests and add support for general state tests Tests are now included as a submodule. This should make updating easier and removes ~60MB of JSON data from the working copy. State tests are replaced by General State Tests, which run the same test with multiple fork configurations. With the new test runner, consensus tests are run as subtests by walking json files. Many hex issues have been fixed upstream since the last update and most custom parsing code is replaced by existing JSON hex types. Tests can now be marked as 'expected failures', ensuring that fixes for those tests will trigger an update to test configuration. The new test runner also supports parallel execution and the -short flag. --- .gitmodules | 3 + accounts/keystore/keystore_plain_test.go | 8 +- build/update-license.go | 2 +- consensus/ethash/consensus_test.go | 2 +- tests/block_test.go | 274 ++---- tests/block_test_util.go | 526 +++-------- tests/gen_btheader.go | 128 +++ tests/gen_stenv.go | 66 ++ tests/gen_stlog.go | 61 ++ tests/gen_sttransaction.go | 80 ++ tests/gen_tttransaction.go | 95 ++ tests/gen_vmexec.go | 88 ++ tests/init.go | 119 --- tests/init_test.go | 263 ++++++ tests/rlp_test.go | 19 +- tests/rlp_test_util.go | 29 - tests/state_test.go | 1014 ++-------------------- tests/state_test_util.go | 422 +++++---- tests/testdata | 1 + tests/transaction_test.go | 116 +-- tests/transaction_test_util.go | 254 ++---- tests/util.go | 214 ----- tests/vm_test.go | 127 +-- tests/vm_test_util.go | 278 ++---- 24 files changed, 1510 insertions(+), 2679 deletions(-) create mode 100644 .gitmodules create mode 100644 tests/gen_btheader.go create mode 100644 tests/gen_stenv.go create mode 100644 tests/gen_stlog.go create mode 100644 tests/gen_sttransaction.go create mode 100644 tests/gen_tttransaction.go create mode 100644 tests/gen_vmexec.go delete mode 100644 tests/init.go create mode 100644 tests/init_test.go create mode 160000 tests/testdata delete mode 100644 tests/util.go diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..32bdb3b6e5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tests"] + path = tests/testdata + url = https://github.com/ethereum/tests diff --git a/accounts/keystore/keystore_plain_test.go b/accounts/keystore/keystore_plain_test.go index 8c0eb52ea1..71a45cb01f 100644 --- a/accounts/keystore/keystore_plain_test.go +++ b/accounts/keystore/keystore_plain_test.go @@ -142,19 +142,19 @@ func TestV3_PBKDF2_1(t *testing.T) { func TestV3_PBKDF2_2(t *testing.T) { t.Parallel() - tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t) + tests := loadKeyStoreTestV3("../../tests/testdata/KeyStoreTests/basic_tests.json", t) testDecryptV3(tests["test1"], t) } func TestV3_PBKDF2_3(t *testing.T) { t.Parallel() - tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t) + tests := loadKeyStoreTestV3("../../tests/testdata/KeyStoreTests/basic_tests.json", t) testDecryptV3(tests["python_generated_test_with_odd_iv"], t) } func TestV3_PBKDF2_4(t *testing.T) { t.Parallel() - tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t) + tests := loadKeyStoreTestV3("../../tests/testdata/KeyStoreTests/basic_tests.json", t) testDecryptV3(tests["evilnonce"], t) } @@ -166,7 +166,7 @@ func TestV3_Scrypt_1(t *testing.T) { func TestV3_Scrypt_2(t *testing.T) { t.Parallel() - tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t) + tests := loadKeyStoreTestV3("../../tests/testdata/KeyStoreTests/basic_tests.json", t) testDecryptV3(tests["test2"], t) } diff --git a/build/update-license.go b/build/update-license.go index 948eabab6f..3d69598b75 100644 --- a/build/update-license.go +++ b/build/update-license.go @@ -45,7 +45,7 @@ var ( // paths with any of these prefixes will be skipped skipPrefixes = []string{ // boring stuff - "vendor/", "tests/files/", "build/", + "vendor/", "tests/testdata/", "build/", // don't relicense vendored sources "cmd/internal/browser", "consensus/ethash/xor.go", diff --git a/consensus/ethash/consensus_test.go b/consensus/ethash/consensus_test.go index 0a375b0bcb..d8f6b2b407 100644 --- a/consensus/ethash/consensus_test.go +++ b/consensus/ethash/consensus_test.go @@ -57,7 +57,7 @@ func (d *diffTest) UnmarshalJSON(b []byte) (err error) { } func TestCalcDifficulty(t *testing.T) { - file, err := os.Open("../../tests/files/BasicTests/difficulty.json") + file, err := os.Open("../../tests/testdata/BasicTests/difficulty.json") if err != nil { t.Fatal(err) } diff --git a/tests/block_test.go b/tests/block_test.go index d341610175..3245aca7db 100644 --- a/tests/block_test.go +++ b/tests/block_test.go @@ -18,230 +18,64 @@ package tests import ( "math/big" - "path/filepath" "testing" + + "github.com/ethereum/go-ethereum/params" ) -func TestBcValidBlockTests(t *testing.T) { - err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcValidBlockTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} +func TestBlockchain(t *testing.T) { + t.Parallel() -func TestBcUncleHeaderValidityTests(t *testing.T) { - err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcUncleHeaderValiditiy.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} + bt := new(testMatcher) + // General state tests are 'exported' as blockchain tests, but we can run them natively. + bt.skipLoad(`^GeneralStateTests/`) + // Skip random failures due to selfish mining test. + bt.skipLoad(`bcForkUncle\.json/ForkUncle`) + bt.skipLoad(`^bcMultiChainTest\.json/ChainAtoChainB_blockorder`) + bt.skipLoad(`^bcTotalDifficultyTest\.json/(lotsOfLeafs|lotsOfBranches|sideChainWithMoreTransactions)$`) + bt.skipLoad(`^bcMultiChainTest\.json/CallContractFromNotBestBlock`) + // Expected failures: + bt.fails(`(?i)metropolis`, "metropolis is not supported yet") + bt.fails(`^TestNetwork/bcTheDaoTest\.json/(DaoTransactions$|DaoTransactions_UncleExtradata$)`, "issue in test") -func TestBcUncleTests(t *testing.T) { - err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcUncleTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} + bt.config(`^TestNetwork/`, params.ChainConfig{ + HomesteadBlock: big.NewInt(5), + DAOForkBlock: big.NewInt(8), + DAOForkSupport: true, + EIP150Block: big.NewInt(10), + EIP155Block: big.NewInt(10), + EIP158Block: big.NewInt(14), + // MetropolisBlock: big.NewInt(16), + }) + bt.config(`^RandomTests/.*EIP150`, params.ChainConfig{ + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + }) + bt.config(`^RandomTests/.*EIP158`, params.ChainConfig{ + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + }) + bt.config(`^RandomTests/`, params.ChainConfig{ + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(10), + }) + bt.config(`^Homestead/`, params.ChainConfig{ + HomesteadBlock: big.NewInt(0), + }) + bt.config(`^EIP150/`, params.ChainConfig{ + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + }) + bt.config(`^[^/]+\.json`, params.ChainConfig{ + HomesteadBlock: big.NewInt(1000000), + }) -func TestBcForkUncleTests(t *testing.T) { - err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcForkUncle.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestBcInvalidHeaderTests(t *testing.T) { - err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcInvalidHeaderTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestBcInvalidRLPTests(t *testing.T) { - err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcInvalidRLPTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestBcRPCAPITests(t *testing.T) { - err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcRPC_API_Test.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestBcForkBlockTests(t *testing.T) { - err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcForkBlockTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestBcForkStress(t *testing.T) { - err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcForkStressTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestBcTotalDifficulty(t *testing.T) { - // skip because these will fail due to selfish mining fix - t.Skip() - - err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcTotalDifficultyTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestBcWallet(t *testing.T) { - err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcWalletTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestBcGasPricer(t *testing.T) { - err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcGasPricerTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -// TODO: iterate over files once we got more than a few -func TestBcRandom(t *testing.T) { - err := RunBlockTest(big.NewInt(1000000), nil, big.NewInt(10), filepath.Join(blockTestDir, "RandomTests/bl201507071825GO.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestBcMultiChain(t *testing.T) { - // skip due to selfish mining - t.Skip() - - err := RunBlockTest(big.NewInt(1000000), nil, big.NewInt(10), filepath.Join(blockTestDir, "bcMultiChainTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestBcState(t *testing.T) { - err := RunBlockTest(big.NewInt(1000000), nil, big.NewInt(10), filepath.Join(blockTestDir, "bcStateTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -// Homestead tests -func TestHomesteadBcValidBlockTests(t *testing.T) { - err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcValidBlockTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestHomesteadBcUncleHeaderValidityTests(t *testing.T) { - err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcUncleHeaderValiditiy.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestHomesteadBcUncleTests(t *testing.T) { - err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcUncleTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestHomesteadBcInvalidHeaderTests(t *testing.T) { - err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcInvalidHeaderTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestHomesteadBcRPCAPITests(t *testing.T) { - err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcRPC_API_Test.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestHomesteadBcForkStress(t *testing.T) { - err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcForkStressTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestHomesteadBcTotalDifficulty(t *testing.T) { - err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcTotalDifficultyTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestHomesteadBcWallet(t *testing.T) { - err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcWalletTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestHomesteadBcGasPricer(t *testing.T) { - err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcGasPricerTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestHomesteadBcMultiChain(t *testing.T) { - err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcMultiChainTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestHomesteadBcState(t *testing.T) { - err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcStateTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -// DAO hard-fork tests -func TestDAOBcTheDao(t *testing.T) { - err := RunBlockTest(big.NewInt(5), big.NewInt(8), nil, filepath.Join(blockTestDir, "TestNetwork", "bcTheDaoTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestEIP150Bc(t *testing.T) { - err := RunBlockTest(big.NewInt(0), big.NewInt(8), big.NewInt(10), filepath.Join(blockTestDir, "TestNetwork", "bcEIP150Test.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestHomesteadBcExploit(t *testing.T) { - err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcExploitTest.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} -func TestHomesteadBcShanghaiLove(t *testing.T) { - err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcShanghaiLove.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } -} -func TestHomesteadBcSuicideIssue(t *testing.T) { - err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcSuicideIssue.json"), BlockSkipTests) - if err != nil { - t.Fatal(err) - } + bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) { + cfg := bt.findConfig(name) + if err := bt.checkFailure(t, name, test.Run(cfg)); err != nil { + t.Error(err) + } + }) } diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 24d4672b64..a74f7d68dd 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -14,19 +14,19 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . +// Package tests implements execution of Ethereum JSON tests. package tests import ( "bytes" "encoding/hex" + "encoding/json" "fmt" - "io" "math/big" - "runtime" - "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/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" @@ -34,212 +34,115 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) -// Block Test JSON Format +// A BlockTest checks handling of entire blocks. type BlockTest struct { - Genesis *types.Block + json btJSON +} - Json *btJSON - preAccounts map[string]btAccount - postAccounts map[string]btAccount - lastblockhash string +func (t *BlockTest) UnmarshalJSON(in []byte) error { + return json.Unmarshal(in, &t.json) } type btJSON struct { - Blocks []btBlock - GenesisBlockHeader btHeader - Pre map[string]btAccount - PostState map[string]btAccount - Lastblockhash string + Blocks []btBlock `json:"blocks"` + Genesis btHeader `json:"genesisBlockHeader"` + Pre core.GenesisAlloc `json:"pre"` + Post core.GenesisAlloc `json:"postState"` + BestBlock common.UnprefixedHash `json:"lastblockhash"` } type btBlock struct { BlockHeader *btHeader Rlp string - Transactions []btTransaction UncleHeaders []*btHeader } -type btAccount struct { - Balance string - Code string - Nonce string - Storage map[string]string - PrivateKey string -} +//go:generate gencodec -type btHeader -field-override btHeaderMarshaling -out gen_btheader.go type btHeader struct { - Bloom string - Coinbase string - MixHash string - Nonce string - Number string - Hash string - ParentHash string - ReceiptTrie string - SeedHash string - StateRoot string - TransactionsTrie string - UncleHash string - - ExtraData string - Difficulty string - GasLimit string - GasUsed string - Timestamp string + 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 *big.Int + GasUsed *big.Int + Timestamp *big.Int } -type btTransaction struct { - Data string - GasLimit string - GasPrice string - Nonce string - R string - S string - To string - V string - Value string +type btHeaderMarshaling struct { + ExtraData hexutil.Bytes + Number *math.HexOrDecimal256 + Difficulty *math.HexOrDecimal256 + GasLimit *math.HexOrDecimal256 + GasUsed *math.HexOrDecimal256 + Timestamp *math.HexOrDecimal256 } -func RunBlockTestWithReader(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, r io.Reader, skipTests []string) error { - btjs := make(map[string]*btJSON) - if err := readJson(r, &btjs); err != nil { - return err - } - - bt, err := convertBlockTests(btjs) - if err != nil { - return err - } - - if err := runBlockTests(homesteadBlock, daoForkBlock, gasPriceFork, bt, skipTests); err != nil { - return err - } - return nil -} - -func RunBlockTest(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, file string, skipTests []string) error { - btjs := make(map[string]*btJSON) - if err := readJsonFile(file, &btjs); err != nil { - return err - } - - bt, err := convertBlockTests(btjs) - if err != nil { - return err - } - if err := runBlockTests(homesteadBlock, daoForkBlock, gasPriceFork, bt, skipTests); err != nil { - return err - } - return nil -} - -func runBlockTests(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, bt map[string]*BlockTest, skipTests []string) error { - skipTest := make(map[string]bool, len(skipTests)) - for _, name := range skipTests { - skipTest[name] = true - } - - for name, test := range bt { - if skipTest[name] /*|| name != "CallingCanonicalContractFromFork_CALLCODE"*/ { - log.Info(fmt.Sprint("Skipping block test", name)) - continue - } - // test the block - if err := runBlockTest(homesteadBlock, daoForkBlock, gasPriceFork, test); err != nil { - return fmt.Errorf("%s: %v", name, err) - } - log.Info(fmt.Sprint("Block test passed: ", name)) - - } - return nil -} - -func runBlockTest(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, test *BlockTest) error { +func (t *BlockTest) Run(config *params.ChainConfig) error { // import pre accounts & construct test genesis block & state root db, _ := ethdb.NewMemDatabase() - if _, err := test.InsertPreState(db); err != nil { - return fmt.Errorf("InsertPreState: %v", err) + gblock, err := t.genesis(config).Commit(db) + if err != nil { + return err + } + if gblock.Hash() != t.json.Genesis.Hash { + return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x\n", 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]) } - core.WriteTd(db, test.Genesis.Hash(), 0, test.Genesis.Difficulty()) - core.WriteBlock(db, test.Genesis) - core.WriteCanonicalHash(db, test.Genesis.Hash(), test.Genesis.NumberU64()) - core.WriteHeadBlockHash(db, test.Genesis.Hash()) - evmux := new(event.TypeMux) - config := ¶ms.ChainConfig{HomesteadBlock: homesteadBlock, DAOForkBlock: daoForkBlock, DAOForkSupport: true, EIP150Block: gasPriceFork} - chain, err := core.NewBlockChain(db, config, ethash.NewShared(), evmux, vm.Config{}) + chain, err := core.NewBlockChain(db, config, ethash.NewShared(), new(event.TypeMux), vm.Config{}) if err != nil { return err } defer chain.Stop() - //vm.Debug = true - validBlocks, err := test.TryBlocksInsert(chain) + validBlocks, err := t.insertBlocks(chain) if err != nil { return err } - - lastblockhash := common.HexToHash(test.lastblockhash) cmlast := chain.LastBlockHash() - if lastblockhash != cmlast { - return fmt.Errorf("lastblockhash validation mismatch: want: %x, have: %x", lastblockhash, cmlast) + 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 = test.ValidatePostState(newDB); err != nil { + if err = t.validatePostState(newDB); err != nil { return fmt.Errorf("post state validation failed: %v", err) } - - return test.ValidateImportedHeaders(chain, validBlocks) + return t.validateImportedHeaders(chain, validBlocks) } -// InsertPreState populates the given database with the genesis -// accounts defined by the test. -func (t *BlockTest) InsertPreState(db ethdb.Database) (*state.StateDB, error) { - statedb, err := state.New(common.Hash{}, state.NewDatabase(db)) - if err != nil { - return nil, err +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.Uint64(), + ParentHash: t.json.Genesis.ParentHash, + ExtraData: t.json.Genesis.ExtraData, + GasLimit: t.json.Genesis.GasLimit.Uint64(), + GasUsed: t.json.Genesis.GasUsed.Uint64(), + Difficulty: t.json.Genesis.Difficulty, + Mixhash: t.json.Genesis.MixHash, + Coinbase: t.json.Genesis.Coinbase, + Alloc: t.json.Pre, } - for addrString, acct := range t.preAccounts { - code, err := hex.DecodeString(strings.TrimPrefix(acct.Code, "0x")) - if err != nil { - return nil, err - } - balance, ok := new(big.Int).SetString(acct.Balance, 0) - if !ok { - return nil, err - } - nonce, err := strconv.ParseUint(prepInt(16, acct.Nonce), 16, 64) - if err != nil { - return nil, err - } - - addr := common.HexToAddress(addrString) - statedb.CreateAccount(addr) - statedb.SetCode(addr, code) - statedb.SetBalance(addr, balance) - statedb.SetNonce(addr, nonce) - for k, v := range acct.Storage { - statedb.SetState(common.HexToAddress(addrString), common.HexToHash(k), common.HexToHash(v)) - } - } - - root, err := statedb.CommitTo(db, false) - if err != nil { - return nil, fmt.Errorf("error writing state: %v", err) - } - if t.Genesis.Root() != root { - return nil, fmt.Errorf("computed state root does not match genesis block: genesis=%x computed=%x", t.Genesis.Root().Bytes()[:4], root.Bytes()[:4]) - } - return statedb, nil } /* See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II @@ -254,11 +157,11 @@ func (t *BlockTest) InsertPreState(db ethdb.Database) (*state.StateDB, error) { expected we are expected to ignore it and continue processing and then validate the post state. */ -func (t *BlockTest) TryBlocksInsert(blockchain *core.BlockChain) ([]btBlock, error) { +func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) { validBlocks := make([]btBlock, 0) // insert the test blocks, which will execute all transactions - for _, b := range t.Json.Blocks { - cb, err := mustConvertBlock(b) + for _, 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 @@ -290,288 +193,99 @@ func (t *BlockTest) TryBlocksInsert(blockchain *core.BlockChain) ([]btBlock, err } func validateHeader(h *btHeader, h2 *types.Header) error { - expectedBloom := mustConvertBytes(h.Bloom) - if !bytes.Equal(expectedBloom, h2.Bloom.Bytes()) { - return fmt.Errorf("Bloom: want: %x have: %x", expectedBloom, h2.Bloom.Bytes()) + if h.Bloom != h2.Bloom { + return fmt.Errorf("Bloom: want: %x have: %x", h.Bloom, h2.Bloom) } - - expectedCoinbase := mustConvertBytes(h.Coinbase) - if !bytes.Equal(expectedCoinbase, h2.Coinbase.Bytes()) { - return fmt.Errorf("Coinbase: want: %x have: %x", expectedCoinbase, h2.Coinbase.Bytes()) + if h.Coinbase != h2.Coinbase { + return fmt.Errorf("Coinbase: want: %x have: %x", h.Coinbase, h2.Coinbase) } - - expectedMixHashBytes := mustConvertBytes(h.MixHash) - if !bytes.Equal(expectedMixHashBytes, h2.MixDigest.Bytes()) { - return fmt.Errorf("MixHash: want: %x have: %x", expectedMixHashBytes, h2.MixDigest.Bytes()) + if h.MixHash != h2.MixDigest { + return fmt.Errorf("MixHash: want: %x have: %x", h.MixHash, h2.MixDigest) } - - expectedNonce := mustConvertBytes(h.Nonce) - if !bytes.Equal(expectedNonce, h2.Nonce[:]) { - return fmt.Errorf("Nonce: want: %x have: %x", expectedNonce, h2.Nonce) + if h.Nonce != h2.Nonce { + return fmt.Errorf("Nonce: want: %x have: %x", h.Nonce, h2.Nonce) } - - expectedNumber := mustConvertBigInt(h.Number, 16) - if expectedNumber.Cmp(h2.Number) != 0 { - return fmt.Errorf("Number: want: %v have: %v", expectedNumber, h2.Number) + if h.Number.Cmp(h2.Number) != 0 { + return fmt.Errorf("Number: want: %v have: %v", h.Number, h2.Number) } - - expectedParentHash := mustConvertBytes(h.ParentHash) - if !bytes.Equal(expectedParentHash, h2.ParentHash.Bytes()) { - return fmt.Errorf("Parent hash: want: %x have: %x", expectedParentHash, h2.ParentHash.Bytes()) + if h.ParentHash != h2.ParentHash { + return fmt.Errorf("Parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash) } - - expectedReceiptHash := mustConvertBytes(h.ReceiptTrie) - if !bytes.Equal(expectedReceiptHash, h2.ReceiptHash.Bytes()) { - return fmt.Errorf("Receipt hash: want: %x have: %x", expectedReceiptHash, h2.ReceiptHash.Bytes()) + if h.ReceiptTrie != h2.ReceiptHash { + return fmt.Errorf("Receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash) } - - expectedTxHash := mustConvertBytes(h.TransactionsTrie) - if !bytes.Equal(expectedTxHash, h2.TxHash.Bytes()) { - return fmt.Errorf("Tx hash: want: %x have: %x", expectedTxHash, h2.TxHash.Bytes()) + if h.TransactionsTrie != h2.TxHash { + return fmt.Errorf("Tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash) } - - expectedStateHash := mustConvertBytes(h.StateRoot) - if !bytes.Equal(expectedStateHash, h2.Root.Bytes()) { - return fmt.Errorf("State hash: want: %x have: %x", expectedStateHash, h2.Root.Bytes()) + if h.StateRoot != h2.Root { + return fmt.Errorf("State hash: want: %x have: %x", h.StateRoot, h2.Root) } - - expectedUncleHash := mustConvertBytes(h.UncleHash) - if !bytes.Equal(expectedUncleHash, h2.UncleHash.Bytes()) { - return fmt.Errorf("Uncle hash: want: %x have: %x", expectedUncleHash, h2.UncleHash.Bytes()) + if h.UncleHash != h2.UncleHash { + return fmt.Errorf("Uncle hash: want: %x have: %x", h.UncleHash, h2.UncleHash) } - - expectedExtraData := mustConvertBytes(h.ExtraData) - if !bytes.Equal(expectedExtraData, h2.Extra) { - return fmt.Errorf("Extra data: want: %x have: %x", expectedExtraData, h2.Extra) + if !bytes.Equal(h.ExtraData, h2.Extra) { + return fmt.Errorf("Extra data: want: %x have: %x", h.ExtraData, h2.Extra) } - - expectedDifficulty := mustConvertBigInt(h.Difficulty, 16) - if expectedDifficulty.Cmp(h2.Difficulty) != 0 { - return fmt.Errorf("Difficulty: want: %v have: %v", expectedDifficulty, h2.Difficulty) + if h.Difficulty.Cmp(h2.Difficulty) != 0 { + return fmt.Errorf("Difficulty: want: %v have: %v", h.Difficulty, h2.Difficulty) } - - expectedGasLimit := mustConvertBigInt(h.GasLimit, 16) - if expectedGasLimit.Cmp(h2.GasLimit) != 0 { - return fmt.Errorf("GasLimit: want: %v have: %v", expectedGasLimit, h2.GasLimit) + if h.GasLimit.Cmp(h2.GasLimit) != 0 { + return fmt.Errorf("GasLimit: want: %v have: %v", h.GasLimit, h2.GasLimit) } - expectedGasUsed := mustConvertBigInt(h.GasUsed, 16) - if expectedGasUsed.Cmp(h2.GasUsed) != 0 { - return fmt.Errorf("GasUsed: want: %v have: %v", expectedGasUsed, h2.GasUsed) + if h.GasUsed.Cmp(h2.GasUsed) != 0 { + return fmt.Errorf("GasUsed: want: %v have: %v", h.GasUsed, h2.GasUsed) } - - expectedTimestamp := mustConvertBigInt(h.Timestamp, 16) - if expectedTimestamp.Cmp(h2.Time) != 0 { - return fmt.Errorf("Timestamp: want: %v have: %v", expectedTimestamp, h2.Time) + if h.Timestamp.Cmp(h2.Time) != 0 { + return fmt.Errorf("Timestamp: want: %v have: %v", h.Timestamp, h2.Time) } - return nil } -func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error { +func (t *BlockTest) validatePostState(statedb *state.StateDB) error { // validate post state accounts in test file against what we have in state db - for addrString, acct := range t.postAccounts { - // XXX: is is worth it checking for errors here? - addr, err := hex.DecodeString(addrString) - if err != nil { - return err - } - code, err := hex.DecodeString(strings.TrimPrefix(acct.Code, "0x")) - if err != nil { - return err - } - balance, ok := new(big.Int).SetString(acct.Balance, 0) - if !ok { - return err - } - nonce, err := strconv.ParseUint(prepInt(16, acct.Nonce), 16, 64) - if err != nil { - return err - } - + for addr, acct := range t.json.Post { // address is indirectly verified by the other fields, as it's the db key - code2 := statedb.GetCode(common.BytesToAddress(addr)) - balance2 := statedb.GetBalance(common.BytesToAddress(addr)) - nonce2 := statedb.GetNonce(common.BytesToAddress(addr)) - if !bytes.Equal(code2, code) { - return fmt.Errorf("account code mismatch for addr: %s want: %s have: %s", addrString, hex.EncodeToString(code), hex.EncodeToString(code2)) + 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(balance) != 0 { - return fmt.Errorf("account balance mismatch for addr: %s, want: %d, have: %d", addrString, balance, balance2) + 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 != nonce { - return fmt.Errorf("account nonce mismatch for addr: %s want: %d have: %d", addrString, nonce, nonce2) + if nonce2 != acct.Nonce { + return fmt.Errorf("account nonce mismatch for addr: %s want: %d have: %d", addr, acct.Nonce, nonce2) } } return nil } -func (test *BlockTest) ValidateImportedHeaders(cm *core.BlockChain, validBlocks []btBlock) error { +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[string]btBlock, len(test.Json.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 ChainManager, as they may not + // 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.NumberU64() != 0; b = cm.GetBlockByHash(b.Header().ParentHash) { - bHash := common.Bytes2Hex(b.Hash().Bytes()) // hex without 0x prefix - if err := validateHeader(bmap[bHash].BlockHeader, b.Header()); err != nil { + if err := validateHeader(bmap[b.Hash()].BlockHeader, b.Header()); err != nil { return fmt.Errorf("Imported block header validation failed: %v", err) } } return nil } -func convertBlockTests(in map[string]*btJSON) (map[string]*BlockTest, error) { - out := make(map[string]*BlockTest) - for name, test := range in { - var err error - if out[name], err = convertBlockTest(test); err != nil { - return out, fmt.Errorf("bad test %q: %v", name, err) - } - } - return out, nil -} - -func convertBlockTest(in *btJSON) (out *BlockTest, err error) { - // the conversion handles errors by catching panics. - // you might consider this ugly, but the alternative (passing errors) - // would be much harder to read. - defer func() { - if recovered := recover(); recovered != nil { - buf := make([]byte, 64<<10) - buf = buf[:runtime.Stack(buf, false)] - err = fmt.Errorf("%v\n%s", recovered, buf) - } - }() - out = &BlockTest{preAccounts: in.Pre, postAccounts: in.PostState, Json: in, lastblockhash: in.Lastblockhash} - out.Genesis = mustConvertGenesis(in.GenesisBlockHeader) - return out, err -} - -func mustConvertGenesis(testGenesis btHeader) *types.Block { - hdr := mustConvertHeader(testGenesis) - hdr.Number = big.NewInt(0) - - return types.NewBlockWithHeader(hdr) -} - -func mustConvertHeader(in btHeader) *types.Header { - // hex decode these fields - header := &types.Header{ - //SeedHash: mustConvertBytes(in.SeedHash), - MixDigest: mustConvertHash(in.MixHash), - Bloom: mustConvertBloom(in.Bloom), - ReceiptHash: mustConvertHash(in.ReceiptTrie), - TxHash: mustConvertHash(in.TransactionsTrie), - Root: mustConvertHash(in.StateRoot), - Coinbase: mustConvertAddress(in.Coinbase), - UncleHash: mustConvertHash(in.UncleHash), - ParentHash: mustConvertHash(in.ParentHash), - Extra: mustConvertBytes(in.ExtraData), - GasUsed: mustConvertBigInt(in.GasUsed, 16), - GasLimit: mustConvertBigInt(in.GasLimit, 16), - Difficulty: mustConvertBigInt(in.Difficulty, 16), - Time: mustConvertBigInt(in.Timestamp, 16), - Nonce: types.EncodeNonce(mustConvertUint(in.Nonce, 16)), - } - return header -} - -func mustConvertBlock(testBlock btBlock) (*types.Block, error) { - var b types.Block - r := bytes.NewReader(mustConvertBytes(testBlock.Rlp)) - err := rlp.Decode(r, &b) - return &b, err -} - -func mustConvertBytes(in string) []byte { - if in == "0x" { - return []byte{} - } - h := unfuckFuckedHex(strings.TrimPrefix(in, "0x")) - out, err := hex.DecodeString(h) +func (bb *btBlock) decode() (*types.Block, error) { + data, err := hexutil.Decode(bb.Rlp) if err != nil { - panic(fmt.Errorf("invalid hex: %q", h)) - } - return out -} - -func mustConvertHash(in string) common.Hash { - out, err := hex.DecodeString(strings.TrimPrefix(in, "0x")) - if err != nil { - panic(fmt.Errorf("invalid hex: %q", in)) - } - return common.BytesToHash(out) -} - -func mustConvertAddress(in string) common.Address { - out, err := hex.DecodeString(strings.TrimPrefix(in, "0x")) - if err != nil { - panic(fmt.Errorf("invalid hex: %q", in)) - } - return common.BytesToAddress(out) -} - -func mustConvertBloom(in string) types.Bloom { - out, err := hex.DecodeString(strings.TrimPrefix(in, "0x")) - if err != nil { - panic(fmt.Errorf("invalid hex: %q", in)) - } - return types.BytesToBloom(out) -} - -func mustConvertBigInt(in string, base int) *big.Int { - in = prepInt(base, in) - out, ok := new(big.Int).SetString(in, base) - if !ok { - panic(fmt.Errorf("invalid integer: %q", in)) - } - return out -} - -func mustConvertUint(in string, base int) uint64 { - in = prepInt(base, in) - out, err := strconv.ParseUint(in, base, 64) - if err != nil { - panic(fmt.Errorf("invalid integer: %q", in)) - } - return out -} - -func LoadBlockTests(file string) (map[string]*BlockTest, error) { - btjs := make(map[string]*btJSON) - if err := readJsonFile(file, &btjs); err != nil { return nil, err } - - return convertBlockTests(btjs) -} - -// Nothing to see here, please move along... -func prepInt(base int, s string) string { - if base == 16 { - s = strings.TrimPrefix(s, "0x") - if len(s) == 0 { - s = "00" - } - s = nibbleFix(s) - } - return s -} - -// don't ask -func unfuckFuckedHex(almostHex string) string { - return nibbleFix(strings.Replace(almostHex, "v", "", -1)) -} - -func nibbleFix(s string) string { - if len(s)%2 != 0 { - s = "0" + s - } - return s + var b types.Block + err = rlp.DecodeBytes(data, &b) + return &b, err } diff --git a/tests/gen_btheader.go b/tests/gen_btheader.go new file mode 100644 index 0000000000..5d65e0dbce --- /dev/null +++ b/tests/gen_btheader.go @@ -0,0 +1,128 @@ +// 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) + +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.HexOrDecimal256 + GasUsed *math.HexOrDecimal256 + Timestamp *math.HexOrDecimal256 + } + 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.HexOrDecimal256)(b.GasLimit) + enc.GasUsed = (*math.HexOrDecimal256)(b.GasUsed) + enc.Timestamp = (*math.HexOrDecimal256)(b.Timestamp) + return json.Marshal(&enc) +} + +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.HexOrDecimal256 + GasUsed *math.HexOrDecimal256 + Timestamp *math.HexOrDecimal256 + } + 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 = (*big.Int)(dec.GasLimit) + } + if dec.GasUsed != nil { + b.GasUsed = (*big.Int)(dec.GasUsed) + } + if dec.Timestamp != nil { + b.Timestamp = (*big.Int)(dec.Timestamp) + } + return nil +} diff --git a/tests/gen_stenv.go b/tests/gen_stenv.go new file mode 100644 index 0000000000..c780524bc3 --- /dev/null +++ b/tests/gen_stenv.go @@ -0,0 +1,66 @@ +// 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) + +func (s stEnv) MarshalJSON() ([]byte, error) { + type stEnv struct { + Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"` + GasLimit *math.HexOrDecimal256 `json:"currentGasLimit" gencodec:"required"` + Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` + Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` + } + var enc stEnv + enc.Coinbase = common.UnprefixedAddress(s.Coinbase) + enc.Difficulty = (*math.HexOrDecimal256)(s.Difficulty) + enc.GasLimit = (*math.HexOrDecimal256)(s.GasLimit) + enc.Number = math.HexOrDecimal64(s.Number) + enc.Timestamp = math.HexOrDecimal64(s.Timestamp) + return json.Marshal(&enc) +} + +func (s *stEnv) UnmarshalJSON(input []byte) error { + type stEnv struct { + Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"` + GasLimit *math.HexOrDecimal256 `json:"currentGasLimit" gencodec:"required"` + Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` + Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` + } + 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 { + return errors.New("missing required field 'currentDifficulty' for stEnv") + } + s.Difficulty = (*big.Int)(dec.Difficulty) + if dec.GasLimit == nil { + return errors.New("missing required field 'currentGasLimit' for stEnv") + } + s.GasLimit = (*big.Int)(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) + return nil +} diff --git a/tests/gen_stlog.go b/tests/gen_stlog.go new file mode 100644 index 0000000000..4f7ebc9660 --- /dev/null +++ b/tests/gen_stlog.go @@ -0,0 +1,61 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package tests + +import ( + "encoding/json" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*stLogMarshaling)(nil) + +func (s stLog) MarshalJSON() ([]byte, error) { + type stLog struct { + Address common.UnprefixedAddress `json:"address"` + Data hexutil.Bytes `json:"data"` + Topics []common.UnprefixedHash `json:"topics"` + Bloom string `json:"bloom"` + } + var enc stLog + enc.Address = common.UnprefixedAddress(s.Address) + enc.Data = s.Data + if s.Topics != nil { + enc.Topics = make([]common.UnprefixedHash, len(s.Topics)) + for k, v := range s.Topics { + enc.Topics[k] = common.UnprefixedHash(v) + } + } + enc.Bloom = s.Bloom + return json.Marshal(&enc) +} + +func (s *stLog) UnmarshalJSON(input []byte) error { + type stLog struct { + Address *common.UnprefixedAddress `json:"address"` + Data hexutil.Bytes `json:"data"` + Topics []common.UnprefixedHash `json:"topics"` + Bloom *string `json:"bloom"` + } + var dec stLog + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Address != nil { + s.Address = common.Address(*dec.Address) + } + if dec.Data != nil { + s.Data = dec.Data + } + if dec.Topics != nil { + s.Topics = make([]common.Hash, len(dec.Topics)) + for k, v := range dec.Topics { + s.Topics[k] = common.Hash(v) + } + } + if dec.Bloom != nil { + s.Bloom = *dec.Bloom + } + return nil +} diff --git a/tests/gen_sttransaction.go b/tests/gen_sttransaction.go new file mode 100644 index 0000000000..5a489d00b8 --- /dev/null +++ b/tests/gen_sttransaction.go @@ -0,0 +1,80 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package tests + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/math" +) + +var _ = (*stTransactionMarshaling)(nil) + +func (s stTransaction) MarshalJSON() ([]byte, error) { + type stTransaction struct { + GasPrice *math.HexOrDecimal256 `json:"gasPrice"` + Nonce math.HexOrDecimal64 `json:"nonce"` + To string `json:"to"` + Data []string `json:"data"` + GasLimit []math.HexOrDecimal64 `json:"gasLimit"` + Value []string `json:"value"` + PrivateKey hexutil.Bytes `json:"secretKey"` + } + var enc stTransaction + enc.GasPrice = (*math.HexOrDecimal256)(s.GasPrice) + enc.Nonce = math.HexOrDecimal64(s.Nonce) + enc.To = s.To + enc.Data = s.Data + if s.GasLimit != nil { + enc.GasLimit = make([]math.HexOrDecimal64, len(s.GasLimit)) + for k, v := range s.GasLimit { + enc.GasLimit[k] = math.HexOrDecimal64(v) + } + } + enc.Value = s.Value + enc.PrivateKey = s.PrivateKey + return json.Marshal(&enc) +} + +func (s *stTransaction) UnmarshalJSON(input []byte) error { + type stTransaction struct { + GasPrice *math.HexOrDecimal256 `json:"gasPrice"` + Nonce *math.HexOrDecimal64 `json:"nonce"` + To *string `json:"to"` + Data []string `json:"data"` + GasLimit []math.HexOrDecimal64 `json:"gasLimit"` + Value []string `json:"value"` + PrivateKey hexutil.Bytes `json:"secretKey"` + } + var dec stTransaction + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.GasPrice != nil { + s.GasPrice = (*big.Int)(dec.GasPrice) + } + if dec.Nonce != nil { + s.Nonce = uint64(*dec.Nonce) + } + if dec.To != nil { + s.To = *dec.To + } + if dec.Data != nil { + s.Data = dec.Data + } + if dec.GasLimit != nil { + s.GasLimit = make([]uint64, len(dec.GasLimit)) + for k, v := range dec.GasLimit { + s.GasLimit[k] = uint64(v) + } + } + if dec.Value != nil { + s.Value = dec.Value + } + if dec.PrivateKey != nil { + s.PrivateKey = dec.PrivateKey + } + return nil +} diff --git a/tests/gen_tttransaction.go b/tests/gen_tttransaction.go new file mode 100644 index 0000000000..b6759be912 --- /dev/null +++ b/tests/gen_tttransaction.go @@ -0,0 +1,95 @@ +// 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/hexutil" + "github.com/ethereum/go-ethereum/common/math" +) + +var _ = (*ttTransactionMarshaling)(nil) + +func (t ttTransaction) MarshalJSON() ([]byte, error) { + type ttTransaction struct { + Data hexutil.Bytes `gencodec:"required"` + GasLimit *math.HexOrDecimal256 `gencodec:"required"` + GasPrice *math.HexOrDecimal256 `gencodec:"required"` + Nonce math.HexOrDecimal64 `gencodec:"required"` + Value *math.HexOrDecimal256 `gencodec:"required"` + R *math.HexOrDecimal256 `gencodec:"required"` + S *math.HexOrDecimal256 `gencodec:"required"` + V *math.HexOrDecimal256 `gencodec:"required"` + To common.Address `gencodec:"required"` + } + var enc ttTransaction + enc.Data = t.Data + enc.GasLimit = (*math.HexOrDecimal256)(t.GasLimit) + enc.GasPrice = (*math.HexOrDecimal256)(t.GasPrice) + enc.Nonce = math.HexOrDecimal64(t.Nonce) + enc.Value = (*math.HexOrDecimal256)(t.Value) + enc.R = (*math.HexOrDecimal256)(t.R) + enc.S = (*math.HexOrDecimal256)(t.S) + enc.V = (*math.HexOrDecimal256)(t.V) + enc.To = t.To + return json.Marshal(&enc) +} + +func (t *ttTransaction) UnmarshalJSON(input []byte) error { + type ttTransaction struct { + Data hexutil.Bytes `gencodec:"required"` + GasLimit *math.HexOrDecimal256 `gencodec:"required"` + GasPrice *math.HexOrDecimal256 `gencodec:"required"` + Nonce *math.HexOrDecimal64 `gencodec:"required"` + Value *math.HexOrDecimal256 `gencodec:"required"` + R *math.HexOrDecimal256 `gencodec:"required"` + S *math.HexOrDecimal256 `gencodec:"required"` + V *math.HexOrDecimal256 `gencodec:"required"` + To *common.Address `gencodec:"required"` + } + var dec ttTransaction + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Data == nil { + return errors.New("missing required field 'data' for ttTransaction") + } + t.Data = dec.Data + if dec.GasLimit == nil { + return errors.New("missing required field 'gasLimit' for ttTransaction") + } + t.GasLimit = (*big.Int)(dec.GasLimit) + if dec.GasPrice == nil { + return errors.New("missing required field 'gasPrice' for ttTransaction") + } + t.GasPrice = (*big.Int)(dec.GasPrice) + if dec.Nonce == nil { + return errors.New("missing required field 'nonce' for ttTransaction") + } + t.Nonce = uint64(*dec.Nonce) + if dec.Value == nil { + return errors.New("missing required field 'value' for ttTransaction") + } + t.Value = (*big.Int)(dec.Value) + if dec.R == nil { + return errors.New("missing required field 'r' for ttTransaction") + } + t.R = (*big.Int)(dec.R) + if dec.S == nil { + return errors.New("missing required field 's' for ttTransaction") + } + t.S = (*big.Int)(dec.S) + if dec.V == nil { + return errors.New("missing required field 'v' for ttTransaction") + } + t.V = (*big.Int)(dec.V) + if dec.To == nil { + return errors.New("missing required field 'to' for ttTransaction") + } + t.To = *dec.To + return nil +} diff --git a/tests/gen_vmexec.go b/tests/gen_vmexec.go new file mode 100644 index 0000000000..dd2d3d94e7 --- /dev/null +++ b/tests/gen_vmexec.go @@ -0,0 +1,88 @@ +// 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/hexutil" + "github.com/ethereum/go-ethereum/common/math" +) + +var _ = (*vmExecMarshaling)(nil) + +func (v vmExec) MarshalJSON() ([]byte, error) { + type vmExec struct { + Address common.UnprefixedAddress `json:"address" gencodec:"required"` + Caller common.UnprefixedAddress `json:"caller" gencodec:"required"` + Origin common.UnprefixedAddress `json:"origin" gencodec:"required"` + Code hexutil.Bytes `json:"code" gencodec:"required"` + Data hexutil.Bytes `json:"data" gencodec:"required"` + Value *math.HexOrDecimal256 `json:"value" gencodec:"required"` + GasLimit math.HexOrDecimal64 `json:"gas" gencodec:"required"` + GasPrice *math.HexOrDecimal256 `json:"gasPrice" gencodec:"required"` + } + var enc vmExec + enc.Address = common.UnprefixedAddress(v.Address) + enc.Caller = common.UnprefixedAddress(v.Caller) + enc.Origin = common.UnprefixedAddress(v.Origin) + enc.Code = v.Code + enc.Data = v.Data + enc.Value = (*math.HexOrDecimal256)(v.Value) + enc.GasLimit = math.HexOrDecimal64(v.GasLimit) + enc.GasPrice = (*math.HexOrDecimal256)(v.GasPrice) + return json.Marshal(&enc) +} + +func (v *vmExec) UnmarshalJSON(input []byte) error { + type vmExec struct { + Address *common.UnprefixedAddress `json:"address" gencodec:"required"` + Caller *common.UnprefixedAddress `json:"caller" gencodec:"required"` + Origin *common.UnprefixedAddress `json:"origin" gencodec:"required"` + Code hexutil.Bytes `json:"code" gencodec:"required"` + Data hexutil.Bytes `json:"data" gencodec:"required"` + Value *math.HexOrDecimal256 `json:"value" gencodec:"required"` + GasLimit *math.HexOrDecimal64 `json:"gas" gencodec:"required"` + GasPrice *math.HexOrDecimal256 `json:"gasPrice" gencodec:"required"` + } + var dec vmExec + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Address == nil { + return errors.New("missing required field 'address' for vmExec") + } + v.Address = common.Address(*dec.Address) + if dec.Caller == nil { + return errors.New("missing required field 'caller' for vmExec") + } + v.Caller = common.Address(*dec.Caller) + if dec.Origin == nil { + return errors.New("missing required field 'origin' for vmExec") + } + v.Origin = common.Address(*dec.Origin) + if dec.Code == nil { + return errors.New("missing required field 'code' for vmExec") + } + v.Code = dec.Code + if dec.Data == nil { + return errors.New("missing required field 'data' for vmExec") + } + v.Data = dec.Data + if dec.Value == nil { + return errors.New("missing required field 'value' for vmExec") + } + v.Value = (*big.Int)(dec.Value) + if dec.GasLimit == nil { + return errors.New("missing required field 'gas' for vmExec") + } + v.GasLimit = uint64(*dec.GasLimit) + if dec.GasPrice == nil { + return errors.New("missing required field 'gasPrice' for vmExec") + } + v.GasPrice = (*big.Int)(dec.GasPrice) + return nil +} diff --git a/tests/init.go b/tests/init.go deleted file mode 100644 index 7b0924bc38..0000000000 --- a/tests/init.go +++ /dev/null @@ -1,119 +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 . - -// Package tests implements execution of Ethereum JSON tests. -package tests - -import ( - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net/http" - "os" - "path/filepath" -) - -var ( - baseDir = filepath.Join(".", "files") - blockTestDir = filepath.Join(baseDir, "BlockchainTests") - stateTestDir = filepath.Join(baseDir, "StateTests") - transactionTestDir = filepath.Join(baseDir, "TransactionTests") - vmTestDir = filepath.Join(baseDir, "VMTests") - rlpTestDir = filepath.Join(baseDir, "RLPTests") - - BlockSkipTests = []string{ - // These tests are not valid, as they are out of scope for RLP and - // the consensus protocol. - "BLOCK__RandomByteAtTheEnd", - "TRANSCT__RandomByteAtTheEnd", - "BLOCK__ZeroByteAtTheEnd", - "TRANSCT__ZeroByteAtTheEnd", - - "ChainAtoChainB_blockorder2", - "ChainAtoChainB_blockorder1", - - "GasLimitHigherThan2p63m1", // not yet ;) - "SuicideIssue", // fails genesis check - } - - /* Go client does not support transaction (account) nonces above 2^64. This - technically breaks consensus but is regarded as "reasonable - engineering constraint" as accounts cannot easily reach such high - nonce values in practice - */ - TransSkipTests = []string{ - "TransactionWithHihghNonce256", - "Vitalik_15", - "Vitalik_16", - "Vitalik_17", - } - StateSkipTests = []string{} - VmSkipTests = []string{} -) - -func readJson(reader io.Reader, value interface{}) error { - data, err := ioutil.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 fmt.Errorf("JSON unmarshal error: %v", err) - } - return nil -} - -func readJsonHttp(uri string, value interface{}) error { - resp, err := http.Get(uri) - if err != nil { - return err - } - defer resp.Body.Close() - - return readJson(resp.Body, value) -} - -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 -} diff --git a/tests/init_test.go b/tests/init_test.go new file mode 100644 index 0000000000..0adbb533dd --- /dev/null +++ b/tests/init_test.go @@ -0,0 +1,263 @@ +// 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 . + +package tests + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "reflect" + "regexp" + "sort" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/params" +) + +var ( + baseDir = filepath.Join(".", "testdata") + blockTestDir = filepath.Join(baseDir, "BlockchainTests") + stateTestDir = filepath.Join(baseDir, "GeneralStateTests") + transactionTestDir = filepath.Join(baseDir, "TransactionTests") + vmTestDir = filepath.Join(baseDir, "VMTests") + rlpTestDir = filepath.Join(baseDir, "RLPTests") +) + +func readJson(reader io.Reader, value interface{}) error { + data, err := ioutil.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 + skipshortpat []*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) skipShortMode(pattern string) { + tm.skipshortpat = append(tm.skipshortpat, 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. +func (tm *testMatcher) fails(pattern string, reason string) { + if reason == "" { + panic("empty fail reason") + } + tm.failpat = append(tm.failpat, testFailure{regexp.MustCompile(pattern), reason}) +} + +// 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) { + if testing.Short() { + for _, re := range tm.skipshortpat { + if re.MatchString(name) { + return "skipped in -short mode", 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(name string) *params.ChainConfig { + // TODO(fjl): name can be derived from testing.T when min Go version is 1.8 + for _, m := range tm.configpat { + if m.p.MatchString(name) { + return &m.config + } + } + return new(params.ChainConfig) +} + +// checkFailure checks whether a failure is expected. +func (tm *testMatcher) checkFailure(t *testing.T, name string, err error) error { + // TODO(fjl): name can be derived from t when min Go version is 1.8 + failReason := "" + for _, m := range tm.failpat { + if m.p.MatchString(name) { + failReason = m.reason + break + } + } + if failReason != "" { + t.Logf("expected failure: %s", failReason) + if err != nil { + t.Logf("error: %v", err) + return nil + } else { + return fmt.Errorf("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 ), +// 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) + } + t.Parallel() + + // Load the file as map[string]. + 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, ), 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)), + }) +} diff --git a/tests/rlp_test.go b/tests/rlp_test.go index 2469ce0dbc..1601625df5 100644 --- a/tests/rlp_test.go +++ b/tests/rlp_test.go @@ -17,20 +17,15 @@ package tests import ( - "path/filepath" "testing" ) func TestRLP(t *testing.T) { - err := RunRLPTest(filepath.Join(rlpTestDir, "rlptest.json"), nil) - if err != nil { - t.Fatal(err) - } -} - -func TestRLP_invalid(t *testing.T) { - err := RunRLPTest(filepath.Join(rlpTestDir, "invalidRLPTest.json"), nil) - if err != nil { - t.Fatal(err) - } + t.Parallel() + tm := new(testMatcher) + tm.walk(t, rlpTestDir, func(t *testing.T, name string, test *RLPTest) { + if err := tm.checkFailure(t, name, test.Run()); err != nil { + t.Error(err) + } + }) } diff --git a/tests/rlp_test_util.go b/tests/rlp_test_util.go index ac53a4f52c..58ef8a6428 100644 --- a/tests/rlp_test_util.go +++ b/tests/rlp_test_util.go @@ -21,9 +21,7 @@ import ( "encoding/hex" "errors" "fmt" - "io" "math/big" - "os" "strings" "github.com/ethereum/go-ethereum/rlp" @@ -44,33 +42,6 @@ type RLPTest struct { Out string } -// RunRLPTest runs the tests in the given file, skipping tests by name. -func RunRLPTest(file string, skip []string) error { - f, err := os.Open(file) - if err != nil { - return err - } - defer f.Close() - return RunRLPTestWithReader(f, skip) -} - -// RunRLPTest runs the tests encoded in r, skipping tests by name. -func RunRLPTestWithReader(r io.Reader, skip []string) error { - var tests map[string]*RLPTest - if err := readJson(r, &tests); err != nil { - return err - } - for _, s := range skip { - delete(tests, s) - } - for name, test := range tests { - if err := test.Run(); err != nil { - return fmt.Errorf("test %q failed: %v", name, err) - } - } - return nil -} - // Run executes the test. func (t *RLPTest) Run() error { outb, err := hex.DecodeString(t.Out) diff --git a/tests/state_test.go b/tests/state_test.go index 29180942b2..e4e691589e 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -17,970 +17,72 @@ package tests import ( - "math/big" - "os" - "path/filepath" + "bytes" + "fmt" + "reflect" "testing" - "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/core/vm" ) -func BenchmarkStateCall1024(b *testing.B) { - fn := filepath.Join(stateTestDir, "stCallCreateCallCodeTest.json") - if err := BenchVmTest(fn, bconf{"Call1024BalanceTooLow", true, os.Getenv("JITVM") == "true"}, b); err != nil { - b.Error(err) - } -} +func TestState(t *testing.T) { + t.Parallel() -func TestStateSystemOperations(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } + st := new(testMatcher) + // Long tests: + st.skipShortMode(`^stQuadraticComplexityTest/`) + // Broken tests: + st.skipLoad(`^stTransactionTest/OverflowGasRequire\.json`) // gasLimit > 256 bits + st.skipLoad(`^stStackTests/shallowStackOK\.json`) // bad hex encoding + st.skipLoad(`^stTransactionTest/zeroSigTransa[^/]*\.json`) // EIP-86 is not supported yet + // Expected failures: + st.fails(`^stCallCreateCallCodeTest/createJS_ExampleContract\.json`, "bug in test") + st.fails(`^stCodeSizeLimit/codesizeOOGInvalidSize\.json/(Frontier|Homestead)`, + "code size limit implementation is not conditional on fork") + st.fails(`^stRevertTest/RevertDepthCreateAddressCollision\.json/EIP15[08]/[67]`, "bug in test") + st.fails(`^stRevertTest/RevertPrecompiledTouch\.json/EIP158`, "bug in test") + st.fails(`^stRevertTest/RevertPrefoundEmptyOOG\.json/EIP158`, "bug in test") - fn := filepath.Join(stateTestDir, "stSystemOperationsTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestStateExample(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - fn := filepath.Join(stateTestDir, "stExample.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestStatePreCompiledContracts(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - fn := filepath.Join(stateTestDir, "stPreCompiledContracts.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestStateRecursiveCreate(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - fn := filepath.Join(stateTestDir, "stRecursiveCreate.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestStateSpecial(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - fn := filepath.Join(stateTestDir, "stSpecialTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestStateRefund(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - fn := filepath.Join(stateTestDir, "stRefundTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestStateBlockHash(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - fn := filepath.Join(stateTestDir, "stBlockHashTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestStateInitCode(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - fn := filepath.Join(stateTestDir, "stInitCodeTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestStateLog(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - fn := filepath.Join(stateTestDir, "stLogTests.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestStateTransaction(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - fn := filepath.Join(stateTestDir, "stTransactionTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestStateTransition(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - fn := filepath.Join(stateTestDir, "stTransitionTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestCallCreateCallCode(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - fn := filepath.Join(stateTestDir, "stCallCreateCallCodeTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestCallCodes(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - fn := filepath.Join(stateTestDir, "stCallCodes.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestMemory(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - fn := filepath.Join(stateTestDir, "stMemoryTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestMemoryStress(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - if os.Getenv("TEST_VM_COMPLEX") == "" { - t.Skip() - } - fn := filepath.Join(stateTestDir, "stMemoryStressTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestQuadraticComplexity(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - if os.Getenv("TEST_VM_COMPLEX") == "" { - t.Skip() - } - fn := filepath.Join(stateTestDir, "stQuadraticComplexityTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestSolidity(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - fn := filepath.Join(stateTestDir, "stSolidityTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestWallet(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - fn := filepath.Join(stateTestDir, "stWalletTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestStateTestsRandom(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } - - fns, _ := filepath.Glob("./files/StateTests/RandomTests/*") - for _, fn := range fns { - t.Log("running:", fn) - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(fn, err) + st.walk(t, stateTestDir, func(t *testing.T, name string, test *StateTest) { + for _, subtest := range test.Subtests() { + subtest := subtest + key := fmt.Sprintf("%s/%d", subtest.Fork, subtest.Index) + name := name + "/" + key + t.Run(key, func(t *testing.T) { + if subtest.Fork == "Metropolis" { + t.Skip("metropolis not supported yet") + } + withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error { + return st.checkFailure(t, name, test.Run(subtest, vmconfig)) + }) + }) } - } + }) } -// homestead tests -func TestHomesteadDelegateCall(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(1150000), - } +// Transactions with gasLimit above this value will not get a VM trace on failure. +const traceErrorLimit = 400000 - fn := filepath.Join(stateTestDir, "Homestead", "stDelegatecallTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestHomesteadStateSystemOperations(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - } - - fn := filepath.Join(stateTestDir, "Homestead", "stSystemOperationsTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestHomesteadStatePreCompiledContracts(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - } - - fn := filepath.Join(stateTestDir, "Homestead", "stPreCompiledContracts.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestHomesteadStateRecursiveCreate(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - } - - fn := filepath.Join(stateTestDir, "Homestead", "stSpecialTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestHomesteadStateRefund(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - } - - fn := filepath.Join(stateTestDir, "Homestead", "stRefundTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestHomesteadStateInitCode(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - } - - fn := filepath.Join(stateTestDir, "Homestead", "stInitCodeTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestHomesteadStateLog(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - } - - fn := filepath.Join(stateTestDir, "Homestead", "stLogTests.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestHomesteadStateTransaction(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - } - - fn := filepath.Join(stateTestDir, "Homestead", "stTransactionTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestHomesteadCallCreateCallCode(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - } - - fn := filepath.Join(stateTestDir, "Homestead", "stCallCreateCallCodeTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestHomesteadCallCodes(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - } - - fn := filepath.Join(stateTestDir, "Homestead", "stCallCodes.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestHomesteadMemory(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - } - - fn := filepath.Join(stateTestDir, "Homestead", "stMemoryTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestHomesteadMemoryStress(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - } - - if os.Getenv("TEST_VM_COMPLEX") == "" { - t.Skip() - } - fn := filepath.Join(stateTestDir, "Homestead", "stMemoryStressTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestHomesteadQuadraticComplexity(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - } - - if os.Getenv("TEST_VM_COMPLEX") == "" { - t.Skip() - } - fn := filepath.Join(stateTestDir, "Homestead", "stQuadraticComplexityTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestHomesteadWallet(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - } - - fn := filepath.Join(stateTestDir, "Homestead", "stWalletTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestHomesteadDelegateCodes(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - } - - fn := filepath.Join(stateTestDir, "Homestead", "stCallDelegateCodes.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestHomesteadDelegateCodesCallCode(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - } - - fn := filepath.Join(stateTestDir, "Homestead", "stCallDelegateCodesCallCode.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestHomesteadBounds(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - } - - fn := filepath.Join(stateTestDir, "Homestead", "stBoundsTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -// EIP150 tests -func TestEIP150Specific(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - fn := filepath.Join(stateTestDir, "EIP150", "stEIPSpecificTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150SingleCodeGasPrice(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - fn := filepath.Join(stateTestDir, "EIP150", "stEIPSingleCodeGasPrices.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150MemExpandingCalls(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - fn := filepath.Join(stateTestDir, "EIP150", "stMemExpandingEIPCalls.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150HomesteadStateSystemOperations(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stSystemOperationsTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150HomesteadStatePreCompiledContracts(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stPreCompiledContracts.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150HomesteadStateRecursiveCreate(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stSpecialTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150HomesteadStateRefund(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stRefundTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150HomesteadStateInitCode(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stInitCodeTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150HomesteadStateLog(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stLogTests.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150HomesteadStateTransaction(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stTransactionTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150HomesteadCallCreateCallCode(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stCallCreateCallCodeTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150HomesteadCallCodes(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stCallCodes.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150HomesteadMemory(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stMemoryTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150HomesteadMemoryStress(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - if os.Getenv("TEST_VM_COMPLEX") == "" { - t.Skip() - } - fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stMemoryStressTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150HomesteadQuadraticComplexity(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - if os.Getenv("TEST_VM_COMPLEX") == "" { - t.Skip() - } - fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stQuadraticComplexityTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150HomesteadWallet(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stWalletTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150HomesteadDelegateCodes(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stCallDelegateCodes.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150HomesteadDelegateCodesCallCode(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stCallDelegateCodesCallCode.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP150HomesteadBounds(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - } - - fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stBoundsTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -// EIP158 tests -func TestEIP158Create(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "stCreateTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158Specific(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "stEIP158SpecificTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158NonZeroCalls(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "stNonZeroCallsTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158ZeroCalls(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "stZeroCallsTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158_150Specific(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "EIP150", "stEIPSpecificTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158_150SingleCodeGasPrice(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "EIP150", "stEIPsingleCodeGasPrices.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158_150MemExpandingCalls(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "EIP150", "stMemExpandingEIPCalls.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158HomesteadStateSystemOperations(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "Homestead", "stSystemOperationsTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158HomesteadStatePreCompiledContracts(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "Homestead", "stPreCompiledContracts.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158HomesteadStateRecursiveCreate(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "Homestead", "stSpecialTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158HomesteadStateRefund(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "Homestead", "stRefundTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158HomesteadStateInitCode(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "Homestead", "stInitCodeTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158HomesteadStateLog(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "Homestead", "stLogTests.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158HomesteadStateTransaction(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "Homestead", "stTransactionTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158HomesteadCallCreateCallCode(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "Homestead", "stCallCreateCallCodeTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158HomesteadCallCodes(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "Homestead", "stCallCodes.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158HomesteadMemory(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "Homestead", "stMemoryTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158HomesteadMemoryStress(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - if os.Getenv("TEST_VM_COMPLEX") == "" { - t.Skip() - } - fn := filepath.Join(stateTestDir, "EIP158", "Homestead", "stMemoryStressTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158HomesteadQuadraticComplexity(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - if os.Getenv("TEST_VM_COMPLEX") == "" { - t.Skip() - } - fn := filepath.Join(stateTestDir, "EIP158", "Homestead", "stQuadraticComplexityTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158HomesteadWallet(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "Homestead", "stWalletTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158HomesteadDelegateCodes(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "Homestead", "stCallDelegateCodes.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158HomesteadDelegateCodesCallCode(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "Homestead", "stCallDelegateCodesCallCode.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) - } -} - -func TestEIP158HomesteadBounds(t *testing.T) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - EIP150Block: big.NewInt(2457000), - EIP158Block: params.MainnetChainConfig.EIP158Block, - } - - fn := filepath.Join(stateTestDir, "EIP158", "Homestead", "stBoundsTest.json") - if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { - t.Error(err) +func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) { + err := test(vm.Config{}) + if err == nil { + return + } + t.Error(err) + if gasLimit > traceErrorLimit { + t.Log("gas limit too high for EVM trace") + return + } + tracer := vm.NewStructLogger(nil) + err2 := test(vm.Config{Debug: true, Tracer: tracer}) + if !reflect.DeepEqual(err, err2) { + t.Errorf("different error for second run: %v", err2) + } + buf := new(bytes.Buffer) + vm.WriteTrace(buf, tracer.StructLogs()) + if buf.Len() == 0 { + t.Log("no EVM operation logs generated") + } else { + t.Log("EVM operation log:\n" + buf.String()) } } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 58acdd488b..fb74f5df09 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// 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 @@ -18,180 +18,296 @@ package tests import ( "bytes" + "encoding/hex" + "encoding/json" "fmt" - "io" - "strconv" + "math/big" + "reflect" "strings" - "testing" "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" "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/crypto" "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" ) -func RunStateTestWithReader(chainConfig *params.ChainConfig, r io.Reader, skipTests []string) error { - tests := make(map[string]VmTest) - if err := readJson(r, &tests); err != nil { - return err - } - - if err := runStateTests(chainConfig, tests, skipTests); err != nil { - return err - } - - return nil +// This table defines supported forks and their chain config. +var stateTestForks = map[string]*params.ChainConfig{ + "Frontier": ¶ms.ChainConfig{ + ChainId: big.NewInt(1), + }, + "Homestead": ¶ms.ChainConfig{ + HomesteadBlock: big.NewInt(0), + ChainId: big.NewInt(1), + }, + "EIP150": ¶ms.ChainConfig{ + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + ChainId: big.NewInt(1), + }, + "EIP158": ¶ms.ChainConfig{ + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ChainId: big.NewInt(1), + }, + "Metropolis": ¶ms.ChainConfig{ + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + MetropolisBlock: big.NewInt(0), + ChainId: big.NewInt(1), + }, } -func RunStateTest(chainConfig *params.ChainConfig, p string, skipTests []string) error { - tests := make(map[string]VmTest) - if err := readJsonFile(p, &tests); err != nil { - return err - } - - if err := runStateTests(chainConfig, tests, skipTests); err != nil { - return err - } - - return nil - +// 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 } -func BenchStateTest(chainConfig *params.ChainConfig, p string, conf bconf, b *testing.B) error { - tests := make(map[string]VmTest) - if err := readJsonFile(p, &tests); err != nil { - return err +// 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 *[]stLog `json:"logs"` + Indexes struct { + Data int `json:"data"` + Gas int `json:"gas"` + Value int `json:"value"` } - test, ok := tests[conf.name] +} + +//go:generate 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:"required"` + GasLimit *big.Int `json:"currentGasLimit" gencodec:"required"` + Number uint64 `json:"currentNumber" gencodec:"required"` + Timestamp uint64 `json:"currentTimestamp" gencodec:"required"` +} + +type stEnvMarshaling struct { + Coinbase common.UnprefixedAddress + Difficulty *math.HexOrDecimal256 + GasLimit *math.HexOrDecimal256 + Number math.HexOrDecimal64 + Timestamp math.HexOrDecimal64 +} + +//go:generate gencodec -type stTransaction -field-override stTransactionMarshaling -out gen_sttransaction.go + +type stTransaction struct { + GasPrice *big.Int `json:"gasPrice"` + Nonce uint64 `json:"nonce"` + To string `json:"to"` + Data []string `json:"data"` + GasLimit []uint64 `json:"gasLimit"` + Value []string `json:"value"` + PrivateKey []byte `json:"secretKey"` +} + +type stTransactionMarshaling struct { + GasPrice *math.HexOrDecimal256 + Nonce math.HexOrDecimal64 + GasLimit []math.HexOrDecimal64 + PrivateKey hexutil.Bytes +} + +//go:generate gencodec -type stLog -field-override stLogMarshaling -out gen_stlog.go + +type stLog struct { + Address common.Address `json:"address"` + Data []byte `json:"data"` + Topics []common.Hash `json:"topics"` + Bloom string `json:"bloom"` +} + +type stLogMarshaling struct { + Address common.UnprefixedAddress + Data hexutil.Bytes + Topics []common.UnprefixedHash +} + +// 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 +} + +// Run executes a specific subtest. +func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) error { + config, ok := stateTestForks[subtest.Fork] if !ok { - return fmt.Errorf("test not found: %s", conf.name) + return fmt.Errorf("no config for fork %q", subtest.Fork) } - - // XXX Yeah, yeah... - env := make(map[string]string) - env["currentCoinbase"] = test.Env.CurrentCoinbase - env["currentDifficulty"] = test.Env.CurrentDifficulty - env["currentGasLimit"] = test.Env.CurrentGasLimit - env["currentNumber"] = test.Env.CurrentNumber - env["previousHash"] = test.Env.PreviousHash - if n, ok := test.Env.CurrentTimestamp.(float64); ok { - env["currentTimestamp"] = strconv.Itoa(int(n)) - } else { - env["currentTimestamp"] = test.Env.CurrentTimestamp.(string) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchStateTest(chainConfig, test, env, b) - } - - return nil -} - -func benchStateTest(chainConfig *params.ChainConfig, test VmTest, env map[string]string, b *testing.B) { - b.StopTimer() + block, _ := t.genesis(config).ToBlock() db, _ := ethdb.NewMemDatabase() - statedb := makePreState(db, test.Pre) - b.StartTimer() + statedb := makePreState(db, t.json.Pre) - RunState(chainConfig, statedb, db, env, test.Exec) -} - -func runStateTests(chainConfig *params.ChainConfig, tests map[string]VmTest, skipTests []string) error { - skipTest := make(map[string]bool, len(skipTests)) - for _, name := range skipTests { - skipTest[name] = true - } - - for name, test := range tests { - if skipTest[name] /*|| name != "JUMPDEST_Attack"*/ { - log.Info(fmt.Sprint("Skipping state test", name)) - continue - } - - //fmt.Println("StateTest:", name) - if err := runStateTest(chainConfig, test); err != nil { - return fmt.Errorf("%s: %s\n", name, err.Error()) - } - - //log.Info(fmt.Sprint("State test passed: ", name)) - //fmt.Println(string(statedb.Dump())) - } - return nil - -} - -func runStateTest(chainConfig *params.ChainConfig, test VmTest) error { - db, _ := ethdb.NewMemDatabase() - statedb := makePreState(db, test.Pre) - - // XXX Yeah, yeah... - env := make(map[string]string) - env["currentCoinbase"] = test.Env.CurrentCoinbase - env["currentDifficulty"] = test.Env.CurrentDifficulty - env["currentGasLimit"] = test.Env.CurrentGasLimit - env["currentNumber"] = test.Env.CurrentNumber - env["previousHash"] = test.Env.PreviousHash - if n, ok := test.Env.CurrentTimestamp.(float64); ok { - env["currentTimestamp"] = strconv.Itoa(int(n)) - } else { - env["currentTimestamp"] = test.Env.CurrentTimestamp.(string) - } - - ret, logs, root, _ := RunState(chainConfig, statedb, db, env, test.Transaction) - - // Return value: - var rexp []byte - if strings.HasPrefix(test.Out, "#") { - n, _ := strconv.Atoi(test.Out[1:]) - rexp = make([]byte, n) - } else { - rexp = common.FromHex(test.Out) - } - if !bytes.Equal(rexp, ret) { - return fmt.Errorf("return failed. Expected %x, got %x\n", rexp, ret) - } - // Post state content: - for addr, account := range test.Post { - address := common.HexToAddress(addr) - if !statedb.Exist(address) { - return fmt.Errorf("did not find expected post-state account: %s", addr) - } - if balance := statedb.GetBalance(address); balance.Cmp(math.MustParseBig256(account.Balance)) != 0 { - return fmt.Errorf("(%x) balance failed. Expected: %v have: %v\n", address[:4], math.MustParseBig256(account.Balance), balance) - } - if nonce := statedb.GetNonce(address); nonce != math.MustParseUint64(account.Nonce) { - return fmt.Errorf("(%x) nonce failed. Expected: %v have: %v\n", address[:4], account.Nonce, nonce) - } - for addr, value := range account.Storage { - v := statedb.GetState(address, common.HexToHash(addr)) - vexp := common.HexToHash(value) - if v != vexp { - return fmt.Errorf("storage failed:\n%x: %s:\nexpected: %x\nhave: %x\n(%v %v)\n", address[:4], addr, vexp, v, vexp.Big(), v.Big()) - } - } - } - // Root: - if common.HexToHash(test.PostStateRoot) != root { - return fmt.Errorf("Post state root error. Expected: %s have: %x", test.PostStateRoot, root) - } - // Logs: - return checkLogs(test.Logs, logs) -} - -func RunState(chainConfig *params.ChainConfig, statedb *state.StateDB, db ethdb.Database, env, tx map[string]string) ([]byte, []*types.Log, common.Hash, error) { - environment, msg := NewEVMEnvironment(false, chainConfig, statedb, env, tx) - gaspool := new(core.GasPool).AddGas(math.MustParseBig256(env["currentGasLimit"])) - - snapshot := statedb.Snapshot() - ret, _, err := core.ApplyMessage(environment, msg, gaspool) + post := t.json.Post[subtest.Fork][subtest.Index] + msg, err := t.json.Tx.toMessage(post) if err != nil { + return err + } + context := core.NewEVMContext(msg, block.Header(), nil, &t.json.Env.Coinbase) + context.GetHash = vmTestBlockHash + evm := vm.NewEVM(context, statedb, config, vmconfig) + + gaspool := new(core.GasPool) + gaspool.AddGas(block.GasLimit()) + snapshot := statedb.Snapshot() + if _, _, err := core.ApplyMessage(evm, msg, gaspool); err != nil { statedb.RevertToSnapshot(snapshot) } - root, _ := statedb.CommitTo(db, chainConfig.IsEIP158(environment.Context.BlockNumber)) - return ret, statedb.Logs(), root, err + if post.Logs != nil { + if err := checkLogs(statedb.Logs(), *post.Logs); err != nil { + return err + } + } + root, _ := statedb.CommitTo(db, config.IsEIP158(block.Number())) + if root != common.Hash(post.Root) { + return fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root) + } + // TODO(fjl): check return data + return nil +} + +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) *state.StateDB { + sdb := state.NewDatabase(db) + statedb, _ := state.New(common.Hash{}, sdb) + 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.CommitTo(db, false) + statedb, _ = state.New(root, sdb) + return statedb +} + +func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis { + return &core.Genesis{ + Config: config, + Coinbase: t.json.Env.Coinbase, + Difficulty: t.json.Env.Difficulty, + GasLimit: t.json.Env.GasLimit.Uint64(), + Number: t.json.Env.Number, + Timestamp: t.json.Env.Timestamp, + Alloc: t.json.Pre, + } +} + +func (tx *stTransaction) toMessage(ps stPostState) (core.Message, error) { + // Derive sender from private key if present. + var from common.Address + if len(tx.PrivateKey) > 0 { + 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) + } + + msg := types.NewMessage(from, to, tx.Nonce, value, new(big.Int).SetUint64(gasLimit), tx.GasPrice, data, true) + return msg, nil +} + +func checkLogs(have []*types.Log, want []stLog) error { + if len(have) != len(want) { + return fmt.Errorf("logs length mismatch: got %d, want %d", len(have), len(want)) + } + for i := range have { + if have[i].Address != want[i].Address { + return fmt.Errorf("log address %d: got %x, want %x", i, have[i].Address, want[i].Address) + } + if !bytes.Equal(have[i].Data, want[i].Data) { + return fmt.Errorf("log data %d: got %x, want %x", i, have[i].Data, want[i].Data) + } + if !reflect.DeepEqual(have[i].Topics, want[i].Topics) { + return fmt.Errorf("log topics %d:\ngot %x\nwant %x", i, have[i].Topics, want[i].Topics) + } + genBloom := math.PaddedBigBytes(types.LogsBloom([]*types.Log{have[i]}), 256) + var wantBloom types.Bloom + if err := hexutil.UnmarshalFixedUnprefixedText("Bloom", []byte(want[i].Bloom), wantBloom[:]); err != nil { + return fmt.Errorf("test log %d has invalid bloom: %v", i, err) + } + if !bytes.Equal(genBloom, wantBloom[:]) { + return fmt.Errorf("bloom mismatch") + } + } + return nil } diff --git a/tests/testdata b/tests/testdata new file mode 160000 index 0000000000..f1de8c3b7f --- /dev/null +++ b/tests/testdata @@ -0,0 +1 @@ +Subproject commit f1de8c3b7fa2c2c0aa281b6b3a1ad7010356c5ff diff --git a/tests/transaction_test.go b/tests/transaction_test.go index 317355eb34..72d43c0ece 100644 --- a/tests/transaction_test.go +++ b/tests/transaction_test.go @@ -18,109 +18,37 @@ package tests import ( "math/big" - "path/filepath" "testing" "github.com/ethereum/go-ethereum/params" ) -func TestTransactions(t *testing.T) { - config := ¶ms.ChainConfig{} - err := RunTransactionTests(config, filepath.Join(transactionTestDir, "ttTransactionTest.json"), TransSkipTests) - if err != nil { - t.Fatal(err) - } -} +func TestTransaction(t *testing.T) { + t.Parallel() -func TestWrongRLPTransactions(t *testing.T) { - config := ¶ms.ChainConfig{} - err := RunTransactionTests(config, filepath.Join(transactionTestDir, "ttWrongRLPTransaction.json"), TransSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func Test10MBTransactions(t *testing.T) { - config := ¶ms.ChainConfig{} - err := RunTransactionTests(config, filepath.Join(transactionTestDir, "tt10mbDataField.json"), TransSkipTests) - if err != nil { - t.Fatal(err) - } -} - -// homestead tests -func TestHomesteadTransactions(t *testing.T) { - config := ¶ms.ChainConfig{ + txt := new(testMatcher) + txt.config(`^Homestead/`, params.ChainConfig{ HomesteadBlock: big.NewInt(0), - } - err := RunTransactionTests(config, filepath.Join(transactionTestDir, "Homestead", "ttTransactionTest.json"), TransSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestHomesteadWrongRLPTransactions(t *testing.T) { - config := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(0), - } - err := RunTransactionTests(config, filepath.Join(transactionTestDir, "Homestead", "ttWrongRLPTransaction.json"), TransSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestHomestead10MBTransactions(t *testing.T) { - config := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(0), - } - err := RunTransactionTests(config, filepath.Join(transactionTestDir, "Homestead", "tt10mbDataField.json"), TransSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestHomesteadVitalik(t *testing.T) { - config := ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(0), - } - err := RunTransactionTests(config, filepath.Join(transactionTestDir, "Homestead", "ttTransactionTestEip155VitaliksTests.json"), TransSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestTxEIP155Transaction(t *testing.T) { - config := ¶ms.ChainConfig{ - ChainId: big.NewInt(1), + }) + txt.config(`^EIP155/`, params.ChainConfig{ HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), EIP155Block: big.NewInt(0), - } - err := RunTransactionTests(config, filepath.Join(transactionTestDir, "EIP155", "ttTransactionTest.json"), TransSkipTests) - if err != nil { - t.Fatal(err) - } -} - -func TestTxEIP155VitaliksTests(t *testing.T) { - config := ¶ms.ChainConfig{ + EIP158Block: big.NewInt(0), ChainId: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP155Block: big.NewInt(0), - } - err := RunTransactionTests(config, filepath.Join(transactionTestDir, "EIP155", "ttTransactionTestEip155VitaliksTests.json"), TransSkipTests) - if err != nil { - t.Fatal(err) - } -} + }) + txt.config(`^Metropolis/`, params.ChainConfig{ + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + MetropolisBlock: big.NewInt(0), + }) -func TestTxEIP155VRule(t *testing.T) { - config := ¶ms.ChainConfig{ - ChainId: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP155Block: big.NewInt(0), - } - err := RunTransactionTests(config, filepath.Join(transactionTestDir, "EIP155", "ttTransactionTestVRule.json"), TransSkipTests) - if err != nil { - t.Fatal(err) - } + txt.walk(t, transactionTestDir, func(t *testing.T, name string, test *TransactionTest) { + cfg := txt.findConfig(name) + if err := txt.checkFailure(t, name, test.Run(cfg)); err != nil { + t.Error(err) + } + }) } diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go index 1ecc73a67c..472b3d6f24 100644 --- a/tests/transaction_test_util.go +++ b/tests/transaction_test_util.go @@ -20,208 +20,114 @@ import ( "bytes" "errors" "fmt" - "io" - "runtime" + "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" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) -// Transaction Test JSON Format -type TtTransaction struct { - Data string - GasLimit string - GasPrice string - Nonce string - R string - S string - To string - V string - Value string -} - +// TransactionTest checks RLP decoding and sender derivation of transactions. type TransactionTest struct { - Blocknumber string - Rlp string - Sender string - Transaction TtTransaction + json ttJSON } -func RunTransactionTestsWithReader(config *params.ChainConfig, r io.Reader, skipTests []string) error { - skipTest := make(map[string]bool, len(skipTests)) - for _, name := range skipTests { - skipTest[name] = true - } - - bt := make(map[string]TransactionTest) - if err := readJson(r, &bt); err != nil { - return err - } - - for name, test := range bt { - // if the test should be skipped, return - if skipTest[name] { - log.Info(fmt.Sprint("Skipping transaction test", name)) - return nil - } - // test the block - if err := runTransactionTest(config, test); err != nil { - return err - } - log.Info(fmt.Sprint("Transaction test passed: ", name)) - - } - return nil +type ttJSON struct { + BlockNumber math.HexOrDecimal64 `json:"blockNumber"` + RLP hexutil.Bytes `json:"rlp"` + Sender hexutil.Bytes `json:"sender"` + Transaction *ttTransaction `json:"transaction"` } -func RunTransactionTests(config *params.ChainConfig, file string, skipTests []string) error { - tests := make(map[string]TransactionTest) - if err := readJsonFile(file, &tests); err != nil { - return err - } +//go:generate gencodec -type ttTransaction -field-override ttTransactionMarshaling -out gen_tttransaction.go - if err := runTransactionTests(config, tests, skipTests); err != nil { - return err - } - return nil +type ttTransaction struct { + Data []byte `gencodec:"required"` + GasLimit *big.Int `gencodec:"required"` + GasPrice *big.Int `gencodec:"required"` + Nonce uint64 `gencodec:"required"` + Value *big.Int `gencodec:"required"` + R *big.Int `gencodec:"required"` + S *big.Int `gencodec:"required"` + V *big.Int `gencodec:"required"` + To common.Address `gencodec:"required"` } -func runTransactionTests(config *params.ChainConfig, tests map[string]TransactionTest, skipTests []string) error { - skipTest := make(map[string]bool, len(skipTests)) - for _, name := range skipTests { - skipTest[name] = true - } - - for name, test := range tests { - // if the test should be skipped, return - if skipTest[name] { - log.Info(fmt.Sprint("Skipping transaction test", name)) - return nil - } - - // test the block - if err := runTransactionTest(config, test); err != nil { - return fmt.Errorf("%s: %v", name, err) - } - log.Info(fmt.Sprint("Transaction test passed: ", name)) - - } - return nil +type ttTransactionMarshaling struct { + Data hexutil.Bytes + GasLimit *math.HexOrDecimal256 + GasPrice *math.HexOrDecimal256 + Nonce math.HexOrDecimal64 + Value *math.HexOrDecimal256 + R *math.HexOrDecimal256 + S *math.HexOrDecimal256 + V *math.HexOrDecimal256 } -func runTransactionTest(config *params.ChainConfig, txTest TransactionTest) (err error) { +func (tt *TransactionTest) Run(config *params.ChainConfig) error { tx := new(types.Transaction) - err = rlp.DecodeBytes(mustConvertBytes(txTest.Rlp), tx) - - if err != nil { - if txTest.Sender == "" { - // RLP decoding failed and this is expected (test OK) + if err := rlp.DecodeBytes(tt.json.RLP, tx); err != nil { + if tt.json.Transaction == nil { return nil } else { - // RLP decoding failed but is expected to succeed (test FAIL) - return fmt.Errorf("RLP decoding failed when expected to succeed: %s", err) + return fmt.Errorf("RLP decoding failed: %v", err) } } - - validationError := verifyTxFields(config, txTest, tx) - if txTest.Sender == "" { - if validationError != nil { - // RLP decoding works but validation should fail (test OK) - return nil - } else { - // RLP decoding works but validation should fail (test FAIL) - // (this should not be possible but added here for completeness) - return errors.New("Field validations succeeded but should fail") - } - } - - if txTest.Sender != "" { - if validationError == nil { - // RLP decoding works and validations pass (test OK) - return nil - } else { - // RLP decoding works and validations pass (test FAIL) - return fmt.Errorf("Field validations failed after RLP decoding: %s", validationError) - } - } - return errors.New("Should not happen: verify RLP decoding and field validation") -} - -func verifyTxFields(chainConfig *params.ChainConfig, txTest TransactionTest, decodedTx *types.Transaction) (err error) { - defer func() { - if recovered := recover(); recovered != nil { - buf := make([]byte, 64<<10) - buf = buf[:runtime.Stack(buf, false)] - err = fmt.Errorf("%v\n%s", recovered, buf) - } - }() - - var decodedSender common.Address - - signer := types.MakeSigner(chainConfig, math.MustParseBig256(txTest.Blocknumber)) - decodedSender, err = types.Sender(signer, decodedTx) + // Check sender derivation. + signer := types.MakeSigner(config, new(big.Int).SetUint64(uint64(tt.json.BlockNumber))) + sender, err := types.Sender(signer, tx) if err != nil { return err } - - expectedSender := mustConvertAddress(txTest.Sender) - if expectedSender != decodedSender { - return fmt.Errorf("Sender mismatch: %x %x", expectedSender, decodedSender) + if sender != common.BytesToAddress(tt.json.Sender) { + return fmt.Errorf("Sender mismatch: got %x, want %x", sender, tt.json.Sender) } - - expectedData := mustConvertBytes(txTest.Transaction.Data) - if !bytes.Equal(expectedData, decodedTx.Data()) { - return fmt.Errorf("Tx input data mismatch: %#v %#v", expectedData, decodedTx.Data()) + // Check decoded fields. + err = tt.json.Transaction.verify(signer, tx) + if tt.json.Sender == nil && err == nil { + return errors.New("field validations succeeded but should fail") } - - expectedGasLimit := mustConvertBigInt(txTest.Transaction.GasLimit, 16) - if expectedGasLimit.Cmp(decodedTx.Gas()) != 0 { - return fmt.Errorf("GasLimit mismatch: %v %v", expectedGasLimit, decodedTx.Gas()) + if tt.json.Sender != nil && err != nil { + return fmt.Errorf("field validations failed after RLP decoding: %s", err) + } + return nil +} + +func (tt *ttTransaction) verify(signer types.Signer, tx *types.Transaction) error { + if !bytes.Equal(tx.Data(), tt.Data) { + return fmt.Errorf("Tx input data mismatch: got %x want %x", tx.Data(), tt.Data) + } + if tx.Gas().Cmp(tt.GasLimit) != 0 { + return fmt.Errorf("GasLimit mismatch: got %v, want %v", tx.Gas(), tt.GasLimit) + } + if tx.GasPrice().Cmp(tt.GasPrice) != 0 { + return fmt.Errorf("GasPrice mismatch: got %v, want %v", tx.GasPrice(), tt.GasPrice) + } + if tx.Nonce() != tt.Nonce { + return fmt.Errorf("Nonce mismatch: got %v, want %v", tx.Nonce(), tt.Nonce) + } + v, r, s := tx.RawSignatureValues() + if r.Cmp(tt.R) != 0 { + return fmt.Errorf("R mismatch: got %v, want %v", r, tt.R) + } + if s.Cmp(tt.S) != 0 { + return fmt.Errorf("S mismatch: got %v, want %v", s, tt.S) + } + if v.Cmp(tt.V) != 0 { + return fmt.Errorf("V mismatch: got %v, want %v", v, tt.V) + } + if tx.To() == nil { + if tt.To != (common.Address{}) { + return fmt.Errorf("To mismatch when recipient is nil (contract creation): %x", tt.To) + } + } else if *tx.To() != tt.To { + return fmt.Errorf("To mismatch: got %x, want %x", *tx.To(), tt.To) + } + if tx.Value().Cmp(tt.Value) != 0 { + return fmt.Errorf("Value mismatch: got %x, want %x", tx.Value(), tt.Value) } - - expectedGasPrice := mustConvertBigInt(txTest.Transaction.GasPrice, 16) - if expectedGasPrice.Cmp(decodedTx.GasPrice()) != 0 { - return fmt.Errorf("GasPrice mismatch: %v %v", expectedGasPrice, decodedTx.GasPrice()) - } - - expectedNonce := mustConvertUint(txTest.Transaction.Nonce, 16) - if expectedNonce != decodedTx.Nonce() { - return fmt.Errorf("Nonce mismatch: %v %v", expectedNonce, decodedTx.Nonce()) - } - - v, r, s := decodedTx.RawSignatureValues() - expectedR := mustConvertBigInt(txTest.Transaction.R, 16) - if r.Cmp(expectedR) != 0 { - return fmt.Errorf("R mismatch: %v %v", expectedR, r) - } - expectedS := mustConvertBigInt(txTest.Transaction.S, 16) - if s.Cmp(expectedS) != 0 { - return fmt.Errorf("S mismatch: %v %v", expectedS, s) - } - expectedV := mustConvertBigInt(txTest.Transaction.V, 16) - if v.Cmp(expectedV) != 0 { - return fmt.Errorf("V mismatch: %v %v", expectedV, v) - } - - expectedTo := mustConvertAddress(txTest.Transaction.To) - if decodedTx.To() == nil { - if expectedTo != common.BytesToAddress([]byte{}) { // "empty" or "zero" address - return fmt.Errorf("To mismatch when recipient is nil (contract creation): %v", expectedTo) - } - } else { - if expectedTo != *decodedTx.To() { - return fmt.Errorf("To mismatch: %v %v", expectedTo, *decodedTx.To()) - } - } - - expectedValue := mustConvertBigInt(txTest.Transaction.Value, 16) - if expectedValue.Cmp(decodedTx.Value()) != 0 { - return fmt.Errorf("Value mismatch: %v %v", expectedValue, decodedTx.Value()) - } - return nil } diff --git a/tests/util.go b/tests/util.go deleted file mode 100644 index ff02679ec4..0000000000 --- a/tests/util.go +++ /dev/null @@ -1,214 +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 . - -package tests - -import ( - "bytes" - "fmt" - "math/big" - "os" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/core" - "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/crypto" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/params" -) - -var ( - ForceJit bool - EnableJit bool -) - -func init() { - log.Root().SetHandler(log.LvlFilterHandler(log.LvlCrit, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) - if os.Getenv("JITVM") == "true" { - ForceJit = true - EnableJit = true - } -} - -func checkLogs(tlog []Log, logs []*types.Log) error { - if len(tlog) != len(logs) { - return fmt.Errorf("log length mismatch. Expected %d, got %d", len(tlog), len(logs)) - } else { - for i, log := range tlog { - if common.HexToAddress(log.AddressF) != logs[i].Address { - return fmt.Errorf("log address expected %v got %x", log.AddressF, logs[i].Address) - } - - if !bytes.Equal(logs[i].Data, common.FromHex(log.DataF)) { - return fmt.Errorf("log data expected %v got %x", log.DataF, logs[i].Data) - } - - if len(log.TopicsF) != len(logs[i].Topics) { - return fmt.Errorf("log topics length expected %d got %d", len(log.TopicsF), logs[i].Topics) - } else { - for j, topic := range log.TopicsF { - if common.HexToHash(topic) != logs[i].Topics[j] { - return fmt.Errorf("log topic[%d] expected %v got %x", j, topic, logs[i].Topics[j]) - } - } - } - genBloom := math.PaddedBigBytes(types.LogsBloom([]*types.Log{logs[i]}), 256) - - if !bytes.Equal(genBloom, common.Hex2Bytes(log.BloomF)) { - return fmt.Errorf("bloom mismatch") - } - } - } - return nil -} - -type Account struct { - Balance string - Code string - Nonce string - Storage map[string]string -} - -type Log struct { - AddressF string `json:"address"` - DataF string `json:"data"` - TopicsF []string `json:"topics"` - BloomF string `json:"bloom"` -} - -func (self Log) Address() []byte { return common.Hex2Bytes(self.AddressF) } -func (self Log) Data() []byte { return common.Hex2Bytes(self.DataF) } -func (self Log) RlpData() interface{} { return nil } -func (self Log) Topics() [][]byte { - t := make([][]byte, len(self.TopicsF)) - for i, topic := range self.TopicsF { - t[i] = common.Hex2Bytes(topic) - } - return t -} - -func makePreState(db ethdb.Database, accounts map[string]Account) *state.StateDB { - sdb := state.NewDatabase(db) - statedb, _ := state.New(common.Hash{}, sdb) - for addr, account := range accounts { - insertAccount(statedb, addr, account) - } - // Commit and re-open to start with a clean state. - root, _ := statedb.CommitTo(db, false) - statedb, _ = state.New(root, sdb) - return statedb -} - -func insertAccount(state *state.StateDB, saddr string, account Account) { - if common.IsHex(account.Code) { - account.Code = account.Code[2:] - } - addr := common.HexToAddress(saddr) - state.SetCode(addr, common.Hex2Bytes(account.Code)) - state.SetNonce(addr, math.MustParseUint64(account.Nonce)) - state.SetBalance(addr, math.MustParseBig256(account.Balance)) - for a, v := range account.Storage { - state.SetState(addr, common.HexToHash(a), common.HexToHash(v)) - } -} - -type VmEnv struct { - CurrentCoinbase string - CurrentDifficulty string - CurrentGasLimit string - CurrentNumber string - CurrentTimestamp interface{} - PreviousHash string -} - -type VmTest struct { - Callcreates interface{} - //Env map[string]string - Env VmEnv - Exec map[string]string - Transaction map[string]string - Logs []Log - Gas string - Out string - Post map[string]Account - Pre map[string]Account - PostStateRoot string -} - -func NewEVMEnvironment(vmTest bool, chainConfig *params.ChainConfig, statedb *state.StateDB, envValues map[string]string, tx map[string]string) (*vm.EVM, core.Message) { - var ( - data = common.FromHex(tx["data"]) - gas = math.MustParseBig256(tx["gasLimit"]) - price = math.MustParseBig256(tx["gasPrice"]) - value = math.MustParseBig256(tx["value"]) - nonce = math.MustParseUint64(tx["nonce"]) - ) - - origin := common.HexToAddress(tx["caller"]) - if len(tx["secretKey"]) > 0 { - key, _ := crypto.HexToECDSA(tx["secretKey"]) - origin = crypto.PubkeyToAddress(key.PublicKey) - } - - var to *common.Address - if len(tx["to"]) > 2 { - t := common.HexToAddress(tx["to"]) - to = &t - } - - msg := types.NewMessage(origin, to, nonce, value, gas, price, data, true) - - initialCall := true - canTransfer := func(db vm.StateDB, address common.Address, amount *big.Int) bool { - if vmTest { - if initialCall { - initialCall = false - return true - } - } - return core.CanTransfer(db, address, amount) - } - transfer := func(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { - if vmTest { - return - } - core.Transfer(db, sender, recipient, amount) - } - - context := vm.Context{ - CanTransfer: canTransfer, - Transfer: transfer, - GetHash: func(n uint64) common.Hash { - return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String()))) - }, - - Origin: origin, - Coinbase: common.HexToAddress(envValues["currentCoinbase"]), - BlockNumber: math.MustParseBig256(envValues["currentNumber"]), - Time: math.MustParseBig256(envValues["currentTimestamp"]), - GasLimit: math.MustParseBig256(envValues["currentGasLimit"]), - Difficulty: math.MustParseBig256(envValues["currentDifficulty"]), - GasPrice: price, - } - if context.GasPrice == nil { - context.GasPrice = new(big.Int) - } - return vm.NewEVM(context, statedb, chainConfig, vm.Config{NoRecursion: vmTest}), msg -} diff --git a/tests/vm_test.go b/tests/vm_test.go index b546007fb9..5289ba3559 100644 --- a/tests/vm_test.go +++ b/tests/vm_test.go @@ -17,122 +17,21 @@ package tests import ( - "os" - "path/filepath" "testing" + + "github.com/ethereum/go-ethereum/core/vm" ) -func BenchmarkVmAckermann32Tests(b *testing.B) { - fn := filepath.Join(vmTestDir, "vmPerformanceTest.json") - if err := BenchVmTest(fn, bconf{"ackermann32", os.Getenv("JITFORCE") == "true", os.Getenv("JITVM") == "true"}, b); err != nil { - b.Error(err) - } -} +func TestVM(t *testing.T) { + t.Parallel() + vmt := new(testMatcher) + vmt.fails("^vmSystemOperationsTest.json/createNameRegistrator$", "fails without parallel execution") + vmt.skipShortMode("^vmPerformanceTest.json") + vmt.skipShortMode("^vmInputLimits(Light)?.json") -func BenchmarkVmFibonacci16Tests(b *testing.B) { - fn := filepath.Join(vmTestDir, "vmPerformanceTest.json") - if err := BenchVmTest(fn, bconf{"fibonacci16", os.Getenv("JITFORCE") == "true", os.Getenv("JITVM") == "true"}, b); err != nil { - b.Error(err) - } -} - -// I've created a new function for each tests so it's easier to identify where the problem lies if any of them fail. -func TestVmVMArithmetic(t *testing.T) { - fn := filepath.Join(vmTestDir, "vmArithmeticTest.json") - if err := RunVmTest(fn, VmSkipTests); err != nil { - t.Error(err) - } -} - -func TestVmBitwiseLogicOperation(t *testing.T) { - fn := filepath.Join(vmTestDir, "vmBitwiseLogicOperationTest.json") - if err := RunVmTest(fn, VmSkipTests); err != nil { - t.Error(err) - } -} - -func TestVmBlockInfo(t *testing.T) { - fn := filepath.Join(vmTestDir, "vmBlockInfoTest.json") - if err := RunVmTest(fn, VmSkipTests); err != nil { - t.Error(err) - } -} - -func TestVmEnvironmentalInfo(t *testing.T) { - fn := filepath.Join(vmTestDir, "vmEnvironmentalInfoTest.json") - if err := RunVmTest(fn, VmSkipTests); err != nil { - t.Error(err) - } -} - -func TestVmFlowOperation(t *testing.T) { - fn := filepath.Join(vmTestDir, "vmIOandFlowOperationsTest.json") - if err := RunVmTest(fn, VmSkipTests); err != nil { - t.Error(err) - } -} - -func TestVmLogTest(t *testing.T) { - fn := filepath.Join(vmTestDir, "vmLogTest.json") - if err := RunVmTest(fn, VmSkipTests); err != nil { - t.Error(err) - } -} - -func TestVmPerformance(t *testing.T) { - fn := filepath.Join(vmTestDir, "vmPerformanceTest.json") - if err := RunVmTest(fn, VmSkipTests); err != nil { - t.Error(err) - } -} - -func TestVmPushDupSwap(t *testing.T) { - fn := filepath.Join(vmTestDir, "vmPushDupSwapTest.json") - if err := RunVmTest(fn, VmSkipTests); err != nil { - t.Error(err) - } -} - -func TestVmVMSha3(t *testing.T) { - fn := filepath.Join(vmTestDir, "vmSha3Test.json") - if err := RunVmTest(fn, VmSkipTests); err != nil { - t.Error(err) - } -} - -func TestVm(t *testing.T) { - fn := filepath.Join(vmTestDir, "vmtests.json") - if err := RunVmTest(fn, VmSkipTests); err != nil { - t.Error(err) - } -} - -func TestVmLog(t *testing.T) { - fn := filepath.Join(vmTestDir, "vmLogTest.json") - if err := RunVmTest(fn, VmSkipTests); err != nil { - t.Error(err) - } -} - -func TestVmInputLimits(t *testing.T) { - fn := filepath.Join(vmTestDir, "vmInputLimits.json") - if err := RunVmTest(fn, VmSkipTests); err != nil { - t.Error(err) - } -} - -func TestVmInputLimitsLight(t *testing.T) { - fn := filepath.Join(vmTestDir, "vmInputLimitsLight.json") - if err := RunVmTest(fn, VmSkipTests); err != nil { - t.Error(err) - } -} - -func TestVmVMRandom(t *testing.T) { - fns, _ := filepath.Glob(filepath.Join(baseDir, "RandomTests", "*")) - for _, fn := range fns { - if err := RunVmTest(fn, VmSkipTests); err != nil { - t.Error(err) - } - } + vmt.walk(t, vmTestDir, func(t *testing.T, name string, test *VMTest) { + withTrace(t, test.json.Exec.GasLimit, func(vmconfig vm.Config) error { + return vmt.checkFailure(t, name, test.Run(vmconfig)) + }) + }) } diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index e7fe74f49b..afdd896c35 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -18,218 +18,132 @@ package tests import ( "bytes" + "encoding/json" "fmt" - "io" "math/big" - "strconv" - "testing" "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" "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/crypto" "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" ) -func RunVmTestWithReader(r io.Reader, skipTests []string) error { - tests := make(map[string]VmTest) - err := readJson(r, &tests) - if err != nil { - return err - } - - if err != nil { - return err - } - - if err := runVmTests(tests, skipTests); err != nil { - return err - } - - return nil +// VMTest checks EVM execution without block or transaction context. +// See https://github.com/ethereum/tests/wiki/VM-Tests for the test format specification. +type VMTest struct { + json vmJSON } -type bconf struct { - name string - precomp bool - jit bool +func (t *VMTest) UnmarshalJSON(data []byte) error { + return json.Unmarshal(data, &t.json) } -func BenchVmTest(p string, conf bconf, b *testing.B) error { - tests := make(map[string]VmTest) - err := readJsonFile(p, &tests) - if err != nil { - return err - } - - test, ok := tests[conf.name] - if !ok { - return fmt.Errorf("test not found: %s", conf.name) - } - - env := make(map[string]string) - env["currentCoinbase"] = test.Env.CurrentCoinbase - env["currentDifficulty"] = test.Env.CurrentDifficulty - env["currentGasLimit"] = test.Env.CurrentGasLimit - env["currentNumber"] = test.Env.CurrentNumber - env["previousHash"] = test.Env.PreviousHash - if n, ok := test.Env.CurrentTimestamp.(float64); ok { - env["currentTimestamp"] = strconv.Itoa(int(n)) - } else { - env["currentTimestamp"] = test.Env.CurrentTimestamp.(string) - } - - /* - if conf.precomp { - program := vm.NewProgram(test.code) - err := vm.AttachProgram(program) - if err != nil { - return err - } - } - */ - - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchVmTest(test, env, b) - } - - return nil +type vmJSON struct { + Env stEnv `json:"env"` + Exec vmExec `json:"exec"` + Logs []stLog `json:"logs"` + GasRemaining *math.HexOrDecimal64 `json:"gas"` + Out hexutil.Bytes `json:"out"` + Pre core.GenesisAlloc `json:"pre"` + Post core.GenesisAlloc `json:"post"` + PostStateRoot common.Hash `json:"postStateRoot"` } -func benchVmTest(test VmTest, env map[string]string, b *testing.B) { - b.StopTimer() +//go:generate gencodec -type vmExec -field-override vmExecMarshaling -out gen_vmexec.go + +type vmExec struct { + Address common.Address `json:"address" gencodec:"required"` + Caller common.Address `json:"caller" gencodec:"required"` + Origin common.Address `json:"origin" gencodec:"required"` + Code []byte `json:"code" gencodec:"required"` + Data []byte `json:"data" gencodec:"required"` + Value *big.Int `json:"value" gencodec:"required"` + GasLimit uint64 `json:"gas" gencodec:"required"` + GasPrice *big.Int `json:"gasPrice" gencodec:"required"` +} + +type vmExecMarshaling struct { + Address common.UnprefixedAddress + Caller common.UnprefixedAddress + Origin common.UnprefixedAddress + Code hexutil.Bytes + Data hexutil.Bytes + Value *math.HexOrDecimal256 + GasLimit math.HexOrDecimal64 + GasPrice *math.HexOrDecimal256 +} + +func (t *VMTest) Run(vmconfig vm.Config) error { db, _ := ethdb.NewMemDatabase() - statedb := makePreState(db, test.Pre) - b.StartTimer() + statedb := makePreState(db, t.json.Pre) + ret, gasRemaining, err := t.exec(statedb, vmconfig) - RunVm(statedb, env, test.Exec) -} - -func RunVmTest(p string, skipTests []string) error { - tests := make(map[string]VmTest) - err := readJsonFile(p, &tests) - if err != nil { - return err - } - - if err := runVmTests(tests, skipTests); err != nil { - return err - } - - return nil -} - -func runVmTests(tests map[string]VmTest, skipTests []string) error { - skipTest := make(map[string]bool, len(skipTests)) - for _, name := range skipTests { - skipTest[name] = true - } - - for name, test := range tests { - if skipTest[name] /*|| name != "exp0"*/ { - log.Info(fmt.Sprint("Skipping VM test", name)) - continue + if t.json.GasRemaining == nil { + if err == nil { + return fmt.Errorf("gas unspecified (indicating an error), but VM returned no error") } - - if err := runVmTest(test); err != nil { - return fmt.Errorf("%s %s", name, err.Error()) + if gasRemaining > 0 { + return fmt.Errorf("gas unspecified (indicating an error), but VM returned gas remaining > 0") } - - log.Info(fmt.Sprint("VM test passed: ", name)) - //fmt.Println(string(statedb.Dump())) + return nil } - return nil -} - -func runVmTest(test VmTest) error { - db, _ := ethdb.NewMemDatabase() - statedb := makePreState(db, test.Pre) - - // XXX Yeah, yeah... - env := make(map[string]string) - env["currentCoinbase"] = test.Env.CurrentCoinbase - env["currentDifficulty"] = test.Env.CurrentDifficulty - env["currentGasLimit"] = test.Env.CurrentGasLimit - env["currentNumber"] = test.Env.CurrentNumber - env["previousHash"] = test.Env.PreviousHash - if n, ok := test.Env.CurrentTimestamp.(float64); ok { - env["currentTimestamp"] = strconv.Itoa(int(n)) - } else { - env["currentTimestamp"] = test.Env.CurrentTimestamp.(string) + // Test declares gas, expecting outputs to match. + if !bytes.Equal(ret, t.json.Out) { + return fmt.Errorf("return data mismatch: got %x, want %x", ret, t.json.Out) } - - var ( - ret []byte - gas *big.Int - err error - logs []*types.Log - ) - - ret, logs, gas, err = RunVm(statedb, env, test.Exec) - - // Compare expected and actual return - rexp := common.FromHex(test.Out) - if !bytes.Equal(rexp, ret) { - return fmt.Errorf("return failed. Expected %x, got %x\n", rexp, ret) + if gasRemaining != uint64(*t.json.GasRemaining) { + return fmt.Errorf("remaining gas %v, want %v", gasRemaining, *t.json.GasRemaining) } - - // Check gas usage - if len(test.Gas) == 0 && err == nil { - return fmt.Errorf("gas unspecified, indicating an error. VM returned (incorrectly) successful") - } else { - gexp := math.MustParseBig256(test.Gas) - if gexp.Cmp(gas) != 0 { - return fmt.Errorf("gas failed. Expected %v, got %v\n", gexp, gas) - } - } - - // check post state - for address, account := range test.Post { - accountAddr := common.HexToAddress(address) - if !statedb.Exist(accountAddr) { - continue - } - for addr, value := range account.Storage { - v := statedb.GetState(accountAddr, common.HexToHash(addr)) - vexp := common.HexToHash(value) - if v != vexp { - return fmt.Errorf("(%x: %s) storage failed. Expected %x, got %x (%v %v)\n", addr[:4], addr, vexp, v, vexp.Big(), v.Big()) + for addr, account := range t.json.Post { + for k, wantV := range account.Storage { + if haveV := statedb.GetState(addr, k); haveV != wantV { + return fmt.Errorf("wrong storage value at %x:\n got %x\n want %x", k, haveV, wantV) } } } + // if root := statedb.IntermediateRoot(false); root != t.json.PostStateRoot { + // return fmt.Errorf("post state root mismatch, got %x, want %x", root, t.json.PostStateRoot) + // } + return checkLogs(statedb.Logs(), t.json.Logs) +} - // check logs - if len(test.Logs) > 0 { - lerr := checkLogs(test.Logs, logs) - if lerr != nil { - return lerr +func (t *VMTest) exec(statedb *state.StateDB, vmconfig vm.Config) ([]byte, uint64, error) { + evm := t.newEVM(statedb, vmconfig) + e := t.json.Exec + return evm.Call(vm.AccountRef(e.Caller), e.Address, e.Data, e.GasLimit, e.Value) +} + +func (t *VMTest) newEVM(statedb *state.StateDB, vmconfig vm.Config) *vm.EVM { + initialCall := true + canTransfer := func(db vm.StateDB, address common.Address, amount *big.Int) bool { + if initialCall { + initialCall = false + return true } + return core.CanTransfer(db, address, amount) } - - return nil + transfer := func(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {} + context := vm.Context{ + CanTransfer: canTransfer, + Transfer: transfer, + GetHash: vmTestBlockHash, + Origin: t.json.Exec.Origin, + Coinbase: t.json.Env.Coinbase, + BlockNumber: new(big.Int).SetUint64(t.json.Env.Number), + Time: new(big.Int).SetUint64(t.json.Env.Timestamp), + GasLimit: t.json.Env.GasLimit, + Difficulty: t.json.Env.Difficulty, + GasPrice: t.json.Exec.GasPrice, + } + vmconfig.NoRecursion = true + return vm.NewEVM(context, statedb, params.MainnetChainConfig, vmconfig) } -func RunVm(statedb *state.StateDB, env, exec map[string]string) ([]byte, []*types.Log, *big.Int, error) { - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: params.MainnetChainConfig.HomesteadBlock, - DAOForkBlock: params.MainnetChainConfig.DAOForkBlock, - DAOForkSupport: true, - } - var ( - to = common.HexToAddress(exec["address"]) - from = common.HexToAddress(exec["caller"]) - data = common.FromHex(exec["data"]) - gas = math.MustParseBig256(exec["gas"]) - value = math.MustParseBig256(exec["value"]) - ) - caller := statedb.GetOrNewStateObject(from) - vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract) - - environment, _ := NewEVMEnvironment(true, chainConfig, statedb, env, exec) - ret, g, err := environment.Call(caller, to, data, gas.Uint64(), value) - return ret, statedb.Logs(), new(big.Int).SetUint64(g), err +func vmTestBlockHash(n uint64) common.Hash { + return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String()))) }