| 
									
										
										
										
											2015-03-13 18:01:51 +01:00
										 |  |  | package tests | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"encoding/hex" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io/ioutil" | 
					
						
							|  |  |  | 	"math/big" | 
					
						
							|  |  |  | 	"runtime" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-16 11:27:38 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							| 
									
										
										
										
											2015-03-23 22:05:12 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/state" | 
					
						
							| 
									
										
										
										
											2015-03-16 23:10:26 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/types" | 
					
						
							| 
									
										
										
										
											2015-03-13 18:01:51 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/rlp" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Block Test JSON Format | 
					
						
							|  |  |  | type btJSON struct { | 
					
						
							|  |  |  | 	Blocks             []btBlock | 
					
						
							|  |  |  | 	GenesisBlockHeader btHeader | 
					
						
							|  |  |  | 	Pre                map[string]btAccount | 
					
						
							| 
									
										
										
										
											2015-03-20 09:10:13 +01:00
										 |  |  | 	PostState          map[string]btAccount | 
					
						
							| 
									
										
										
										
											2015-03-13 18:01:51 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 | 
					
						
							| 
									
										
										
										
											2015-04-01 10:53:32 +02:00
										 |  |  | 	UncleHeaders []*btHeader | 
					
						
							| 
									
										
										
										
											2015-03-13 18:01:51 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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. | 
					
						
							| 
									
										
										
										
											2015-03-20 09:10:13 +01:00
										 |  |  | func (t *BlockTest) InsertPreState(db common.Database) (*state.StateDB, error) { | 
					
						
							| 
									
										
										
										
											2015-03-18 11:44:25 +01:00
										 |  |  | 	statedb := state.New(common.Hash{}, db) | 
					
						
							| 
									
										
										
										
											2015-03-13 18:01:51 +01:00
										 |  |  | 	for addrString, acct := range t.preAccounts { | 
					
						
							|  |  |  | 		// XXX: is is worth it checking for errors here? | 
					
						
							| 
									
										
										
										
											2015-03-16 23:10:26 +01:00
										 |  |  | 		//addr, _ := hex.DecodeString(addrString) | 
					
						
							| 
									
										
										
										
											2015-03-13 18:01:51 +01:00
										 |  |  | 		code, _ := hex.DecodeString(strings.TrimPrefix(acct.Code, "0x")) | 
					
						
							|  |  |  | 		balance, _ := new(big.Int).SetString(acct.Balance, 0) | 
					
						
							|  |  |  | 		nonce, _ := strconv.ParseUint(acct.Nonce, 16, 64) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-01 10:53:32 +02:00
										 |  |  | 		obj := statedb.CreateAccount(common.HexToAddress(addrString)) | 
					
						
							| 
									
										
										
										
											2015-03-13 18:01:51 +01:00
										 |  |  | 		obj.SetCode(code) | 
					
						
							|  |  |  | 		obj.SetBalance(balance) | 
					
						
							|  |  |  | 		obj.SetNonce(nonce) | 
					
						
							| 
									
										
										
										
											2015-04-01 10:53:32 +02:00
										 |  |  | 		for k, v := range acct.Storage { | 
					
						
							|  |  |  | 			statedb.SetState(common.HexToAddress(addrString), common.HexToHash(k), common.FromHex(v)) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-03-13 18:01:51 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// sync objects to trie | 
					
						
							|  |  |  | 	statedb.Update(nil) | 
					
						
							|  |  |  | 	// sync trie to disk | 
					
						
							|  |  |  | 	statedb.Sync() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 20:29:12 +01:00
										 |  |  | 	if !bytes.Equal(t.Genesis.Root().Bytes(), statedb.Root().Bytes()) { | 
					
						
							| 
									
										
										
										
											2015-04-01 10:53:32 +02:00
										 |  |  | 		return nil, fmt.Errorf("computed state root does not match genesis block %x %x", t.Genesis.Root().Bytes()[:4], statedb.Root().Bytes()[:4]) | 
					
						
							| 
									
										
										
										
											2015-03-20 09:10:13 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return statedb, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 | 
					
						
							| 
									
										
										
										
											2015-03-21 20:29:12 +01:00
										 |  |  | 		code2 := statedb.GetCode(common.BytesToAddress(addr)) | 
					
						
							|  |  |  | 		balance2 := statedb.GetBalance(common.BytesToAddress(addr)) | 
					
						
							|  |  |  | 		nonce2 := statedb.GetNonce(common.BytesToAddress(addr)) | 
					
						
							| 
									
										
										
										
											2015-03-20 09:10:13 +01:00
										 |  |  | 		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) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-03-13 18:01:51 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	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) | 
					
						
							|  |  |  | 	return b | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func mustConvertHeader(in btHeader) *types.Header { | 
					
						
							|  |  |  | 	// hex decode these fields | 
					
						
							| 
									
										
										
										
											2015-03-16 23:10:26 +01:00
										 |  |  | 	header := &types.Header{ | 
					
						
							| 
									
										
										
										
											2015-03-14 23:37:21 +01:00
										 |  |  | 		//SeedHash:    mustConvertBytes(in.SeedHash), | 
					
						
							| 
									
										
										
										
											2015-03-16 23:10:26 +01:00
										 |  |  | 		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), | 
					
						
							| 
									
										
										
										
											2015-03-13 18:01:51 +01:00
										 |  |  | 		Extra:       string(mustConvertBytes(in.ExtraData)), | 
					
						
							|  |  |  | 		GasUsed:     mustConvertBigInt10(in.GasUsed), | 
					
						
							|  |  |  | 		GasLimit:    mustConvertBigInt10(in.GasLimit), | 
					
						
							|  |  |  | 		Difficulty:  mustConvertBigInt10(in.Difficulty), | 
					
						
							|  |  |  | 		Time:        mustConvertUint(in.Timestamp), | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-16 23:10:26 +01:00
										 |  |  | 	// XXX cheats? :-) | 
					
						
							|  |  |  | 	header.SetNonce(common.BytesToHash(mustConvertBytes(in.Nonce)).Big().Uint64()) | 
					
						
							|  |  |  | 	return header | 
					
						
							| 
									
										
										
										
											2015-03-13 18:01:51 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-16 23:10:26 +01:00
										 |  |  | 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) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-18 11:44:25 +01:00
										 |  |  | func mustConvertBloom(in string) types.Bloom { | 
					
						
							| 
									
										
										
										
											2015-03-16 23:10:26 +01:00
										 |  |  | 	out, err := hex.DecodeString(strings.TrimPrefix(in, "0x")) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		panic(fmt.Errorf("invalid hex: %q", in)) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-03-18 11:44:25 +01:00
										 |  |  | 	return types.BytesToBloom(out) | 
					
						
							| 
									
										
										
										
											2015-03-16 23:10:26 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-13 18:01:51 +01:00
										 |  |  | 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 | 
					
						
							|  |  |  | } |