| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- package tests
- import (
- "bytes"
- "encoding/hex"
- "encoding/json"
- "errors"
- "fmt"
- "io/ioutil"
- "math/big"
- "runtime"
- "strconv"
- "strings"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/state"
- )
- // Block Test JSON Format
- type btJSON struct {
- Blocks []btBlock
- GenesisBlockHeader btHeader
- Pre 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 []string
- }
- type BlockTest struct {
- Genesis *types.Block
- Blocks []*types.Block
- 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 nil, fmt.Errorf("bad test %q: %v", err)
- }
- }
- return out, nil
- }
- // InsertPreState populates the given database with the genesis
- // accounts defined by the test.
- func (t *BlockTest) InsertPreState(db common.Database) error {
- statedb := state.New(nil, 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.NewStateObject(addr)
- obj.SetCode(code)
- obj.SetBalance(balance)
- obj.SetNonce(nonce)
- // for k, v := range acct.Storage {
- // obj.SetState(k, v)
- // }
- }
- // sync objects to trie
- statedb.Update(nil)
- // sync trie to disk
- statedb.Sync()
- if !bytes.Equal(t.Genesis.Root(), statedb.Root()) {
- return errors.New("computed state root does not match genesis block")
- }
- return 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}
- out.Genesis = mustConvertGenesis(in.GenesisBlockHeader)
- out.Blocks = mustConvertBlocks(in.Blocks)
- 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)
- b.Reward = new(big.Int)
- return b
- }
- func mustConvertHeader(in btHeader) *types.Header {
- // hex decode these fields
- return &types.Header{
- //SeedHash: mustConvertBytes(in.SeedHash),
- MixDigest: mustConvertBytes(in.MixHash),
- Bloom: mustConvertBytes(in.Bloom),
- ReceiptHash: mustConvertBytes(in.ReceiptTrie),
- TxHash: mustConvertBytes(in.TransactionsTrie),
- Root: mustConvertBytes(in.StateRoot),
- Coinbase: mustConvertBytes(in.Coinbase),
- UncleHash: mustConvertBytes(in.UncleHash),
- ParentHash: mustConvertBytes(in.ParentHash),
- Nonce: mustConvertBytes(in.Nonce),
- Extra: string(mustConvertBytes(in.ExtraData)),
- GasUsed: mustConvertBigInt10(in.GasUsed),
- GasLimit: mustConvertBigInt10(in.GasLimit),
- Difficulty: mustConvertBigInt10(in.Difficulty),
- Time: mustConvertUint(in.Timestamp),
- }
- }
- 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", i, inb.Rlp))
- }
- out = append(out, &b)
- }
- return out
- }
- func mustConvertBytes(in string) []byte {
- out, err := hex.DecodeString(strings.TrimPrefix(in, "0x"))
- if err != nil {
- panic(fmt.Errorf("invalid hex: %q", in))
- }
- return out
- }
- func mustConvertBigInt10(in string) *big.Int {
- out, ok := new(big.Int).SetString(in, 10)
- 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 {
- 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
- }
|