From 01ec4dbb1251751b8bbf62ddb3b3a02dc50d29fc Mon Sep 17 00:00:00 2001 From: Taylor Gerring Date: Sun, 14 Jun 2015 17:55:03 -0400 Subject: [PATCH] Add stdin option --- cmd/ethtest/main.go | 60 +++++++---- tests/block_test_util.go | 80 ++++++++++---- tests/init.go | 95 ++++++++++------ tests/state_test_util.go | 192 +++++++++++++++++++-------------- tests/transaction_test_util.go | 41 +++++-- tests/vm_test_util.go | 191 +++++++++++++++++++------------- 6 files changed, 422 insertions(+), 237 deletions(-) diff --git a/cmd/ethtest/main.go b/cmd/ethtest/main.go index a78111797..93bf3ce65 100644 --- a/cmd/ethtest/main.go +++ b/cmd/ethtest/main.go @@ -24,6 +24,7 @@ package main import ( "fmt" + "io" "io/ioutil" "os" "path/filepath" @@ -55,28 +56,37 @@ var ( Name: "continue", Usage: "Continue running tests on error (true) or [default] exit immediately (false)", } + ReadStdInFlag = cli.BoolFlag{ + Name: "stdin", + Usage: "Accept input from stdin instead of reading from file", + } ) -func runTest(test, file string) error { - // glog.Infoln("runTest", test, file) +func runTestWithReader(test string, r io.Reader) error { + glog.Infoln("runTest", test) var err error switch test { - case "bc", "BlockTest", "BlockTests", "BlockChainTest": - err = tests.RunBlockTest(file) + case "bt", "BlockTest", "BlockTests", "BlockChainTest": + err = tests.RunBlockTestWithReader(r) case "st", "state", "StateTest", "StateTests": - err = tests.RunStateTest(file) + err = tests.RunStateTestWithReader(r) case "tx", "TransactionTest", "TransactionTests": - err = tests.RunTransactionTests(file) + err = tests.RunTransactionTestsWithReader(r) case "vm", "VMTest", "VMTests": - err = tests.RunVmTest(file) + err = tests.RunVmTestWithReader(r) default: - err = fmt.Errorf("Invalid test type specified:", test) + err = fmt.Errorf("Invalid test type specified: %v", test) } - return err + + if err != nil { + return err + } + + return nil } func getFiles(path string) ([]string, error) { - // glog.Infoln("getFiles", path) + glog.Infoln("getFiles", path) var files []string f, err := os.Open(path) if err != nil { @@ -97,7 +107,7 @@ func getFiles(path string) ([]string, error) { // only go 1 depth and leave directory entires blank if !v.IsDir() && v.Name()[len(v.Name())-len(testExtension):len(v.Name())] == testExtension { files[i] = filepath.Join(path, v.Name()) - // glog.Infoln("Found file", files[i]) + glog.Infoln("Found file", files[i]) } } case mode.IsRegular(): @@ -118,7 +128,7 @@ func runSuite(test, file string) { } for _, curTest := range tests { - // glog.Infoln("runSuite", curTest, file) + glog.Infoln("runSuite", curTest, file) var err error var files []string if test == defaultTest { @@ -134,16 +144,19 @@ func runSuite(test, file string) { if len(files) == 0 { glog.Warningln("No files matched path") } - for _, testfile := range files { + for _, curFile := range files { // Skip blank entries - if len(testfile) == 0 { + if len(curFile) == 0 { continue } - // TODO allow io.Reader to be passed so Stdin can be piped - // RunVmTest(strings.NewReader(os.Args[2])) - // RunVmTest(os.Stdin) - err := runTest(curTest, testfile) + r, err := os.Open(curFile) + if err != nil { + glog.Fatalln(err) + } + defer r.Close() + + err = runTestWithReader(curTest, r) if err != nil { if continueOnError { glog.Errorln(err) @@ -160,8 +173,16 @@ func setupApp(c *cli.Context) { flagTest := c.GlobalString(TestFlag.Name) flagFile := c.GlobalString(FileFlag.Name) continueOnError = c.GlobalBool(ContinueOnErrorFlag.Name) + useStdIn := c.GlobalBool(ReadStdInFlag.Name) - runSuite(flagTest, flagFile) + if !useStdIn { + runSuite(flagTest, flagFile) + } else { + if err := runTestWithReader(flagTest, os.Stdin); err != nil { + glog.Fatalln(err) + } + + } } func main() { @@ -178,6 +199,7 @@ func main() { TestFlag, FileFlag, ContinueOnErrorFlag, + ReadStdInFlag, } if err := app.Run(os.Args); err != nil { diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 21fd07db6..5222b214b 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/hex" "fmt" + "io" "math/big" "path/filepath" "runtime" @@ -85,15 +86,42 @@ type btTransaction struct { Value string } -func RunBlockTest(filepath string) error { - bt, err := LoadBlockTests(filepath) +func RunBlockTestWithReader(r io.Reader) error { + btjs := make(map[string]*btJSON) + if err := readJson(r, &btjs); err != nil { + return err + } + + bt, err := convertBlockTests(btjs) if err != nil { return err } - // map skipped tests to boolean set - skipTest := make(map[string]bool, len(blockSkipTests)) - for _, name := range blockSkipTests { + if err := runBlockTests(bt); err != nil { + return err + } + return nil +} + +func RunBlockTest(file 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(bt); err != nil { + return err + } + return nil +} + +func runBlockTests(bt map[string]*BlockTest) error { + skipTest := make(map[string]bool, len(BlockSkipTests)) + for _, name := range BlockSkipTests { skipTest[name] = true } @@ -103,17 +131,19 @@ func RunBlockTest(filepath string) error { glog.Infoln("Skipping block test", name) return nil } + // test the block - if err := testBlock(test); err != nil { + if err := runBlockTest(test); err != nil { return err } glog.Infoln("Block test passed: ", name) + } return nil -} -func testBlock(test *BlockTest) error { - cfg := testEthConfig() +} +func runBlockTest(test *BlockTest) error { + cfg := test.makeEthConfig() ethereum, err := eth.New(cfg) if err != nil { return err @@ -144,7 +174,7 @@ func testBlock(test *BlockTest) error { return nil } -func testEthConfig() *eth.Config { +func (test *BlockTest) makeEthConfig() *eth.Config { ks := crypto.NewKeyStorePassphrase(filepath.Join(common.DefaultDataDir(), "keystore")) return ð.Config{ @@ -230,7 +260,7 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error { if b.BlockHeader == nil { return fmt.Errorf("Block insertion should have failed") } - err = validateBlockHeader(b.BlockHeader, cb.Header()) + err = t.validateBlockHeader(b.BlockHeader, cb.Header()) if err != nil { return fmt.Errorf("Block header validation failed: ", err) } @@ -238,7 +268,7 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error { return nil } -func validateBlockHeader(h *btHeader, h2 *types.Header) error { +func (s *BlockTest) validateBlockHeader(h *btHeader, h2 *types.Header) error { expectedBloom := mustConvertBytes(h.Bloom) if !bytes.Equal(expectedBloom, h2.Bloom.Bytes()) { return fmt.Errorf("Bloom: expected: %v, decoded: %v", expectedBloom, h2.Bloom.Bytes()) @@ -341,7 +371,18 @@ func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error { return nil } -func convertTest(in *btJSON) (out *BlockTest, err error) { +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. @@ -450,19 +491,12 @@ func mustConvertUint(in string, base int) uint64 { } func LoadBlockTests(file string) (map[string]*BlockTest, error) { - bt := make(map[string]*btJSON) - if err := readTestFile(file, &bt); err != nil { + btjs := make(map[string]*btJSON) + if err := readJsonFile(file, &btjs); err != nil { return nil, err } - out := make(map[string]*BlockTest) - for name, in := range bt { - var err error - if out[name], err = convertTest(in); err != nil { - return out, fmt.Errorf("bad test %q: %v", name, err) - } - } - return out, nil + return convertBlockTests(btjs) } // Nothing to see here, please move along... diff --git a/tests/init.go b/tests/init.go index 164924ab8..326387341 100644 --- a/tests/init.go +++ b/tests/init.go @@ -8,6 +8,8 @@ import ( "net/http" "os" "path/filepath" + + // "github.com/ethereum/go-ethereum/logger/glog" ) var ( @@ -17,13 +19,40 @@ var ( transactionTestDir = filepath.Join(baseDir, "TransactionTests") vmTestDir = filepath.Join(baseDir, "VMTests") - blockSkipTests = []string{"SimpleTx3"} - transSkipTests = []string{"TransactionWithHihghNonce256"} - stateSkipTests = []string{"mload32bitBound_return", "mload32bitBound_return2"} - vmSkipTests = []string{} + BlockSkipTests = []string{"SimpleTx3"} + TransSkipTests = []string{"TransactionWithHihghNonce256"} + StateSkipTests = []string{"mload32bitBound_return", "mload32bitBound_return2"} + VmSkipTests = []string{} ) -func readJSON(reader io.Reader, value interface{}) error { +// type TestRunner interface { +// // LoadTest() +// RunTest() error +// } + +// func RunTests(bt map[string]TestRunner, skipTests []string) error { +// // map skipped tests to boolean set +// skipTest := make(map[string]bool, len(skipTests)) +// for _, name := range skipTests { +// skipTest[name] = true +// } + +// for name, test := range bt { +// // if the test should be skipped, return +// if skipTest[name] { +// glog.Infoln("Skipping block test", name) +// return nil +// } +// // test the block +// if err := test.RunTest(); err != nil { +// return err +// } +// glog.Infoln("Block test passed: ", name) +// } +// return nil +// } + +func readJson(reader io.Reader, value interface{}) error { data, err := ioutil.ReadAll(reader) if err != nil { return fmt.Errorf("Error reading JSON file", err.Error()) @@ -39,6 +68,34 @@ func readJSON(reader io.Reader, value interface{}) error { return nil } +func readJsonHttp(uri string, value interface{}) error { + resp, err := http.Get(uri) + if err != nil { + return err + } + defer resp.Body.Close() + + err = readJson(resp.Body, value) + if err != nil { + 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 @@ -52,31 +109,3 @@ func findLine(data []byte, offset int64) (line int) { } return } - -func readHttpFile(uri string, value interface{}) error { - resp, err := http.Get(uri) - if err != nil { - return err - } - defer resp.Body.Close() - - err = readJSON(resp.Body, value) - if err != nil { - return err - } - return nil -} - -func readTestFile(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 -} diff --git a/tests/state_test_util.go b/tests/state_test_util.go index ad14168c7..ad3aeea6c 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -3,6 +3,7 @@ package tests import ( "bytes" "fmt" + "io" "math/big" "strconv" @@ -15,101 +16,134 @@ import ( "github.com/ethereum/go-ethereum/logger/glog" ) -func RunStateTest(p string) error { - skipTest := make(map[string]bool, len(stateSkipTests)) - for _, name := range stateSkipTests { - skipTest[name] = true +func RunStateTestWithReader(r io.Reader) error { + tests := make(map[string]VmTest) + if err := readJson(r, &tests); err != nil { + return err } + if err := runStateTests(tests); err != nil { + return err + } + + return nil +} + +func RunStateTest(p string) error { tests := make(map[string]VmTest) - readTestFile(p, &tests) + if err := readJsonFile(p, &tests); err != nil { + return err + } + + if err := runStateTests(tests); err != nil { + return err + } + + return nil + +} + +func runStateTests(tests map[string]VmTest) error { + skipTest := make(map[string]bool, len(StateSkipTests)) + for _, name := range StateSkipTests { + skipTest[name] = true + } for name, test := range tests { if skipTest[name] { glog.Infoln("Skipping state test", name) return nil } - db, _ := ethdb.NewMemDatabase() - statedb := state.New(common.Hash{}, db) - for addr, account := range test.Pre { - obj := StateObjectFromAccount(db, addr, account) - statedb.SetStateObject(obj) - for a, v := range account.Storage { - obj.SetState(common.HexToHash(a), common.HexToHash(s)) - } - } - // 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) - } - - var ( - ret []byte - // gas *big.Int - // err error - logs state.Logs - ) - - ret, logs, _, _ = RunState(statedb, env, test.Transaction) - - // // Compare expected and actual return - rexp := common.FromHex(test.Out) - if bytes.Compare(rexp, ret) != 0 { - return fmt.Errorf("%s's return failed. Expected %x, got %x\n", name, rexp, ret) - } - - // check post state - for addr, account := range test.Post { - obj := statedb.GetStateObject(common.HexToAddress(addr)) - if obj == nil { - continue - } - - if obj.Balance().Cmp(common.Big(account.Balance)) != 0 { - return fmt.Errorf("%s's : (%x) balance failed. Expected %v, got %v => %v\n", name, obj.Address().Bytes()[:4], account.Balance, obj.Balance(), new(big.Int).Sub(common.Big(account.Balance), obj.Balance())) - } - - if obj.Nonce() != common.String2Big(account.Nonce).Uint64() { - return fmt.Errorf("%s's : (%x) nonce failed. Expected %v, got %v\n", name, obj.Address().Bytes()[:4], account.Nonce, obj.Nonce()) - } - - for addr, value := range account.Storage { - v := obj.GetState(common.HexToHash(addr)).Bytes() - vexp := common.FromHex(value) - - if bytes.Compare(v, vexp) != 0 { - return fmt.Errorf("%s's : (%x: %s) storage failed. Expected %x, got %x (%v %v)\n", name, obj.Address().Bytes()[0:4], addr, vexp, v, common.BigD(vexp), common.BigD(v)) - } - } - } - - statedb.Sync() - //if !bytes.Equal(common.Hex2Bytes(test.PostStateRoot), statedb.Root()) { - if common.HexToHash(test.PostStateRoot) != statedb.Root() { - return fmt.Errorf("%s's : Post state root error. Expected %s, got %x", name, test.PostStateRoot, statedb.Root()) - } - - // check logs - if len(test.Logs) > 0 { - lerr := checkLogs(test.Logs, logs) - if lerr != nil { - return fmt.Errorf("'%s' ", name, lerr.Error()) - } + if err := runStateTest(test); err != nil { + return fmt.Errorf("%s: %s\n", name, err.Error()) } glog.Infoln("State test passed: ", name) //fmt.Println(string(statedb.Dump())) } return nil + +} + +func runStateTest(test VmTest) error { + db, _ := ethdb.NewMemDatabase() + statedb := state.New(common.Hash{}, db) + for addr, account := range test.Pre { + obj := StateObjectFromAccount(db, addr, account) + statedb.SetStateObject(obj) + for a, v := range account.Storage { + obj.SetState(common.HexToHash(a), common.HexToHash(v)) + } + } + + // 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) + } + + var ( + ret []byte + // gas *big.Int + // err error + logs state.Logs + ) + + ret, logs, _, _ = RunState(statedb, env, test.Transaction) + + // // Compare expected and actual return + rexp := common.FromHex(test.Out) + if bytes.Compare(rexp, ret) != 0 { + return fmt.Errorf("return failed. Expected %x, got %x\n", rexp, ret) + } + + // check post state + for addr, account := range test.Post { + obj := statedb.GetStateObject(common.HexToAddress(addr)) + if obj == nil { + continue + } + + if obj.Balance().Cmp(common.Big(account.Balance)) != 0 { + return fmt.Errorf("(%x) balance failed. Expected %v, got %v => %v\n", obj.Address().Bytes()[:4], account.Balance, obj.Balance(), new(big.Int).Sub(common.Big(account.Balance), obj.Balance())) + } + + if obj.Nonce() != common.String2Big(account.Nonce).Uint64() { + return fmt.Errorf("(%x) nonce failed. Expected %v, got %v\n", obj.Address().Bytes()[:4], account.Nonce, obj.Nonce()) + } + + for addr, value := range account.Storage { + v := obj.GetState(common.HexToHash(addr)).Bytes() + vexp := common.FromHex(value) + + if bytes.Compare(v, vexp) != 0 { + return fmt.Errorf("(%x: %s) storage failed. Expected %x, got %x (%v %v)\n", obj.Address().Bytes()[0:4], addr, vexp, v, common.BigD(vexp), common.BigD(v)) + } + } + } + + statedb.Sync() + //if !bytes.Equal(common.Hex2Bytes(test.PostStateRoot), statedb.Root()) { + if common.HexToHash(test.PostStateRoot) != statedb.Root() { + return fmt.Errorf("Post state root error. Expected %s, got %x", test.PostStateRoot, statedb.Root()) + } + + // check logs + if len(test.Logs) > 0 { + if err := checkLogs(test.Logs, logs); err != nil { + return err + } + } + + return nil } func RunState(statedb *state.StateDB, env, tx map[string]string) ([]byte, state.Logs, *big.Int, error) { diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go index ef133a99d..af33f2c58 100644 --- a/tests/transaction_test_util.go +++ b/tests/transaction_test_util.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + "io" "runtime" "github.com/ethereum/go-ethereum/common" @@ -31,14 +32,14 @@ type TransactionTest struct { Transaction TtTransaction } -func RunTransactionTests(file string) error { - skipTest := make(map[string]bool, len(transSkipTests)) - for _, name := range transSkipTests { +func RunTransactionTestsWithReader(r io.Reader) error { + skipTest := make(map[string]bool, len(TransSkipTests)) + for _, name := range TransSkipTests { skipTest[name] = true } bt := make(map[string]TransactionTest) - if err := readTestFile(file, &bt); err != nil { + if err := readJson(r, &bt); err != nil { return err } @@ -49,7 +50,7 @@ func RunTransactionTests(file string) error { return nil } // test the block - if err := runTest(test); err != nil { + if err := runTransactionTest(test); err != nil { return err } glog.Infoln("Transaction test passed: ", name) @@ -58,7 +59,35 @@ func RunTransactionTests(file string) error { return nil } -func runTest(txTest TransactionTest) (err error) { +func RunTransactionTests(file string) error { + skipTest := make(map[string]bool, len(TransSkipTests)) + for _, name := range TransSkipTests { + skipTest[name] = true + } + + bt := make(map[string]TransactionTest) + if err := readJsonFile(file, &bt); err != nil { + return err + } + + for name, test := range bt { + // if the test should be skipped, return + if skipTest[name] { + glog.Infoln("Skipping transaction test", name) + return nil + } + + // test the block + if err := runTransactionTest(test); err != nil { + return err + } + glog.Infoln("Transaction test passed: ", name) + + } + return nil +} + +func runTransactionTest(txTest TransactionTest) (err error) { tx := new(types.Transaction) err = rlp.DecodeBytes(mustConvertBytes(txTest.Rlp), tx) diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index 4145d1ebf..f7f1198ec 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -3,6 +3,7 @@ package tests import ( "bytes" "fmt" + "io" "math/big" "strconv" @@ -13,94 +14,53 @@ import ( "github.com/ethereum/go-ethereum/logger/glog" ) -func RunVmTest(p string) error { - skipTest := make(map[string]bool, len(vmSkipTests)) - for _, name := range vmSkipTests { - skipTest[name] = true - } - +func RunVmTestWithReader(r io.Reader) error { tests := make(map[string]VmTest) - err := readTestFile(p, &tests) + err := readJson(r, &tests) if err != nil { return err } + if err != nil { + return err + } + + if err := runVmTests(tests); err != nil { + return err + } + + return nil +} + +func RunVmTest(p string) error { + + tests := make(map[string]VmTest) + err := readJsonFile(p, &tests) + if err != nil { + return err + } + + if err := runVmTests(tests); err != nil { + return err + } + + return nil +} + +func runVmTests(tests map[string]VmTest) error { + skipTest := make(map[string]bool, len(VmSkipTests)) + for _, name := range VmSkipTests { + skipTest[name] = true + } + for name, test := range tests { if skipTest[name] { glog.Infoln("Skipping VM test", name) return nil } - db, _ := ethdb.NewMemDatabase() - statedb := state.New(common.Hash{}, db) - for addr, account := range test.Pre { - obj := StateObjectFromAccount(db, addr, account) - statedb.SetStateObject(obj) - for a, v := range account.Storage { - obj.SetState(common.HexToHash(a), common.HexToHash(v)) - } - } - // 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) - } - - var ( - ret []byte - gas *big.Int - err error - logs state.Logs - ) - - ret, logs, gas, err = RunVm(statedb, env, test.Exec) - - // Compare expectedand actual return - rexp := common.FromHex(test.Out) - if bytes.Compare(rexp, ret) != 0 { - return fmt.Errorf("%s's return failed. Expected %x, got %x\n", name, rexp, ret) - } - - // Check gas usage - if len(test.Gas) == 0 && err == nil { - return fmt.Errorf("%s's gas unspecified, indicating an error. VM returned (incorrectly) successfull", name) - } else { - gexp := common.Big(test.Gas) - if gexp.Cmp(gas) != 0 { - return fmt.Errorf("%s's gas failed. Expected %v, got %v\n", name, gexp, gas) - } - } - - // check post state - for addr, account := range test.Post { - obj := statedb.GetStateObject(common.HexToAddress(addr)) - if obj == nil { - continue - } - - for addr, value := range account.Storage { - v := obj.GetState(common.HexToHash(addr)) - vexp := common.HexToHash(value) - - if v != vexp { - return t.Errorf("%s's : (%x: %s) storage failed. Expected %x, got %x (%v %v)\n", name, obj.Address().Bytes()[0:4], addr, vexp, v, vexp.Big(), v.Big()) - } - } - } - - // check logs - if len(test.Logs) > 0 { - lerr := checkLogs(test.Logs, logs) - if lerr != nil { - return fmt.Errorf("'%s' ", name, lerr.Error()) - } + if err := runVmTest(test); err != nil { + return fmt.Errorf("%s %s", name, err.Error()) } glog.Infoln("VM test passed: ", name) @@ -109,6 +69,83 @@ func RunVmTest(p string) error { return nil } +func runVmTest(test VmTest) error { + db, _ := ethdb.NewMemDatabase() + statedb := state.New(common.Hash{}, db) + for addr, account := range test.Pre { + obj := StateObjectFromAccount(db, addr, account) + statedb.SetStateObject(obj) + for a, v := range account.Storage { + obj.SetState(common.HexToHash(a), common.HexToHash(v)) + } + } + + // 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) + } + + var ( + ret []byte + gas *big.Int + err error + logs state.Logs + ) + + ret, logs, gas, err = RunVm(statedb, env, test.Exec) + + // Compare expectedand actual return + rexp := common.FromHex(test.Out) + if bytes.Compare(rexp, ret) != 0 { + return fmt.Errorf("return failed. Expected %x, got %x\n", rexp, ret) + } + + // Check gas usage + if len(test.Gas) == 0 && err == nil { + return fmt.Errorf("gas unspecified, indicating an error. VM returned (incorrectly) successfull") + } else { + gexp := common.Big(test.Gas) + if gexp.Cmp(gas) != 0 { + return fmt.Errorf("gas failed. Expected %v, got %v\n", gexp, gas) + } + } + + // check post state + for addr, account := range test.Post { + obj := statedb.GetStateObject(common.HexToAddress(addr)) + if obj == nil { + continue + } + + for addr, value := range account.Storage { + v := obj.GetState(common.HexToHash(addr)) + vexp := common.HexToHash(value) + + if v != vexp { + return fmt.Errorf("(%x: %s) storage failed. Expected %x, got %x (%v %v)\n", obj.Address().Bytes()[0:4], addr, vexp, v, vexp.BigD(vexp), v.Big(v)) + } + } + } + + // check logs + if len(test.Logs) > 0 { + lerr := checkLogs(test.Logs, logs) + if lerr != nil { + return lerr + } + } + + return nil +} + func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, state.Logs, *big.Int, error) { var ( to = common.HexToAddress(exec["address"])