From 9ac5671c18ae3c36370981cd42a99639f9799042 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Fri, 10 Apr 2015 11:55:31 +0200 Subject: [PATCH] Add TransactionTests wrapped as Go tests * Add initial go wrapping for TransactionTests with some tests disabled in lieu of consistent HEX encodings and a few other pending bugfixes * TODO: Consider better way of perhaps modelling each test in the JSON files as a single Go test, instead of one Go test per JSON file --- tests/blocktest.go | 22 +++++- tests/transaction_test.go | 58 ++++++++++++++ tests/transaction_test_util.go | 134 +++++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 tests/transaction_test.go create mode 100644 tests/transaction_test_util.go diff --git a/tests/blocktest.go b/tests/blocktest.go index 6b8ee9d4a7..84b70678d3 100644 --- a/tests/blocktest.go +++ b/tests/blocktest.go @@ -81,7 +81,7 @@ type BlockTest struct { // 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 { + if err := LoadJSON(file, &bt); err != nil { return nil, err } out := make(map[string]*BlockTest) @@ -250,6 +250,14 @@ func mustConvertBigInt10(in string) *big.Int { return out } +func mustConvertBigIntHex(in string) *big.Int { + out, ok := new(big.Int).SetString(in, 16) + if !ok { + panic(fmt.Errorf("invalid integer: %q", in)) + } + return out +} + func mustConvertUint(in string) uint64 { out, err := strconv.ParseUint(in, 0, 64) if err != nil { @@ -258,8 +266,16 @@ func mustConvertUint(in string) uint64 { return out } -// loadJSON reads the given file and unmarshals its content. -func loadJSON(file string, val interface{}) error { +func mustConvertUintHex(in string) uint64 { + out, err := strconv.ParseUint(in, 16, 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 diff --git a/tests/transaction_test.go b/tests/transaction_test.go new file mode 100644 index 0000000000..5e903e4b41 --- /dev/null +++ b/tests/transaction_test.go @@ -0,0 +1,58 @@ +package tests + +import ( + "testing" +) + +func TestTransactions(t *testing.T) { + notWorking := make(map[string]bool, 100) + // TODO: all commented out tests should work! + + snafus := []string{ + "AddressLessThan20Prefixed0", + "DataTest", + "EmptyTransaction", + "RightVRSTest", + "SenderTest", + "TransactionWithGasLimitxPriceOverflow", + "TransactionWithHihghGas", + "TransactionWithHihghGasPrice", + "TransactionWithHihghNonce", + "TransactionWithHihghValue", + "TransactionWithRvalueWrongSize", + "TransactionWithSvalueHigh", + "TransactionWithSvalueTooHigh", + "TransactionWithSvalueWrongSize", + "ValuesAsDec", + "ValuesAsHex", + "libsecp256k1test", + "unpadedRValue"} + + for _, name := range snafus { + notWorking[name] = true + } + + var err error + err = RunTransactionTests("./files/TransactionTests/ttTransactionTest.json", + notWorking) + if err != nil { + t.Fatal(err) + } +} + +func TestWrongRLPTransactions(t *testing.T) { + notWorking := make(map[string]bool, 100) + // TODO: all commented out tests should work! + notWorking[""] = true + notWorking[""] = true + notWorking[""] = true + notWorking[""] = true + notWorking[""] = true + + var err error + err = RunTransactionTests("./files/TransactionTests/ttWrongRLPTransaction.json", + notWorking) + if err != nil { + t.Fatal(err) + } +} diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go new file mode 100644 index 0000000000..800891887a --- /dev/null +++ b/tests/transaction_test_util.go @@ -0,0 +1,134 @@ +package tests + +import ( + "bytes" + "fmt" + "math/big" + "runtime" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "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 +} + +type TransactionTest struct { + Rlp string + Sender string + Transaction TtTransaction +} + +func RunTransactionTests(file string, notWorking map[string]bool) error { + bt := make(map[string]TransactionTest) + if err := LoadJSON(file, &bt); err != nil { + return err + } + for name, in := range bt { + var err error + // TODO: remove this, we currently ignore some tests which are broken + if !notWorking[name] { + if err = runTest(in); err != nil { + return fmt.Errorf("bad test %s: %v", name, err) + } + fmt.Println("Test passed:", name) + } + } + return nil +} + +func runTest(txTest TransactionTest) (err error) { + expectedSender, expectedTo, expectedData, rlpBytes, expectedGasLimit, expectedGasPrice, expectedValue, expectedR, expectedS, expectedNonce, expectedV, err := convertTestTypes(txTest) + + if err != nil { + if txTest.Sender == "" { // tx is invalid 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) + + 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 expectedTo != *tx.Recipient { + return fmt.Errorf("To mismatch: %v %v", expectedTo, *tx.Recipient) + } + if expectedValue.Cmp(tx.Amount) != 0 { + return fmt.Errorf("Value mismatch: %v %v", expectedValue, tx.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 = mustConvertBigIntHex(txTest.Transaction.GasLimit) + gasPrice = mustConvertBigIntHex(txTest.Transaction.GasPrice) + value = mustConvertBigIntHex(txTest.Transaction.Value) + + r = common.Bytes2Big(mustConvertBytes(txTest.Transaction.R)) + s = common.Bytes2Big(mustConvertBytes(txTest.Transaction.S)) + + nonce = mustConvertUintHex(txTest.Transaction.Nonce) + v = mustConvertUintHex(txTest.Transaction.V) + + return sender, to, txInputData, rlpBytes, gasLimit, gasPrice, value, r, s, nonce, v, nil +}