From 235ed7ecb92527ce0029c2a6f5ace06707db9b28 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 17 Apr 2015 16:30:15 +0200 Subject: 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) --- tests/blocktest.go | 49 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 9 deletions(-) (limited to 'tests') 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 } -- cgit v1.2.3 From c453f1f37093445ba1d9eba5a075169ef0566c19 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 17 Apr 2015 18:20:32 +0200 Subject: 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(-) (limited to 'tests') 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 } -- cgit v1.2.3 From 805345d13594b41e5dd2e4fed471c58c3775be9e Mon Sep 17 00:00:00 2001 From: Gustav Simonsson 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