2015-07-07 00:54:22 +00:00
|
|
|
// Copyright 2015 The go-ethereum Authors
|
2015-07-22 16:48:40 +00:00
|
|
|
// This file is part of the go-ethereum library.
|
2015-07-07 00:54:22 +00:00
|
|
|
//
|
2015-07-23 16:35:11 +00:00
|
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
2015-07-07 00:54:22 +00:00
|
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
2015-07-22 16:48:40 +00:00
|
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
2015-07-07 00:54:22 +00:00
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2015-07-22 16:48:40 +00:00
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2015-07-07 00:54:22 +00:00
|
|
|
// GNU Lesser General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Lesser General Public License
|
2015-07-22 16:48:40 +00:00
|
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
2015-07-07 00:54:22 +00:00
|
|
|
|
2015-03-13 17:01:51 +00:00
|
|
|
package tests
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/hex"
|
|
|
|
"fmt"
|
2015-06-14 21:55:03 +00:00
|
|
|
"io"
|
2015-03-13 17:01:51 +00:00
|
|
|
"math/big"
|
2015-06-10 16:04:56 +00:00
|
|
|
"path/filepath"
|
2015-03-13 17:01:51 +00:00
|
|
|
"runtime"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
2015-04-27 17:32:45 +00:00
|
|
|
"time"
|
2015-03-13 17:01:51 +00:00
|
|
|
|
2015-06-10 16:04:56 +00:00
|
|
|
"github.com/ethereum/go-ethereum/accounts"
|
2015-03-16 10:27:38 +00:00
|
|
|
"github.com/ethereum/go-ethereum/common"
|
2015-04-17 14:30:15 +00:00
|
|
|
"github.com/ethereum/go-ethereum/core"
|
2015-03-23 21:05:12 +00:00
|
|
|
"github.com/ethereum/go-ethereum/core/state"
|
2015-03-16 22:10:26 +00:00
|
|
|
"github.com/ethereum/go-ethereum/core/types"
|
2015-04-27 17:32:45 +00:00
|
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
|
|
"github.com/ethereum/go-ethereum/eth"
|
2015-06-10 16:04:56 +00:00
|
|
|
"github.com/ethereum/go-ethereum/ethdb"
|
2015-06-11 17:06:56 +00:00
|
|
|
"github.com/ethereum/go-ethereum/logger/glog"
|
2015-03-13 17:01:51 +00:00
|
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Block Test JSON Format
|
2015-04-20 16:14:57 +00:00
|
|
|
type BlockTest struct {
|
|
|
|
Genesis *types.Block
|
|
|
|
|
2015-09-14 12:27:25 +00:00
|
|
|
Json *btJSON
|
|
|
|
preAccounts map[string]btAccount
|
|
|
|
postAccounts map[string]btAccount
|
2015-04-20 16:14:57 +00:00
|
|
|
}
|
|
|
|
|
2015-03-13 17:01:51 +00:00
|
|
|
type btJSON struct {
|
|
|
|
Blocks []btBlock
|
|
|
|
GenesisBlockHeader btHeader
|
|
|
|
Pre map[string]btAccount
|
2015-03-20 08:10:13 +00:00
|
|
|
PostState map[string]btAccount
|
2015-03-13 17:01:51 +00:00
|
|
|
}
|
|
|
|
|
2015-04-20 16:14:57 +00:00
|
|
|
type btBlock struct {
|
|
|
|
BlockHeader *btHeader
|
|
|
|
Rlp string
|
|
|
|
Transactions []btTransaction
|
|
|
|
UncleHeaders []*btHeader
|
|
|
|
}
|
|
|
|
|
2015-03-13 17:01:51 +00:00
|
|
|
type btAccount struct {
|
2015-04-27 17:32:45 +00:00
|
|
|
Balance string
|
|
|
|
Code string
|
|
|
|
Nonce string
|
|
|
|
Storage map[string]string
|
|
|
|
PrivateKey string
|
2015-03-13 17:01:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2015-06-19 09:38:23 +00:00
|
|
|
func RunBlockTestWithReader(r io.Reader, skipTests []string) error {
|
2015-06-14 21:55:03 +00:00
|
|
|
btjs := make(map[string]*btJSON)
|
|
|
|
if err := readJson(r, &btjs); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
bt, err := convertBlockTests(btjs)
|
2015-06-10 16:04:56 +00:00
|
|
|
if err != nil {
|
2015-06-10 22:11:30 +00:00
|
|
|
return err
|
2015-06-10 16:04:56 +00:00
|
|
|
}
|
|
|
|
|
2015-06-19 09:38:23 +00:00
|
|
|
if err := runBlockTests(bt, skipTests); err != nil {
|
2015-06-14 21:55:03 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-06-19 09:38:23 +00:00
|
|
|
func RunBlockTest(file string, skipTests []string) error {
|
2015-06-14 21:55:03 +00:00
|
|
|
btjs := make(map[string]*btJSON)
|
|
|
|
if err := readJsonFile(file, &btjs); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
bt, err := convertBlockTests(btjs)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-06-19 09:38:23 +00:00
|
|
|
if err := runBlockTests(bt, skipTests); err != nil {
|
2015-06-14 21:55:03 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-06-19 09:38:23 +00:00
|
|
|
func runBlockTests(bt map[string]*BlockTest, skipTests []string) error {
|
|
|
|
skipTest := make(map[string]bool, len(skipTests))
|
|
|
|
for _, name := range skipTests {
|
2015-06-10 21:04:06 +00:00
|
|
|
skipTest[name] = true
|
2015-06-10 16:04:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for name, test := range bt {
|
2015-06-10 21:04:06 +00:00
|
|
|
// if the test should be skipped, return
|
|
|
|
if skipTest[name] {
|
2015-06-11 17:06:56 +00:00
|
|
|
glog.Infoln("Skipping block test", name)
|
2015-07-03 07:40:07 +00:00
|
|
|
continue
|
2015-06-10 16:04:56 +00:00
|
|
|
}
|
2015-06-10 21:04:06 +00:00
|
|
|
// test the block
|
2015-06-14 21:55:03 +00:00
|
|
|
if err := runBlockTest(test); err != nil {
|
2015-08-20 16:22:50 +00:00
|
|
|
return fmt.Errorf("%s: %v", name, err)
|
2015-06-10 21:04:06 +00:00
|
|
|
}
|
2015-06-11 17:06:56 +00:00
|
|
|
glog.Infoln("Block test passed: ", name)
|
2015-06-14 21:55:03 +00:00
|
|
|
|
2015-06-10 16:04:56 +00:00
|
|
|
}
|
2015-06-10 20:10:33 +00:00
|
|
|
return nil
|
2015-06-10 16:04:56 +00:00
|
|
|
|
2015-06-14 21:55:03 +00:00
|
|
|
}
|
|
|
|
func runBlockTest(test *BlockTest) error {
|
|
|
|
cfg := test.makeEthConfig()
|
2015-07-10 12:29:40 +00:00
|
|
|
cfg.GenesisBlock = test.Genesis
|
|
|
|
|
2015-06-10 16:04:56 +00:00
|
|
|
ethereum, err := eth.New(cfg)
|
|
|
|
if err != nil {
|
2015-06-10 20:10:33 +00:00
|
|
|
return err
|
2015-06-10 16:04:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err = ethereum.Start()
|
|
|
|
if err != nil {
|
2015-06-10 20:10:33 +00:00
|
|
|
return err
|
2015-06-10 16:04:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// import pre accounts
|
2015-09-14 12:27:25 +00:00
|
|
|
_, err = test.InsertPreState(ethereum)
|
2015-06-10 16:04:56 +00:00
|
|
|
if err != nil {
|
2015-06-10 20:10:33 +00:00
|
|
|
return fmt.Errorf("InsertPreState: %v", err)
|
2015-06-10 16:04:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err = test.TryBlocksInsert(ethereum.ChainManager())
|
|
|
|
if err != nil {
|
2015-06-10 20:10:33 +00:00
|
|
|
return err
|
2015-06-10 16:04:56 +00:00
|
|
|
}
|
|
|
|
|
2015-09-14 12:27:25 +00:00
|
|
|
newDB := ethereum.ChainManager().State()
|
|
|
|
if err = test.ValidatePostState(newDB); err != nil {
|
2015-06-10 20:10:33 +00:00
|
|
|
return fmt.Errorf("post state validation failed: %v", err)
|
2015-06-10 16:04:56 +00:00
|
|
|
}
|
2015-06-10 20:10:33 +00:00
|
|
|
return nil
|
2015-06-10 16:04:56 +00:00
|
|
|
}
|
|
|
|
|
2015-06-14 21:55:03 +00:00
|
|
|
func (test *BlockTest) makeEthConfig() *eth.Config {
|
2015-06-10 16:04:56 +00:00
|
|
|
ks := crypto.NewKeyStorePassphrase(filepath.Join(common.DefaultDataDir(), "keystore"))
|
|
|
|
|
|
|
|
return ð.Config{
|
|
|
|
DataDir: common.DefaultDataDir(),
|
|
|
|
Verbosity: 5,
|
2015-07-07 08:32:05 +00:00
|
|
|
Etherbase: common.Address{},
|
2015-06-10 16:04:56 +00:00
|
|
|
AccountManager: accounts.NewManager(ks),
|
2015-09-14 07:35:57 +00:00
|
|
|
NewDB: func(path string) (ethdb.Database, error) { return ethdb.NewMemDatabase() },
|
2015-06-10 16:04:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-13 17:01:51 +00:00
|
|
|
// InsertPreState populates the given database with the genesis
|
|
|
|
// accounts defined by the test.
|
2015-04-27 17:32:45 +00:00
|
|
|
func (t *BlockTest) InsertPreState(ethereum *eth.Ethereum) (*state.StateDB, error) {
|
2015-08-06 17:57:39 +00:00
|
|
|
db := ethereum.ChainDb()
|
2015-03-18 10:44:25 +00:00
|
|
|
statedb := state.New(common.Hash{}, db)
|
2015-03-13 17:01:51 +00:00
|
|
|
for addrString, acct := range t.preAccounts {
|
2015-07-07 21:57:54 +00:00
|
|
|
addr, err := hex.DecodeString(addrString)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
code, err := hex.DecodeString(strings.TrimPrefix(acct.Code, "0x"))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
balance, ok := new(big.Int).SetString(acct.Balance, 0)
|
|
|
|
if !ok {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
nonce, err := strconv.ParseUint(prepInt(16, acct.Nonce), 16, 64)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-03-13 17:01:51 +00:00
|
|
|
|
2015-04-27 17:32:45 +00:00
|
|
|
if acct.PrivateKey != "" {
|
|
|
|
privkey, err := hex.DecodeString(strings.TrimPrefix(acct.PrivateKey, "0x"))
|
|
|
|
err = crypto.ImportBlockTestKey(privkey)
|
2015-05-10 18:30:02 +00:00
|
|
|
err = ethereum.AccountManager().TimedUnlock(common.BytesToAddress(addr), "", 999999*time.Second)
|
2015-04-27 17:32:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-01 08:53:32 +00:00
|
|
|
obj := statedb.CreateAccount(common.HexToAddress(addrString))
|
2015-03-13 17:01:51 +00:00
|
|
|
obj.SetCode(code)
|
|
|
|
obj.SetBalance(balance)
|
|
|
|
obj.SetNonce(nonce)
|
2015-04-01 08:53:32 +00:00
|
|
|
for k, v := range acct.Storage {
|
2015-06-17 09:44:40 +00:00
|
|
|
statedb.SetState(common.HexToAddress(addrString), common.HexToHash(k), common.HexToHash(v))
|
2015-04-01 08:53:32 +00:00
|
|
|
}
|
2015-03-13 17:01:51 +00:00
|
|
|
}
|
|
|
|
// sync objects to trie
|
2015-07-01 10:07:14 +00:00
|
|
|
statedb.SyncObjects()
|
2015-03-13 17:01:51 +00:00
|
|
|
// sync trie to disk
|
|
|
|
statedb.Sync()
|
|
|
|
|
2015-03-21 19:29:12 +00:00
|
|
|
if !bytes.Equal(t.Genesis.Root().Bytes(), statedb.Root().Bytes()) {
|
2015-04-01 08:53:32 +00: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 08:10:13 +00:00
|
|
|
}
|
|
|
|
return statedb, nil
|
|
|
|
}
|
|
|
|
|
2015-04-20 16:14:57 +00:00
|
|
|
/* See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II
|
|
|
|
|
|
|
|
Whether a block is valid or not is a bit subtle, it's defined by presence of
|
|
|
|
blockHeader, transactions and uncleHeaders fields. If they are missing, the block is
|
|
|
|
invalid and we must verify that we do not accept it.
|
|
|
|
|
|
|
|
Since some tests mix valid and invalid blocks we need to check this for every block.
|
|
|
|
|
|
|
|
If a block is invalid it does not necessarily fail the test, if it's invalidness is
|
|
|
|
expected we are expected to ignore it and continue processing and then validate the
|
|
|
|
post state.
|
|
|
|
*/
|
|
|
|
func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error {
|
2015-09-14 12:27:25 +00:00
|
|
|
blockNums := make(map[string]bool)
|
2015-04-20 16:14:57 +00:00
|
|
|
// insert the test blocks, which will execute all transactions
|
|
|
|
for _, b := range t.Json.Blocks {
|
|
|
|
cb, err := mustConvertBlock(b)
|
|
|
|
if err != nil {
|
|
|
|
if b.BlockHeader == nil {
|
|
|
|
continue // OK - block is supposed to be invalid, continue with next block
|
|
|
|
} else {
|
2015-06-16 10:41:50 +00:00
|
|
|
return fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err)
|
2015-04-20 16:14:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// RLP decoding worked, try to insert into chain:
|
2015-04-29 12:00:24 +00:00
|
|
|
_, err = chainManager.InsertChain(types.Blocks{cb})
|
2015-04-20 16:14:57 +00:00
|
|
|
if err != nil {
|
|
|
|
if b.BlockHeader == nil {
|
|
|
|
continue // OK - block is supposed to be invalid, continue with next block
|
|
|
|
} else {
|
2015-06-16 10:41:50 +00:00
|
|
|
return fmt.Errorf("Block insertion into chain failed: %v", err)
|
2015-04-20 16:14:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if b.BlockHeader == nil {
|
|
|
|
return fmt.Errorf("Block insertion should have failed")
|
|
|
|
}
|
2015-09-14 12:27:25 +00:00
|
|
|
|
|
|
|
// validate RLP decoding by checking all values against test file JSON
|
|
|
|
if err = t.validateBlockHeader(b.BlockHeader, cb.Header()); err != nil {
|
|
|
|
return fmt.Errorf("Deserialised block header validation failed: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// validate the imported header against test file JSON
|
|
|
|
|
|
|
|
/*
|
|
|
|
TODO: currently test files do not contain information on what
|
|
|
|
reorg is expected other than possibly the post state (which may
|
|
|
|
or may not depend on a specific chain).
|
|
|
|
|
|
|
|
discussed with winswega and it was agreed to add this information
|
|
|
|
to the test files explicitly.
|
|
|
|
|
|
|
|
meanwhile we skip header validation on blocks with the same block
|
|
|
|
number as a prior block, since this test code cannot know what
|
|
|
|
blocks are in the longest chain without making use of the very
|
|
|
|
protocol rules the tests verify or rely on the correctness of the
|
|
|
|
code that is being tested.
|
|
|
|
|
|
|
|
*/
|
|
|
|
if !blockNums[b.BlockHeader.Number] {
|
|
|
|
importedBlock := chainManager.CurrentBlock()
|
|
|
|
if err = t.validateBlockHeader(b.BlockHeader, importedBlock.Header()); err != nil {
|
|
|
|
return fmt.Errorf("Imported block header validation failed: %v", err)
|
|
|
|
}
|
|
|
|
blockNums[b.BlockHeader.Number] = true
|
2015-04-23 02:38:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-06-14 21:55:03 +00:00
|
|
|
func (s *BlockTest) validateBlockHeader(h *btHeader, h2 *types.Header) error {
|
2015-04-23 02:38:47 +00:00
|
|
|
expectedBloom := mustConvertBytes(h.Bloom)
|
|
|
|
if !bytes.Equal(expectedBloom, h2.Bloom.Bytes()) {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("Bloom: want: %x have: %x", expectedBloom, h2.Bloom.Bytes())
|
2015-04-23 02:38:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expectedCoinbase := mustConvertBytes(h.Coinbase)
|
|
|
|
if !bytes.Equal(expectedCoinbase, h2.Coinbase.Bytes()) {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("Coinbase: want: %x have: %x", expectedCoinbase, h2.Coinbase.Bytes())
|
2015-04-23 02:38:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expectedMixHashBytes := mustConvertBytes(h.MixHash)
|
|
|
|
if !bytes.Equal(expectedMixHashBytes, h2.MixDigest.Bytes()) {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("MixHash: want: %x have: %x", expectedMixHashBytes, h2.MixDigest.Bytes())
|
2015-04-23 02:38:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expectedNonce := mustConvertBytes(h.Nonce)
|
|
|
|
if !bytes.Equal(expectedNonce, h2.Nonce[:]) {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("Nonce: want: %x have: %x", expectedNonce, h2.Nonce)
|
2015-04-23 02:38:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expectedNumber := mustConvertBigInt(h.Number, 16)
|
|
|
|
if expectedNumber.Cmp(h2.Number) != 0 {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("Number: want: %v have: %v", expectedNumber, h2.Number)
|
2015-04-23 02:38:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expectedParentHash := mustConvertBytes(h.ParentHash)
|
|
|
|
if !bytes.Equal(expectedParentHash, h2.ParentHash.Bytes()) {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("Parent hash: want: %x have: %x", expectedParentHash, h2.ParentHash.Bytes())
|
2015-04-23 02:38:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expectedReceiptHash := mustConvertBytes(h.ReceiptTrie)
|
|
|
|
if !bytes.Equal(expectedReceiptHash, h2.ReceiptHash.Bytes()) {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("Receipt hash: want: %x have: %x", expectedReceiptHash, h2.ReceiptHash.Bytes())
|
2015-04-23 02:38:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expectedTxHash := mustConvertBytes(h.TransactionsTrie)
|
|
|
|
if !bytes.Equal(expectedTxHash, h2.TxHash.Bytes()) {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("Tx hash: want: %x have: %x", expectedTxHash, h2.TxHash.Bytes())
|
2015-04-23 02:38:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expectedStateHash := mustConvertBytes(h.StateRoot)
|
|
|
|
if !bytes.Equal(expectedStateHash, h2.Root.Bytes()) {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("State hash: want: %x have: %x", expectedStateHash, h2.Root.Bytes())
|
2015-04-23 02:38:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expectedUncleHash := mustConvertBytes(h.UncleHash)
|
|
|
|
if !bytes.Equal(expectedUncleHash, h2.UncleHash.Bytes()) {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("Uncle hash: want: %x have: %x", expectedUncleHash, h2.UncleHash.Bytes())
|
2015-04-23 02:38:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expectedExtraData := mustConvertBytes(h.ExtraData)
|
|
|
|
if !bytes.Equal(expectedExtraData, h2.Extra) {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("Extra data: want: %x have: %x", expectedExtraData, h2.Extra)
|
2015-04-17 14:30:15 +00:00
|
|
|
}
|
2015-04-23 02:38:47 +00:00
|
|
|
|
|
|
|
expectedDifficulty := mustConvertBigInt(h.Difficulty, 16)
|
|
|
|
if expectedDifficulty.Cmp(h2.Difficulty) != 0 {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("Difficulty: want: %v have: %v", expectedDifficulty, h2.Difficulty)
|
2015-04-23 02:38:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expectedGasLimit := mustConvertBigInt(h.GasLimit, 16)
|
|
|
|
if expectedGasLimit.Cmp(h2.GasLimit) != 0 {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("GasLimit: want: %v have: %v", expectedGasLimit, h2.GasLimit)
|
2015-04-23 02:38:47 +00:00
|
|
|
}
|
|
|
|
expectedGasUsed := mustConvertBigInt(h.GasUsed, 16)
|
|
|
|
if expectedGasUsed.Cmp(h2.GasUsed) != 0 {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("GasUsed: want: %v have: %v", expectedGasUsed, h2.GasUsed)
|
2015-04-23 02:38:47 +00:00
|
|
|
}
|
|
|
|
|
2015-08-24 00:52:53 +00:00
|
|
|
expectedTimestamp := mustConvertBigInt(h.Timestamp, 16)
|
|
|
|
if expectedTimestamp.Cmp(h2.Time) != 0 {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("Timestamp: want: %v have: %v", expectedTimestamp, h2.Time)
|
2015-04-23 02:38:47 +00:00
|
|
|
}
|
|
|
|
|
2015-04-20 16:14:57 +00:00
|
|
|
return nil
|
2015-04-17 14:30:15 +00:00
|
|
|
}
|
|
|
|
|
2015-03-20 08:10:13 +00:00
|
|
|
func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error {
|
2015-09-14 12:27:25 +00:00
|
|
|
// validate post state accounts in test file against what we have in state db
|
|
|
|
for addrString, acct := range t.postAccounts {
|
2015-03-20 08:10:13 +00:00
|
|
|
// XXX: is is worth it checking for errors here?
|
2015-07-07 21:57:54 +00:00
|
|
|
addr, err := hex.DecodeString(addrString)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
code, err := hex.DecodeString(strings.TrimPrefix(acct.Code, "0x"))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
balance, ok := new(big.Int).SetString(acct.Balance, 0)
|
|
|
|
if !ok {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
nonce, err := strconv.ParseUint(prepInt(16, acct.Nonce), 16, 64)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-03-20 08:10:13 +00:00
|
|
|
|
|
|
|
// address is indirectly verified by the other fields, as it's the db key
|
2015-03-21 19:29:12 +00:00
|
|
|
code2 := statedb.GetCode(common.BytesToAddress(addr))
|
|
|
|
balance2 := statedb.GetBalance(common.BytesToAddress(addr))
|
|
|
|
nonce2 := statedb.GetNonce(common.BytesToAddress(addr))
|
2015-03-20 08:10:13 +00:00
|
|
|
if !bytes.Equal(code2, code) {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("account code mismatch for addr: %s want: %s have: %s", addrString, hex.EncodeToString(code), hex.EncodeToString(code2))
|
2015-03-20 08:10:13 +00:00
|
|
|
}
|
|
|
|
if balance2.Cmp(balance) != 0 {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("account balance mismatch for addr: %s, want: %d, have: %d", addrString, balance, balance2)
|
2015-03-20 08:10:13 +00:00
|
|
|
}
|
|
|
|
if nonce2 != nonce {
|
2015-09-14 12:27:25 +00:00
|
|
|
return fmt.Errorf("account nonce mismatch for addr: %s want: %d have: %d", addrString, nonce, nonce2)
|
2015-03-20 08:10:13 +00:00
|
|
|
}
|
2015-03-13 17:01:51 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-06-14 21:55:03 +00:00
|
|
|
func convertBlockTests(in map[string]*btJSON) (map[string]*BlockTest, error) {
|
|
|
|
out := make(map[string]*BlockTest)
|
|
|
|
for name, test := range in {
|
|
|
|
var err error
|
|
|
|
if out[name], err = convertBlockTest(test); err != nil {
|
|
|
|
return out, fmt.Errorf("bad test %q: %v", name, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func convertBlockTest(in *btJSON) (out *BlockTest, err error) {
|
2015-03-13 17:01:51 +00:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
}()
|
2015-09-14 12:27:25 +00:00
|
|
|
out = &BlockTest{preAccounts: in.Pre, postAccounts: in.PostState, Json: in}
|
2015-03-13 17:01:51 +00:00
|
|
|
out.Genesis = mustConvertGenesis(in.GenesisBlockHeader)
|
|
|
|
return out, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func mustConvertGenesis(testGenesis btHeader) *types.Block {
|
|
|
|
hdr := mustConvertHeader(testGenesis)
|
|
|
|
hdr.Number = big.NewInt(0)
|
2015-09-07 17:43:01 +00:00
|
|
|
|
|
|
|
return types.NewBlockWithHeader(hdr)
|
2015-03-13 17:01:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func mustConvertHeader(in btHeader) *types.Header {
|
|
|
|
// hex decode these fields
|
2015-03-16 22:10:26 +00:00
|
|
|
header := &types.Header{
|
2015-03-14 22:37:21 +00:00
|
|
|
//SeedHash: mustConvertBytes(in.SeedHash),
|
2015-03-16 22:10:26 +00: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-04-05 16:57:03 +00:00
|
|
|
Extra: mustConvertBytes(in.ExtraData),
|
2015-04-22 13:21:02 +00:00
|
|
|
GasUsed: mustConvertBigInt(in.GasUsed, 16),
|
|
|
|
GasLimit: mustConvertBigInt(in.GasLimit, 16),
|
|
|
|
Difficulty: mustConvertBigInt(in.Difficulty, 16),
|
2015-08-24 00:52:53 +00:00
|
|
|
Time: mustConvertBigInt(in.Timestamp, 16),
|
2015-06-16 10:41:50 +00:00
|
|
|
Nonce: types.EncodeNonce(mustConvertUint(in.Nonce, 16)),
|
2015-03-13 17:01:51 +00:00
|
|
|
}
|
2015-03-16 22:10:26 +00:00
|
|
|
return header
|
2015-03-13 17:01:51 +00:00
|
|
|
}
|
|
|
|
|
2015-04-20 16:14:57 +00:00
|
|
|
func mustConvertBlock(testBlock btBlock) (*types.Block, error) {
|
|
|
|
var b types.Block
|
|
|
|
r := bytes.NewReader(mustConvertBytes(testBlock.Rlp))
|
|
|
|
err := rlp.Decode(r, &b)
|
|
|
|
return &b, err
|
2015-03-13 17:01:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func mustConvertBytes(in string) []byte {
|
2015-04-15 20:37:16 +00:00
|
|
|
if in == "0x" {
|
|
|
|
return []byte{}
|
|
|
|
}
|
2015-04-18 22:35:48 +00:00
|
|
|
h := unfuckFuckedHex(strings.TrimPrefix(in, "0x"))
|
2015-04-15 20:37:16 +00:00
|
|
|
out, err := hex.DecodeString(h)
|
2015-03-13 17:01:51 +00:00
|
|
|
if err != nil {
|
2015-04-18 22:35:48 +00:00
|
|
|
panic(fmt.Errorf("invalid hex: %q: ", h, err))
|
2015-03-13 17:01:51 +00:00
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2015-03-16 22:10:26 +00: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 {
|
2015-04-18 22:35:48 +00:00
|
|
|
out, err := hex.DecodeString(strings.TrimPrefix(in, "0x"))
|
2015-03-16 22:10:26 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Errorf("invalid hex: %q", in))
|
|
|
|
}
|
|
|
|
return common.BytesToAddress(out)
|
|
|
|
}
|
|
|
|
|
2015-03-18 10:44:25 +00:00
|
|
|
func mustConvertBloom(in string) types.Bloom {
|
2015-03-16 22:10:26 +00:00
|
|
|
out, err := hex.DecodeString(strings.TrimPrefix(in, "0x"))
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Errorf("invalid hex: %q", in))
|
|
|
|
}
|
2015-03-18 10:44:25 +00:00
|
|
|
return types.BytesToBloom(out)
|
2015-03-16 22:10:26 +00:00
|
|
|
}
|
|
|
|
|
2015-04-17 16:20:32 +00:00
|
|
|
func mustConvertBigInt(in string, base int) *big.Int {
|
|
|
|
in = prepInt(base, in)
|
|
|
|
out, ok := new(big.Int).SetString(in, base)
|
2015-04-10 09:55:31 +00:00
|
|
|
if !ok {
|
|
|
|
panic(fmt.Errorf("invalid integer: %q", in))
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2015-04-17 16:20:32 +00:00
|
|
|
func mustConvertUint(in string, base int) uint64 {
|
|
|
|
in = prepInt(base, in)
|
|
|
|
out, err := strconv.ParseUint(in, base, 64)
|
2015-04-10 09:55:31 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Errorf("invalid integer: %q", in))
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2015-06-11 16:20:30 +00:00
|
|
|
func LoadBlockTests(file string) (map[string]*BlockTest, error) {
|
2015-06-14 21:55:03 +00:00
|
|
|
btjs := make(map[string]*btJSON)
|
|
|
|
if err := readJsonFile(file, &btjs); err != nil {
|
2015-06-10 22:11:30 +00:00
|
|
|
return nil, err
|
2015-03-13 17:01:51 +00:00
|
|
|
}
|
|
|
|
|
2015-06-14 21:55:03 +00:00
|
|
|
return convertBlockTests(btjs)
|
2015-03-13 17:01:51 +00:00
|
|
|
}
|
2015-04-15 20:37:16 +00:00
|
|
|
|
2015-04-18 22:35:48 +00:00
|
|
|
// Nothing to see here, please move along...
|
2015-04-17 16:20:32 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2015-04-18 22:35:48 +00:00
|
|
|
// don't ask
|
|
|
|
func unfuckFuckedHex(almostHex string) string {
|
|
|
|
return nibbleFix(strings.Replace(almostHex, "v", "", -1))
|
|
|
|
}
|
|
|
|
|
2015-04-17 16:20:32 +00:00
|
|
|
func nibbleFix(s string) string {
|
|
|
|
if len(s)%2 != 0 {
|
|
|
|
s = "0" + s
|
2015-04-15 20:37:16 +00:00
|
|
|
}
|
2015-04-17 16:20:32 +00:00
|
|
|
return s
|
2015-04-15 20:37:16 +00:00
|
|
|
}
|