From 805345d13594b41e5dd2e4fed471c58c3775be9e Mon Sep 17 00:00:00 2001
From: Gustav Simonsson <gustav.simonsson@gmail.com>
Date: Sun, 19 Apr 2015 00:35:48 +0200
Subject: Add block tests wrapper and fixes for tx tests

* Add fixes to parsing and converting of fields in tx tests
* Correct logic in tx tests; validation of fields and correct
  logic for when RLP decoding works/fails and when this is
  expected or not
* Rename files for consistency
* Add block tests wrapper to run block tests with go test
---
 tests/block_test_util.go       | 345 +++++++++++++++++++++++++++++++++++++++++
 tests/blocktest.go             | 339 ----------------------------------------
 tests/transaction_test_util.go | 143 +++++++++--------
 3 files changed, 426 insertions(+), 401 deletions(-)
 create mode 100644 tests/block_test_util.go
 delete mode 100644 tests/blocktest.go

(limited to 'tests')

diff --git a/tests/block_test_util.go b/tests/block_test_util.go
new file mode 100644
index 000000000..5c42ae18c
--- /dev/null
+++ b/tests/block_test_util.go
@@ -0,0 +1,345 @@
+package tests
+
+import (
+	"bytes"
+	"encoding/hex"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"math/big"
+	"runtime"
+	"strconv"
+	"strings"
+
+	"github.com/ethereum/go-ethereum/common"
+	"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/rlp"
+)
+
+// Block Test JSON Format
+type btJSON struct {
+	Blocks             []btBlock
+	GenesisBlockHeader btHeader
+	Pre                map[string]btAccount
+	PostState          map[string]btAccount
+}
+
+type btAccount struct {
+	Balance string
+	Code    string
+	Nonce   string
+	Storage map[string]string
+}
+
+type btHeader struct {
+	Bloom            string
+	Coinbase         string
+	MixHash          string
+	Nonce            string
+	Number           string
+	ParentHash       string
+	ReceiptTrie      string
+	SeedHash         string
+	StateRoot        string
+	TransactionsTrie string
+	UncleHash        string
+
+	ExtraData  string
+	Difficulty string
+	GasLimit   string
+	GasUsed    string
+	Timestamp  string
+}
+
+type btTransaction struct {
+	Data     string
+	GasLimit string
+	GasPrice string
+	Nonce    string
+	R        string
+	S        string
+	To       string
+	V        string
+	Value    string
+}
+
+type btBlock struct {
+	BlockHeader  *btHeader
+	Rlp          string
+	Transactions []btTransaction
+	UncleHeaders []*btHeader
+}
+
+type BlockTest struct {
+	Genesis *types.Block
+
+	json        *btJSON
+	preAccounts map[string]btAccount
+}
+
+// LoadBlockTests loads a block test JSON file.
+func LoadBlockTests(file string) (map[string]*BlockTest, error) {
+	bt := make(map[string]*btJSON)
+	if err := LoadJSON(file, &bt); 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
+}
+
+// InsertPreState populates the given database with the genesis
+// accounts defined by the test.
+func (t *BlockTest) InsertPreState(db common.Database) (*state.StateDB, error) {
+	statedb := state.New(common.Hash{}, db)
+	for addrString, acct := range t.preAccounts {
+		// XXX: is is worth it checking for errors here?
+		//addr, _ := hex.DecodeString(addrString)
+		code, _ := hex.DecodeString(strings.TrimPrefix(acct.Code, "0x"))
+		balance, _ := new(big.Int).SetString(acct.Balance, 0)
+		nonce, _ := strconv.ParseUint(acct.Nonce, 16, 64)
+
+		obj := statedb.CreateAccount(common.HexToAddress(addrString))
+		obj.SetCode(code)
+		obj.SetBalance(balance)
+		obj.SetNonce(nonce)
+		for k, v := range acct.Storage {
+			statedb.SetState(common.HexToAddress(addrString), common.HexToHash(k), common.FromHex(v))
+		}
+	}
+	// sync objects to trie
+	statedb.Update()
+	// sync trie to disk
+	statedb.Sync()
+
+	if !bytes.Equal(t.Genesis.Root().Bytes(), statedb.Root().Bytes()) {
+		return nil, fmt.Errorf("computed state root does not match genesis block %x %x", t.Genesis.Root().Bytes()[:4], statedb.Root().Bytes()[:4])
+	}
+	return statedb, nil
+}
+
+// InsertBlocks loads the test's blocks into the given chain.
+func (t *BlockTest) InsertBlocks(chain *core.ChainManager) error {
+	blocks, err := t.convertBlocks()
+	if err != nil {
+		return err
+	}
+	return chain.InsertChain(blocks)
+}
+
+func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error {
+	for addrString, acct := range t.preAccounts {
+		// XXX: is is worth it checking for errors here?
+		addr, _ := hex.DecodeString(addrString)
+		code, _ := hex.DecodeString(strings.TrimPrefix(acct.Code, "0x"))
+		balance, _ := new(big.Int).SetString(acct.Balance, 0)
+		nonce, _ := strconv.ParseUint(acct.Nonce, 16, 64)
+
+		// 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, addr, found, expected: ", addrString, hex.EncodeToString(code2), hex.EncodeToString(code))
+		}
+		if balance2.Cmp(balance) != 0 {
+			return fmt.Errorf("account balance mismatch, addr, found, expected: ", addrString, balance2, balance)
+		}
+		if nonce2 != nonce {
+			return fmt.Errorf("account nonce mismatch, addr, found, expected: ", addrString, nonce2, nonce)
+		}
+	}
+	return nil
+}
+
+func (t *BlockTest) convertBlocks() (blocks []*types.Block, 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)
+		}
+	}()
+	blocks = mustConvertBlocks(t.json.Blocks)
+	return blocks, nil
+}
+
+func convertTest(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, json: in}
+	out.Genesis = mustConvertGenesis(in.GenesisBlockHeader)
+	return out, err
+}
+
+func mustConvertGenesis(testGenesis btHeader) *types.Block {
+	hdr := mustConvertHeader(testGenesis)
+	hdr.Number = big.NewInt(0)
+	b := types.NewBlockWithHeader(hdr)
+	b.Td = new(big.Int)
+	return b
+}
+
+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, 10),
+		GasLimit:    mustConvertBigInt(in.GasLimit, 10),
+		Difficulty:  mustConvertBigInt(in.Difficulty, 10),
+		Time:        mustConvertUint(in.Timestamp, 10),
+	}
+	// XXX cheats? :-)
+	header.SetNonce(mustConvertUint(in.Nonce, 16))
+	return header
+}
+
+func mustConvertBlocks(testBlocks []btBlock) []*types.Block {
+	var out []*types.Block
+	for i, inb := range testBlocks {
+		var b types.Block
+		r := bytes.NewReader(mustConvertBytes(inb.Rlp))
+		if err := rlp.Decode(r, &b); err != nil {
+			panic(fmt.Errorf("invalid block %d: %q\nerror: %v", i, inb.Rlp, err))
+		}
+		out = append(out, &b)
+	}
+	return out
+}
+
+func mustConvertBytes(in string) []byte {
+	if in == "0x" {
+		return []byte{}
+	}
+	h := unfuckFuckedHex(strings.TrimPrefix(in, "0x"))
+	out, err := hex.DecodeString(h)
+	if err != nil {
+		panic(fmt.Errorf("invalid hex: %q: ", h, err))
+	}
+	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
+}
+
+// LoadJSON reads the given file and unmarshals its content.
+func LoadJSON(file string, val interface{}) error {
+	content, err := ioutil.ReadFile(file)
+	if err != nil {
+		return err
+	}
+	if err := json.Unmarshal(content, val); err != nil {
+		if syntaxerr, ok := err.(*json.SyntaxError); ok {
+			line := findLine(content, syntaxerr.Offset)
+			return fmt.Errorf("JSON syntax error at %v:%v: %v", file, line, err)
+		}
+		return fmt.Errorf("JSON unmarshal error in %v: %v", file, err)
+	}
+	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
+}
+
+// Nothing to see here, please move along...
+func prepInt(base int, s string) string {
+	if base == 16 {
+		if strings.HasPrefix(s, "0x") {
+			s = s[2:]
+		}
+		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
+}
diff --git a/tests/blocktest.go b/tests/blocktest.go
deleted file mode 100644
index d0a910336..000000000
--- a/tests/blocktest.go
+++ /dev/null
@@ -1,339 +0,0 @@
-package tests
-
-import (
-	"bytes"
-	"encoding/hex"
-	"encoding/json"
-	"fmt"
-	"io/ioutil"
-	"math/big"
-	"runtime"
-	"strconv"
-	"strings"
-
-	"github.com/ethereum/go-ethereum/common"
-	"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/rlp"
-)
-
-// Block Test JSON Format
-type btJSON struct {
-	Blocks             []btBlock
-	GenesisBlockHeader btHeader
-	Pre                map[string]btAccount
-	PostState          map[string]btAccount
-}
-
-type btAccount struct {
-	Balance string
-	Code    string
-	Nonce   string
-	Storage map[string]string
-}
-
-type btHeader struct {
-	Bloom            string
-	Coinbase         string
-	MixHash          string
-	Nonce            string
-	Number           string
-	ParentHash       string
-	ReceiptTrie      string
-	SeedHash         string
-	StateRoot        string
-	TransactionsTrie string
-	UncleHash        string
-
-	ExtraData  string
-	Difficulty string
-	GasLimit   string
-	GasUsed    string
-	Timestamp  string
-}
-
-type btTransaction struct {
-	Data     string
-	GasLimit string
-	GasPrice string
-	Nonce    string
-	R        string
-	S        string
-	To       string
-	V        string
-	Value    string
-}
-
-type btBlock struct {
-	BlockHeader  *btHeader
-	Rlp          string
-	Transactions []btTransaction
-	UncleHeaders []*btHeader
-}
-
-type BlockTest struct {
-	Genesis *types.Block
-
-	json        *btJSON
-	preAccounts map[string]btAccount
-}
-
-// LoadBlockTests loads a block test JSON file.
-func LoadBlockTests(file string) (map[string]*BlockTest, error) {
-	bt := make(map[string]*btJSON)
-	if err := LoadJSON(file, &bt); 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
-}
-
-// InsertPreState populates the given database with the genesis
-// accounts defined by the test.
-func (t *BlockTest) InsertPreState(db common.Database) (*state.StateDB, error) {
-	statedb := state.New(common.Hash{}, db)
-	for addrString, acct := range t.preAccounts {
-		// XXX: is is worth it checking for errors here?
-		//addr, _ := hex.DecodeString(addrString)
-		code, _ := hex.DecodeString(strings.TrimPrefix(acct.Code, "0x"))
-		balance, _ := new(big.Int).SetString(acct.Balance, 0)
-		nonce, _ := strconv.ParseUint(acct.Nonce, 16, 64)
-
-		obj := statedb.CreateAccount(common.HexToAddress(addrString))
-		obj.SetCode(code)
-		obj.SetBalance(balance)
-		obj.SetNonce(nonce)
-		for k, v := range acct.Storage {
-			statedb.SetState(common.HexToAddress(addrString), common.HexToHash(k), common.FromHex(v))
-		}
-	}
-	// sync objects to trie
-	statedb.Update()
-	// sync trie to disk
-	statedb.Sync()
-
-	if !bytes.Equal(t.Genesis.Root().Bytes(), statedb.Root().Bytes()) {
-		return nil, fmt.Errorf("computed state root does not match genesis block %x %x", t.Genesis.Root().Bytes()[:4], statedb.Root().Bytes()[:4])
-	}
-	return statedb, nil
-}
-
-// InsertBlocks loads the test's blocks into the given chain.
-func (t *BlockTest) InsertBlocks(chain *core.ChainManager) error {
-	blocks, err := t.convertBlocks()
-	if err != nil {
-		return err
-	}
-	return chain.InsertChain(blocks)
-}
-
-func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error {
-	for addrString, acct := range t.preAccounts {
-		// XXX: is is worth it checking for errors here?
-		addr, _ := hex.DecodeString(addrString)
-		code, _ := hex.DecodeString(strings.TrimPrefix(acct.Code, "0x"))
-		balance, _ := new(big.Int).SetString(acct.Balance, 0)
-		nonce, _ := strconv.ParseUint(acct.Nonce, 16, 64)
-
-		// 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, addr, found, expected: ", addrString, hex.EncodeToString(code2), hex.EncodeToString(code))
-		}
-		if balance2.Cmp(balance) != 0 {
-			return fmt.Errorf("account balance mismatch, addr, found, expected: ", addrString, balance2, balance)
-		}
-		if nonce2 != nonce {
-			return fmt.Errorf("account nonce mismatch, addr, found, expected: ", addrString, nonce2, nonce)
-		}
-	}
-	return nil
-}
-
-func (t *BlockTest) convertBlocks() (blocks []*types.Block, 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)
-		}
-	}()
-	blocks = mustConvertBlocks(t.json.Blocks)
-	return blocks, nil
-}
-
-func convertTest(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, json: in}
-	out.Genesis = mustConvertGenesis(in.GenesisBlockHeader)
-	return out, err
-}
-
-func mustConvertGenesis(testGenesis btHeader) *types.Block {
-	hdr := mustConvertHeader(testGenesis)
-	hdr.Number = big.NewInt(0)
-	b := types.NewBlockWithHeader(hdr)
-	b.Td = new(big.Int)
-	return b
-}
-
-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, 10),
-		GasLimit:    mustConvertBigInt(in.GasLimit, 10),
-		Difficulty:  mustConvertBigInt(in.Difficulty, 10),
-		Time:        mustConvertUint(in.Timestamp, 10),
-	}
-	// XXX cheats? :-)
-	header.SetNonce(mustConvertUint(in.Nonce, 16))
-	return header
-}
-
-func mustConvertBlocks(testBlocks []btBlock) []*types.Block {
-	var out []*types.Block
-	for i, inb := range testBlocks {
-		var b types.Block
-		r := bytes.NewReader(mustConvertBytes(inb.Rlp))
-		if err := rlp.Decode(r, &b); err != nil {
-			panic(fmt.Errorf("invalid block %d: %q\nerror: %v", i, inb.Rlp, err))
-		}
-		out = append(out, &b)
-	}
-	return out
-}
-
-func mustConvertBytes(in string) []byte {
-	if in == "0x" {
-		return []byte{}
-	}
-	h := nibbleFix(strings.TrimPrefix(in, "0x"))
-	out, err := hex.DecodeString(h)
-	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(nibbleFix(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
-}
-
-// LoadJSON reads the given file and unmarshals its content.
-func LoadJSON(file string, val interface{}) error {
-	content, err := ioutil.ReadFile(file)
-	if err != nil {
-		return err
-	}
-	if err := json.Unmarshal(content, val); err != nil {
-		if syntaxerr, ok := err.(*json.SyntaxError); ok {
-			line := findLine(content, syntaxerr.Offset)
-			return fmt.Errorf("JSON syntax error at %v:%v: %v", file, line, err)
-		}
-		return fmt.Errorf("JSON unmarshal error in %v: %v", file, err)
-	}
-	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
-}
-
-func prepInt(base int, s string) string {
-	if base == 16 {
-		if strings.HasPrefix(s, "0x") {
-			s = s[2:]
-		}
-		if len(s) == 0 {
-			s = "00"
-		}
-		s = nibbleFix(s)
-	}
-	return s
-}
-
-func nibbleFix(s string) string {
-	if len(s)%2 != 0 {
-		s = "0" + s
-	}
-	return s
-}
diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go
index d82946e20..23ec375e3 100644
--- a/tests/transaction_test_util.go
+++ b/tests/transaction_test_util.go
@@ -2,8 +2,8 @@ package tests
 
 import (
 	"bytes"
+	"errors"
 	"fmt"
-	"math/big"
 	"runtime"
 
 	"github.com/ethereum/go-ethereum/common"
@@ -49,93 +49,112 @@ func RunTransactionTests(file string, notWorking map[string]bool) error {
 }
 
 func runTest(txTest TransactionTest) (err error) {
-	expectedSender, expectedTo, expectedData, rlpBytes, expectedGasLimit, expectedGasPrice, expectedValue, expectedR, expectedS, expectedNonce, expectedV, err := convertTestTypes(txTest)
+	tx := new(types.Transaction)
+	err = rlp.DecodeBytes(mustConvertBytes(txTest.Rlp), tx)
 
 	if err != nil {
-		if txTest.Sender == "" { // tx is invalid and this is expected (test OK)
+		if txTest.Sender == "" {
+			// RLP decoding failed and this is expected (test OK)
 			return nil
 		} else {
-			return err // tx is invalid and this is NOT expected (test FAIL)
+			// RLP decoding failed but is expected to succeed (test FAIL)
+			return errors.New("RLP decoding failed when expected to succeed")
 		}
 	}
-	tx := new(types.Transaction)
-	rlp.DecodeBytes(rlpBytes, tx)
-	//fmt.Println("HURR tx: %v", tx)
-	sender, err := tx.From()
+
+	validationError := verifyTxFields(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 errors.New("Field validations failed after RLP decoding")
+		}
+	}
+	return errors.New("Should not happen: verify RLP decoding and field validation")
+}
+
+func verifyTxFields(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)
+		}
+	}()
+
+	decodedSender, err := decodedTx.From()
 	if err != nil {
 		return err
 	}
 
-	if expectedSender != sender {
-		return fmt.Errorf("Sender mismatch: %v %v", expectedSender, sender)
+	expectedSender := mustConvertAddress(txTest.Sender)
+	if expectedSender != decodedSender {
+		return fmt.Errorf("Sender mismatch: %v %v", expectedSender, decodedSender)
 	}
-	if !bytes.Equal(expectedData, tx.Payload) {
-		return fmt.Errorf("Tx input data mismatch: %#v %#v", expectedData, tx.Payload)
+
+	expectedData := mustConvertBytes(txTest.Transaction.Data)
+	if !bytes.Equal(expectedData, decodedTx.Payload) {
+		return fmt.Errorf("Tx input data mismatch: %#v %#v", expectedData, decodedTx.Payload)
 	}
-	if expectedGasLimit.Cmp(tx.GasLimit) != 0 {
-		return fmt.Errorf("GasLimit mismatch: %v %v", expectedGasLimit, tx.GasLimit)
+
+	expectedGasLimit := mustConvertBigInt(txTest.Transaction.GasLimit, 16)
+	if expectedGasLimit.Cmp(decodedTx.GasLimit) != 0 {
+		return fmt.Errorf("GasLimit mismatch: %v %v", expectedGasLimit, decodedTx.GasLimit)
 	}
-	if expectedGasPrice.Cmp(tx.Price) != 0 {
-		return fmt.Errorf("GasPrice mismatch: %v %v", expectedGasPrice, tx.Price)
+
+	expectedGasPrice := mustConvertBigInt(txTest.Transaction.GasPrice, 16)
+	if expectedGasPrice.Cmp(decodedTx.Price) != 0 {
+		return fmt.Errorf("GasPrice mismatch: %v %v", expectedGasPrice, decodedTx.Price)
 	}
-	if expectedNonce != tx.AccountNonce {
-		return fmt.Errorf("Nonce mismatch: %v %v", expectedNonce, tx.AccountNonce)
+
+	expectedNonce := mustConvertUint(txTest.Transaction.Nonce, 16)
+	if expectedNonce != decodedTx.AccountNonce {
+		return fmt.Errorf("Nonce mismatch: %v %v", expectedNonce, decodedTx.AccountNonce)
 	}
-	if expectedR.Cmp(tx.R) != 0 {
-		return fmt.Errorf("R mismatch: %v %v", expectedR, tx.R)
+
+	expectedR := common.Bytes2Big(mustConvertBytes(txTest.Transaction.R))
+	if expectedR.Cmp(decodedTx.R) != 0 {
+		return fmt.Errorf("R mismatch: %v %v", expectedR, decodedTx.R)
 	}
-	if expectedS.Cmp(tx.S) != 0 {
-		return fmt.Errorf("S mismatch: %v %v", expectedS, tx.S)
+
+	expectedS := common.Bytes2Big(mustConvertBytes(txTest.Transaction.S))
+	if expectedS.Cmp(decodedTx.S) != 0 {
+		return fmt.Errorf("S mismatch: %v %v", expectedS, decodedTx.S)
 	}
-	if expectedV != uint64(tx.V) {
-		return fmt.Errorf("V mismatch: %v %v", expectedV, uint64(tx.V))
+
+	expectedV := mustConvertUint(txTest.Transaction.V, 16)
+	if expectedV != uint64(decodedTx.V) {
+		return fmt.Errorf("V mismatch: %v %v", expectedV, uint64(decodedTx.V))
 	}
-	if tx.Recipient == nil {
+
+	expectedTo := mustConvertAddress(txTest.Transaction.To)
+	if decodedTx.Recipient == 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 != *tx.Recipient {
-			return fmt.Errorf("To mismatch: %v %v", expectedTo, *tx.Recipient)
+		if expectedTo != *decodedTx.Recipient {
+			return fmt.Errorf("To mismatch: %v %v", expectedTo, *decodedTx.Recipient)
 		}
 	}
 
-	if expectedValue.Cmp(tx.Amount) != 0 {
-		return fmt.Errorf("Value mismatch: %v %v", expectedValue, tx.Amount)
+	expectedValue := mustConvertBigInt(txTest.Transaction.Value, 16)
+	if expectedValue.Cmp(decodedTx.Amount) != 0 {
+		return fmt.Errorf("Value mismatch: %v %v", expectedValue, decodedTx.Amount)
 	}
 
 	return nil
 }
-
-func convertTestTypes(txTest TransactionTest) (sender, to common.Address,
-	txInputData, rlpBytes []byte,
-	gasLimit, gasPrice, value, r, s *big.Int,
-	nonce, v uint64,
-	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)
-		}
-	}()
-
-	sender = mustConvertAddress(txTest.Sender)
-	to = mustConvertAddress(txTest.Transaction.To)
-
-	txInputData = mustConvertBytes(txTest.Transaction.Data)
-	rlpBytes = mustConvertBytes(txTest.Rlp)
-
-	gasLimit = mustConvertBigInt(txTest.Transaction.GasLimit, 16)
-	gasPrice = mustConvertBigInt(txTest.Transaction.GasPrice, 16)
-	value = mustConvertBigInt(txTest.Transaction.Value, 16)
-
-	r = common.Bytes2Big(mustConvertBytes(txTest.Transaction.R))
-	s = common.Bytes2Big(mustConvertBytes(txTest.Transaction.S))
-
-	nonce = mustConvertUint(txTest.Transaction.Nonce, 16)
-	v = mustConvertUint(txTest.Transaction.V, 16)
-
-	return sender, to, txInputData, rlpBytes, gasLimit, gasPrice, value, r, s, nonce, v, nil
-}
-- 
cgit v1.2.3