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()))) }