From 2ce21cefdc0b8f9a4942a4a337b73b3ca131c2cd Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 17 Apr 2015 16:28:44 +0200 Subject: [PATCH 1/4] eth: use NewDB hook also for extra DB (cherry picked from commit d5083033f15bb815a6212eddae16a7125db58738) --- eth/backend.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/eth/backend.go b/eth/backend.go index 264753aba..5798e641a 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -171,7 +171,10 @@ func New(config *Config) (*Ethereum, error) { if err != nil { return nil, err } - extraDb, err := ethdb.NewLDBDatabase(path.Join(config.DataDir, "extra")) + extraDb, err := newdb(path.Join(config.DataDir, "extra")) + if err != nil { + return nil, err + } // Perform database sanity checks d, _ := blockDb.Get([]byte("ProtocolVersion")) From 235ed7ecb92527ce0029c2a6f5ace06707db9b28 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 17 Apr 2015 16:30:15 +0200 Subject: [PATCH 2/4] cmd/geth, tests: enable running multiple tests from a single file This commit also changes the block test loading so tests containing invalid RLP blocks can be loaded and return an error only when they are run. (cherry picked from commit 898ba87984791249586b97c9ce340dd087b79d67) --- cmd/geth/blocktest.go | 89 +++++++++++++++++++++++++++++++------------ tests/blocktest.go | 49 +++++++++++++++++++----- 2 files changed, 105 insertions(+), 33 deletions(-) diff --git a/cmd/geth/blocktest.go b/cmd/geth/blocktest.go index f0b6bb1a2..792981ec0 100644 --- a/cmd/geth/blocktest.go +++ b/cmd/geth/blocktest.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "os" "github.com/codegangsta/cli" "github.com/ethereum/go-ethereum/cmd/utils" @@ -12,7 +13,7 @@ import ( ) var blocktestCmd = cli.Command{ - Action: runblocktest, + Action: runBlockTest, Name: "blocktest", Usage: `loads a block test file`, Description: ` @@ -25,27 +26,78 @@ be able to interact with the chain defined by the test. `, } -func runblocktest(ctx *cli.Context) { - if len(ctx.Args()) != 3 { - utils.Fatalf("Usage: ethereum blocktest {rpc, norpc}") +func runBlockTest(ctx *cli.Context) { + var ( + file, testname string + rpc bool + ) + args := ctx.Args() + switch { + case len(args) == 1: + file = args[0] + case len(args) == 2: + file, testname = args[0], args[1] + case len(args) == 3: + file, testname = args[0], args[1] + rpc = true + default: + utils.Fatalf(`Usage: ethereum blocktest [ [ "rpc" ] ]`) } - file, testname, startrpc := ctx.Args()[0], ctx.Args()[1], ctx.Args()[2] - bt, err := tests.LoadBlockTests(file) if err != nil { utils.Fatalf("%v", err) } + + // run all tests if no test name is specified + if testname == "" { + ecode := 0 + for name, test := range bt { + fmt.Printf("----------------- Running Block Test %q\n", name) + ethereum, err := runOneBlockTest(ctx, test) + if err != nil { + fmt.Println(err) + fmt.Println("FAIL") + ecode = 1 + } + if ethereum != nil { + ethereum.Stop() + ethereum.WaitForShutdown() + } + } + os.Exit(ecode) + return + } + // otherwise, run the given test test, ok := bt[testname] if !ok { utils.Fatalf("Test file does not contain test named %q", testname) } + ethereum, err := runOneBlockTest(ctx, test) + if err != nil { + utils.Fatalf("%v", err) + } + defer ethereum.Stop() + if rpc { + fmt.Println("Block Test post state validated, starting RPC interface.") + startEth(ctx, ethereum) + utils.StartRPC(ethereum, ctx) + ethereum.WaitForShutdown() + } +} +func runOneBlockTest(ctx *cli.Context, test *tests.BlockTest) (*eth.Ethereum, error) { cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx) cfg.NewDB = func(path string) (common.Database, error) { return ethdb.NewMemDatabase() } cfg.MaxPeers = 0 // disable network + cfg.Shh = false // disable whisper + cfg.NAT = nil // disable port mapping + ethereum, err := eth.New(cfg) if err != nil { - utils.Fatalf("%v", err) + return nil, err + } + if err := ethereum.Start(); err != nil { + return nil, err } // import the genesis block @@ -54,27 +106,16 @@ func runblocktest(ctx *cli.Context) { // import pre accounts statedb, err := test.InsertPreState(ethereum.StateDb()) if err != nil { - utils.Fatalf("could not insert genesis accounts: %v", err) + return ethereum, fmt.Errorf("InsertPreState: %v", err) } // insert the test blocks, which will execute all transactions - chain := ethereum.ChainManager() - if err := chain.InsertChain(test.Blocks); err != nil { - utils.Fatalf("Block Test load error: %v %T", err, err) - } else { - fmt.Println("Block Test chain loaded") + if err := test.InsertBlocks(ethereum.ChainManager()); err != nil { + return ethereum, fmt.Errorf("Block Test load error: %v %T", err, err) } - + fmt.Println("chain loaded") if err := test.ValidatePostState(statedb); err != nil { - utils.Fatalf("post state validation failed: %v", err) - } - fmt.Println("Block Test post state validated, starting ethereum.") - - if startrpc == "rpc" { - startEth(ctx, ethereum) - utils.StartRPC(ethereum, ctx) - ethereum.WaitForShutdown() - } else { - startEth(ctx, ethereum) + return ethereum, fmt.Errorf("post state validation failed: %v", err) } + return ethereum, nil } diff --git a/tests/blocktest.go b/tests/blocktest.go index 2d6b11944..37fd9e494 100644 --- a/tests/blocktest.go +++ b/tests/blocktest.go @@ -12,6 +12,7 @@ import ( "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" @@ -73,8 +74,8 @@ type btBlock struct { type BlockTest struct { Genesis *types.Block - Blocks []*types.Block + json *btJSON preAccounts map[string]btAccount } @@ -88,7 +89,7 @@ func LoadBlockTests(file string) (map[string]*BlockTest, error) { for name, in := range bt { var err error if out[name], err = convertTest(in); err != nil { - return nil, fmt.Errorf("bad test %q: %v", err) + return out, fmt.Errorf("bad test %q: %v", name, err) } } return out, nil @@ -124,6 +125,15 @@ func (t *BlockTest) InsertPreState(db common.Database) (*state.StateDB, error) { 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? @@ -149,6 +159,21 @@ func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error { 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) @@ -160,9 +185,8 @@ func convertTest(in *btJSON) (out *BlockTest, err error) { err = fmt.Errorf("%v\n%s", recovered, buf) } }() - out = &BlockTest{preAccounts: in.Pre} + out = &BlockTest{preAccounts: in.Pre, json: in} out.Genesis = mustConvertGenesis(in.GenesisBlockHeader) - out.Blocks = mustConvertBlocks(in.Blocks) return out, err } @@ -203,7 +227,7 @@ func mustConvertBlocks(testBlocks []btBlock) []*types.Block { 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", i, inb.Rlp)) + panic(fmt.Errorf("invalid block %d: %q\nerror: %v", i, inb.Rlp, err)) } out = append(out, &b) } @@ -293,11 +317,18 @@ func findLine(data []byte, offset int64) (line int) { } func unfuckCPPHexInts(s string) string { - if s == "0x" { // no respect for the empty value :( + switch { + case s == "0x": + // no respect for the empty value :( return "0x00" - } - if (len(s) % 2) != 0 { // motherfucking nibbles + case len(s) == 0: + return "0x00" + case len(s) == 1: + return "0x0" + s[:1] + case len(s)%2 != 0: + // motherfucking nibbles return "0x0" + s[2:] + default: + return s } - return s } From c453f1f37093445ba1d9eba5a075169ef0566c19 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 17 Apr 2015 18:20:32 +0200 Subject: [PATCH 3/4] tests: hopefully improve test conversion helpers (cherry picked from commit 035a30acbefb5eeadc1fc8dbd567775d5688f8a9) --- tests/blocktest.go | 55 ++++++++++++++++++---------------- tests/transaction_test_util.go | 10 +++---- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/tests/blocktest.go b/tests/blocktest.go index 37fd9e494..d0a910336 100644 --- a/tests/blocktest.go +++ b/tests/blocktest.go @@ -211,13 +211,13 @@ func mustConvertHeader(in btHeader) *types.Header { UncleHash: mustConvertHash(in.UncleHash), ParentHash: mustConvertHash(in.ParentHash), Extra: mustConvertBytes(in.ExtraData), - GasUsed: mustConvertBigInt(in.GasUsed), - GasLimit: mustConvertBigInt(in.GasLimit), - Difficulty: mustConvertBigInt(in.Difficulty), - Time: mustConvertUint(in.Timestamp), + GasUsed: mustConvertBigInt(in.GasUsed, 10), + GasLimit: mustConvertBigInt(in.GasLimit, 10), + Difficulty: mustConvertBigInt(in.Difficulty, 10), + Time: mustConvertUint(in.Timestamp, 10), } // XXX cheats? :-) - header.SetNonce(common.BytesToHash(mustConvertBytes(in.Nonce)).Big().Uint64()) + header.SetNonce(mustConvertUint(in.Nonce, 16)) return header } @@ -238,7 +238,7 @@ func mustConvertBytes(in string) []byte { if in == "0x" { return []byte{} } - h := strings.TrimPrefix(unfuckCPPHexInts(in), "0x") + h := nibbleFix(strings.TrimPrefix(in, "0x")) out, err := hex.DecodeString(h) if err != nil { panic(fmt.Errorf("invalid hex: %q", h)) @@ -255,7 +255,7 @@ func mustConvertHash(in string) common.Hash { } func mustConvertAddress(in string) common.Address { - out, err := hex.DecodeString(strings.TrimPrefix(in, "0x")) + out, err := hex.DecodeString(nibbleFix(strings.TrimPrefix(in, "0x"))) if err != nil { panic(fmt.Errorf("invalid hex: %q", in)) } @@ -270,16 +270,18 @@ func mustConvertBloom(in string) types.Bloom { return types.BytesToBloom(out) } -func mustConvertBigInt(in string) *big.Int { - out, ok := new(big.Int).SetString(unfuckCPPHexInts(in), 0) +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) uint64 { - out, err := strconv.ParseUint(unfuckCPPHexInts(in), 0, 64) +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)) } @@ -316,19 +318,22 @@ func findLine(data []byte, offset int64) (line int) { return } -func unfuckCPPHexInts(s string) string { - switch { - case s == "0x": - // no respect for the empty value :( - return "0x00" - case len(s) == 0: - return "0x00" - case len(s) == 1: - return "0x0" + s[:1] - case len(s)%2 != 0: - // motherfucking nibbles - return "0x0" + s[2:] - default: - return s +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 567aba66f..d82946e20 100644 --- a/tests/transaction_test_util.go +++ b/tests/transaction_test_util.go @@ -127,15 +127,15 @@ func convertTestTypes(txTest TransactionTest) (sender, to common.Address, txInputData = mustConvertBytes(txTest.Transaction.Data) rlpBytes = mustConvertBytes(txTest.Rlp) - gasLimit = mustConvertBigInt(txTest.Transaction.GasLimit) - gasPrice = mustConvertBigInt(txTest.Transaction.GasPrice) - value = mustConvertBigInt(txTest.Transaction.Value) + 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) - v = mustConvertUint(txTest.Transaction.V) + 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 } From 805345d13594b41e5dd2e4fed471c58c3775be9e Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Sun, 19 Apr 2015 00:35:48 +0200 Subject: [PATCH 4/4] 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 --- cmd/geth/block_go_test.go | 80 +++++++++++ tests/{blocktest.go => block_test_util.go} | 12 +- tests/transaction_test_util.go | 151 ++++++++++++--------- 3 files changed, 174 insertions(+), 69 deletions(-) create mode 100644 cmd/geth/block_go_test.go rename tests/{blocktest.go => block_test_util.go} (96%) diff --git a/cmd/geth/block_go_test.go b/cmd/geth/block_go_test.go new file mode 100644 index 000000000..1980e4798 --- /dev/null +++ b/cmd/geth/block_go_test.go @@ -0,0 +1,80 @@ +package main + +import ( + "path" + "testing" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/tests" +) + +// TODO: refactor test setup & execution to better align with vm and tx tests +// TODO: refactor to avoid duplication with cmd/geth/blocktest.go +func TestBcValidBlockTests(t *testing.T) { + runBlockTestsInFile("../../tests/files/BlockTests/bcValidBlockTest.json", t) +} + +/* +func TestBcUncleTests(t *testing.T) { + runBlockTestsInFile("../../tests/files/BlockTests/bcUncleTest.json", t) +} +*/ + +func runBlockTestsInFile(filepath string, t *testing.T) { + bt, err := tests.LoadBlockTests(filepath) + if err != nil { + t.Fatal(err) + } + for name, test := range bt { + runTest(name, test, t) + } +} + +func runTest(name string, test *tests.BlockTest, t *testing.T) { + t.Log("Running test: ", name) + cfg := testEthConfig() + ethereum, err := eth.New(cfg) + if err != nil { + t.Fatalf("%v", err) + } + + err = ethereum.Start() + if err != nil { + t.Fatalf("%v", err) + } + + // import the genesis block + ethereum.ResetWithGenesisBlock(test.Genesis) + + // import pre accounts + statedb, err := test.InsertPreState(ethereum.StateDb()) + if err != nil { + t.Fatalf("InsertPreState: %v", err) + } + + // insert the test blocks, which will execute all transactions + if err := test.InsertBlocks(ethereum.ChainManager()); err != nil { + t.Fatalf("Block Test load error: %v %T", err, err) + } + + if err := test.ValidatePostState(statedb); err != nil { + t.Fatal("post state validation failed: %v", err) + } + t.Log("Test passed: ", name) +} + +func testEthConfig() *eth.Config { + ks := crypto.NewKeyStorePassphrase(path.Join(common.DefaultDataDir(), "keys")) + + return ð.Config{ + DataDir: common.DefaultDataDir(), + LogLevel: 5, + Etherbase: "primary", + AccountManager: accounts.NewManager(ks), + NewDB: func(path string) (common.Database, error) { return ethdb.NewMemDatabase() }, + } +} diff --git a/tests/blocktest.go b/tests/block_test_util.go similarity index 96% rename from tests/blocktest.go rename to tests/block_test_util.go index d0a910336..5c42ae18c 100644 --- a/tests/blocktest.go +++ b/tests/block_test_util.go @@ -238,10 +238,10 @@ func mustConvertBytes(in string) []byte { if in == "0x" { return []byte{} } - h := nibbleFix(strings.TrimPrefix(in, "0x")) + h := unfuckFuckedHex(strings.TrimPrefix(in, "0x")) out, err := hex.DecodeString(h) if err != nil { - panic(fmt.Errorf("invalid hex: %q", h)) + panic(fmt.Errorf("invalid hex: %q: ", h, err)) } return out } @@ -255,7 +255,7 @@ func mustConvertHash(in string) common.Hash { } func mustConvertAddress(in string) common.Address { - out, err := hex.DecodeString(nibbleFix(strings.TrimPrefix(in, "0x"))) + out, err := hex.DecodeString(strings.TrimPrefix(in, "0x")) if err != nil { panic(fmt.Errorf("invalid hex: %q", in)) } @@ -318,6 +318,7 @@ func findLine(data []byte, offset int64) (line int) { return } +// Nothing to see here, please move along... func prepInt(base int, s string) string { if base == 16 { if strings.HasPrefix(s, "0x") { @@ -331,6 +332,11 @@ func prepInt(base int, s string) string { 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 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,70 +49,44 @@ 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) - } - } - tx := new(types.Transaction) - rlp.DecodeBytes(rlpBytes, tx) - //fmt.Println("HURR tx: %v", tx) - sender, err := tx.From() - if err != nil { - return err - } - - if expectedSender != sender { - return fmt.Errorf("Sender mismatch: %v %v", expectedSender, sender) - } - if !bytes.Equal(expectedData, tx.Payload) { - return fmt.Errorf("Tx input data mismatch: %#v %#v", expectedData, tx.Payload) - } - if expectedGasLimit.Cmp(tx.GasLimit) != 0 { - return fmt.Errorf("GasLimit mismatch: %v %v", expectedGasLimit, tx.GasLimit) - } - if expectedGasPrice.Cmp(tx.Price) != 0 { - return fmt.Errorf("GasPrice mismatch: %v %v", expectedGasPrice, tx.Price) - } - if expectedNonce != tx.AccountNonce { - return fmt.Errorf("Nonce mismatch: %v %v", expectedNonce, tx.AccountNonce) - } - if expectedR.Cmp(tx.R) != 0 { - return fmt.Errorf("R mismatch: %v %v", expectedR, tx.R) - } - if expectedS.Cmp(tx.S) != 0 { - return fmt.Errorf("S mismatch: %v %v", expectedS, tx.S) - } - if expectedV != uint64(tx.V) { - return fmt.Errorf("V mismatch: %v %v", expectedV, uint64(tx.V)) - } - if tx.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) + // RLP decoding failed but is expected to succeed (test FAIL) + return errors.New("RLP decoding failed when expected to succeed") } } - if expectedValue.Cmp(tx.Amount) != 0 { - return fmt.Errorf("Value mismatch: %v %v", expectedValue, tx.Amount) + 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") + } } - return nil + 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 convertTestTypes(txTest TransactionTest) (sender, to common.Address, - txInputData, rlpBytes []byte, - gasLimit, gasPrice, value, r, s *big.Int, - nonce, v uint64, - err error) { - +func verifyTxFields(txTest TransactionTest, decodedTx *types.Transaction) (err error) { defer func() { if recovered := recover(); recovered != nil { buf := make([]byte, 64<<10) @@ -121,21 +95,66 @@ func convertTestTypes(txTest TransactionTest) (sender, to common.Address, } }() - sender = mustConvertAddress(txTest.Sender) - to = mustConvertAddress(txTest.Transaction.To) + decodedSender, err := decodedTx.From() + if err != nil { + return err + } - txInputData = mustConvertBytes(txTest.Transaction.Data) - rlpBytes = mustConvertBytes(txTest.Rlp) + expectedSender := mustConvertAddress(txTest.Sender) + if expectedSender != decodedSender { + return fmt.Errorf("Sender mismatch: %v %v", expectedSender, decodedSender) + } - gasLimit = mustConvertBigInt(txTest.Transaction.GasLimit, 16) - gasPrice = mustConvertBigInt(txTest.Transaction.GasPrice, 16) - value = mustConvertBigInt(txTest.Transaction.Value, 16) + expectedData := mustConvertBytes(txTest.Transaction.Data) + if !bytes.Equal(expectedData, decodedTx.Payload) { + return fmt.Errorf("Tx input data mismatch: %#v %#v", expectedData, decodedTx.Payload) + } - r = common.Bytes2Big(mustConvertBytes(txTest.Transaction.R)) - s = common.Bytes2Big(mustConvertBytes(txTest.Transaction.S)) + expectedGasLimit := mustConvertBigInt(txTest.Transaction.GasLimit, 16) + if expectedGasLimit.Cmp(decodedTx.GasLimit) != 0 { + return fmt.Errorf("GasLimit mismatch: %v %v", expectedGasLimit, decodedTx.GasLimit) + } - nonce = mustConvertUint(txTest.Transaction.Nonce, 16) - v = mustConvertUint(txTest.Transaction.V, 16) + expectedGasPrice := mustConvertBigInt(txTest.Transaction.GasPrice, 16) + if expectedGasPrice.Cmp(decodedTx.Price) != 0 { + return fmt.Errorf("GasPrice mismatch: %v %v", expectedGasPrice, decodedTx.Price) + } - return sender, to, txInputData, rlpBytes, gasLimit, gasPrice, value, r, s, nonce, v, nil + expectedNonce := mustConvertUint(txTest.Transaction.Nonce, 16) + if expectedNonce != decodedTx.AccountNonce { + return fmt.Errorf("Nonce mismatch: %v %v", expectedNonce, decodedTx.AccountNonce) + } + + expectedR := common.Bytes2Big(mustConvertBytes(txTest.Transaction.R)) + if expectedR.Cmp(decodedTx.R) != 0 { + return fmt.Errorf("R mismatch: %v %v", expectedR, decodedTx.R) + } + + expectedS := common.Bytes2Big(mustConvertBytes(txTest.Transaction.S)) + if expectedS.Cmp(decodedTx.S) != 0 { + return fmt.Errorf("S mismatch: %v %v", expectedS, decodedTx.S) + } + + expectedV := mustConvertUint(txTest.Transaction.V, 16) + if expectedV != uint64(decodedTx.V) { + return fmt.Errorf("V mismatch: %v %v", expectedV, uint64(decodedTx.V)) + } + + 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 != *decodedTx.Recipient { + return fmt.Errorf("To mismatch: %v %v", expectedTo, *decodedTx.Recipient) + } + } + + expectedValue := mustConvertBigInt(txTest.Transaction.Value, 16) + if expectedValue.Cmp(decodedTx.Amount) != 0 { + return fmt.Errorf("Value mismatch: %v %v", expectedValue, decodedTx.Amount) + } + + return nil }