| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575 |
- // Copyright 2015 The go-ethereum Authors
- // This file is part of the go-ethereum library.
- //
- // The go-ethereum library is free software: you can redistribute it and/or modify
- // 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.
- //
- // The go-ethereum library is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
- package tests
- import (
- "bytes"
- "encoding/hex"
- "fmt"
- "io"
- "math/big"
- "runtime"
- "strconv"
- "strings"
- "github.com/ethereum/ethash"
- "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/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/event"
- "github.com/ethereum/go-ethereum/logger/glog"
- "github.com/ethereum/go-ethereum/rlp"
- )
- // Block Test JSON Format
- type BlockTest struct {
- Genesis *types.Block
- Json *btJSON
- preAccounts map[string]btAccount
- postAccounts map[string]btAccount
- lastblockhash string
- }
- type btJSON struct {
- Blocks []btBlock
- GenesisBlockHeader btHeader
- Pre map[string]btAccount
- PostState map[string]btAccount
- Lastblockhash string
- }
- type btBlock struct {
- BlockHeader *btHeader
- Rlp string
- Transactions []btTransaction
- UncleHeaders []*btHeader
- }
- type btAccount struct {
- Balance string
- Code string
- Nonce string
- Storage map[string]string
- PrivateKey string
- }
- type btHeader struct {
- Bloom string
- Coinbase string
- MixHash string
- Nonce string
- Number string
- Hash 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
- }
- func RunBlockTestWithReader(homesteadBlock, daoForkBlock *big.Int, r io.Reader, skipTests []string) error {
- btjs := make(map[string]*btJSON)
- if err := readJson(r, &btjs); err != nil {
- return err
- }
- bt, err := convertBlockTests(btjs)
- if err != nil {
- return err
- }
- if err := runBlockTests(homesteadBlock, daoForkBlock, bt, skipTests); err != nil {
- return err
- }
- return nil
- }
- func RunBlockTest(homesteadBlock, daoForkBlock *big.Int, file string, skipTests []string) error {
- btjs := make(map[string]*btJSON)
- if err := readJsonFile(file, &btjs); err != nil {
- return err
- }
- bt, err := convertBlockTests(btjs)
- if err != nil {
- return err
- }
- if err := runBlockTests(homesteadBlock, daoForkBlock, bt, skipTests); err != nil {
- return err
- }
- return nil
- }
- func runBlockTests(homesteadBlock, daoForkBlock *big.Int, bt map[string]*BlockTest, skipTests []string) error {
- skipTest := make(map[string]bool, len(skipTests))
- for _, name := range skipTests {
- skipTest[name] = true
- }
- for name, test := range bt {
- if skipTest[name] {
- glog.Infoln("Skipping block test", name)
- continue
- }
- // test the block
- if err := runBlockTest(homesteadBlock, daoForkBlock, test); err != nil {
- return fmt.Errorf("%s: %v", name, err)
- }
- glog.Infoln("Block test passed: ", name)
- }
- return nil
- }
- func runBlockTest(homesteadBlock, daoForkBlock *big.Int, test *BlockTest) error {
- // import pre accounts & construct test genesis block & state root
- db, _ := ethdb.NewMemDatabase()
- if _, err := test.InsertPreState(db); err != nil {
- return fmt.Errorf("InsertPreState: %v", err)
- }
- core.WriteTd(db, test.Genesis.Hash(), 0, test.Genesis.Difficulty())
- core.WriteBlock(db, test.Genesis)
- core.WriteCanonicalHash(db, test.Genesis.Hash(), test.Genesis.NumberU64())
- core.WriteHeadBlockHash(db, test.Genesis.Hash())
- evmux := new(event.TypeMux)
- config := &core.ChainConfig{HomesteadBlock: homesteadBlock, DAOForkBlock: daoForkBlock, DAOForkSupport: true}
- chain, err := core.NewBlockChain(db, config, ethash.NewShared(), evmux)
- if err != nil {
- return err
- }
- //vm.Debug = true
- validBlocks, err := test.TryBlocksInsert(chain)
- if err != nil {
- return err
- }
- lastblockhash := common.HexToHash(test.lastblockhash)
- cmlast := chain.LastBlockHash()
- if lastblockhash != cmlast {
- return fmt.Errorf("lastblockhash validation mismatch: want: %x, have: %x", lastblockhash, cmlast)
- }
- newDB, err := chain.State()
- if err != nil {
- return err
- }
- if err = test.ValidatePostState(newDB); err != nil {
- return fmt.Errorf("post state validation failed: %v", err)
- }
- return test.ValidateImportedHeaders(chain, validBlocks)
- }
- // InsertPreState populates the given database with the genesis
- // accounts defined by the test.
- func (t *BlockTest) InsertPreState(db ethdb.Database) (*state.StateDB, error) {
- statedb, err := state.New(common.Hash{}, db)
- if err != nil {
- return nil, err
- }
- for addrString, acct := range t.preAccounts {
- 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
- }
- obj := statedb.CreateAccount(common.HexToAddress(addrString))
- obj.SetCode(crypto.Keccak256Hash(code), code)
- obj.SetBalance(balance)
- obj.SetNonce(nonce)
- for k, v := range acct.Storage {
- statedb.SetState(common.HexToAddress(addrString), common.HexToHash(k), common.HexToHash(v))
- }
- }
- root, err := statedb.Commit()
- if err != nil {
- return nil, fmt.Errorf("error writing state: %v", err)
- }
- if t.Genesis.Root() != root {
- return nil, fmt.Errorf("computed state root does not match genesis block: genesis=%x computed=%x", t.Genesis.Root().Bytes()[:4], root.Bytes()[:4])
- }
- return statedb, nil
- }
- /* 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(blockchain *core.BlockChain) ([]btBlock, error) {
- validBlocks := make([]btBlock, 0)
- // 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 {
- return nil, fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err)
- }
- }
- // RLP decoding worked, try to insert into chain:
- blocks := types.Blocks{cb}
- i, err := blockchain.InsertChain(blocks)
- if err != nil {
- if b.BlockHeader == nil {
- continue // OK - block is supposed to be invalid, continue with next block
- } else {
- return nil, fmt.Errorf("Block #%v insertion into chain failed: %v", blocks[i].Number(), err)
- }
- }
- if b.BlockHeader == nil {
- return nil, fmt.Errorf("Block insertion should have failed")
- }
- // validate RLP decoding by checking all values against test file JSON
- if err = validateHeader(b.BlockHeader, cb.Header()); err != nil {
- return nil, fmt.Errorf("Deserialised block header validation failed: %v", err)
- }
- validBlocks = append(validBlocks, b)
- }
- return validBlocks, nil
- }
- func validateHeader(h *btHeader, h2 *types.Header) error {
- expectedBloom := mustConvertBytes(h.Bloom)
- if !bytes.Equal(expectedBloom, h2.Bloom.Bytes()) {
- return fmt.Errorf("Bloom: want: %x have: %x", expectedBloom, h2.Bloom.Bytes())
- }
- expectedCoinbase := mustConvertBytes(h.Coinbase)
- if !bytes.Equal(expectedCoinbase, h2.Coinbase.Bytes()) {
- return fmt.Errorf("Coinbase: want: %x have: %x", expectedCoinbase, h2.Coinbase.Bytes())
- }
- expectedMixHashBytes := mustConvertBytes(h.MixHash)
- if !bytes.Equal(expectedMixHashBytes, h2.MixDigest.Bytes()) {
- return fmt.Errorf("MixHash: want: %x have: %x", expectedMixHashBytes, h2.MixDigest.Bytes())
- }
- expectedNonce := mustConvertBytes(h.Nonce)
- if !bytes.Equal(expectedNonce, h2.Nonce[:]) {
- return fmt.Errorf("Nonce: want: %x have: %x", expectedNonce, h2.Nonce)
- }
- expectedNumber := mustConvertBigInt(h.Number, 16)
- if expectedNumber.Cmp(h2.Number) != 0 {
- return fmt.Errorf("Number: want: %v have: %v", expectedNumber, h2.Number)
- }
- expectedParentHash := mustConvertBytes(h.ParentHash)
- if !bytes.Equal(expectedParentHash, h2.ParentHash.Bytes()) {
- return fmt.Errorf("Parent hash: want: %x have: %x", expectedParentHash, h2.ParentHash.Bytes())
- }
- expectedReceiptHash := mustConvertBytes(h.ReceiptTrie)
- if !bytes.Equal(expectedReceiptHash, h2.ReceiptHash.Bytes()) {
- return fmt.Errorf("Receipt hash: want: %x have: %x", expectedReceiptHash, h2.ReceiptHash.Bytes())
- }
- expectedTxHash := mustConvertBytes(h.TransactionsTrie)
- if !bytes.Equal(expectedTxHash, h2.TxHash.Bytes()) {
- return fmt.Errorf("Tx hash: want: %x have: %x", expectedTxHash, h2.TxHash.Bytes())
- }
- expectedStateHash := mustConvertBytes(h.StateRoot)
- if !bytes.Equal(expectedStateHash, h2.Root.Bytes()) {
- return fmt.Errorf("State hash: want: %x have: %x", expectedStateHash, h2.Root.Bytes())
- }
- expectedUncleHash := mustConvertBytes(h.UncleHash)
- if !bytes.Equal(expectedUncleHash, h2.UncleHash.Bytes()) {
- return fmt.Errorf("Uncle hash: want: %x have: %x", expectedUncleHash, h2.UncleHash.Bytes())
- }
- expectedExtraData := mustConvertBytes(h.ExtraData)
- if !bytes.Equal(expectedExtraData, h2.Extra) {
- return fmt.Errorf("Extra data: want: %x have: %x", expectedExtraData, h2.Extra)
- }
- expectedDifficulty := mustConvertBigInt(h.Difficulty, 16)
- if expectedDifficulty.Cmp(h2.Difficulty) != 0 {
- return fmt.Errorf("Difficulty: want: %v have: %v", expectedDifficulty, h2.Difficulty)
- }
- expectedGasLimit := mustConvertBigInt(h.GasLimit, 16)
- if expectedGasLimit.Cmp(h2.GasLimit) != 0 {
- return fmt.Errorf("GasLimit: want: %v have: %v", expectedGasLimit, h2.GasLimit)
- }
- expectedGasUsed := mustConvertBigInt(h.GasUsed, 16)
- if expectedGasUsed.Cmp(h2.GasUsed) != 0 {
- return fmt.Errorf("GasUsed: want: %v have: %v", expectedGasUsed, h2.GasUsed)
- }
- expectedTimestamp := mustConvertBigInt(h.Timestamp, 16)
- if expectedTimestamp.Cmp(h2.Time) != 0 {
- return fmt.Errorf("Timestamp: want: %v have: %v", expectedTimestamp, h2.Time)
- }
- return nil
- }
- func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error {
- // validate post state accounts in test file against what we have in state db
- for addrString, acct := range t.postAccounts {
- // XXX: is is worth it checking for errors here?
- 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
- }
- // 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 for addr: %s want: %s have: %s", addrString, hex.EncodeToString(code), hex.EncodeToString(code2))
- }
- if balance2.Cmp(balance) != 0 {
- return fmt.Errorf("account balance mismatch for addr: %s, want: %d, have: %d", addrString, balance, balance2)
- }
- if nonce2 != nonce {
- return fmt.Errorf("account nonce mismatch for addr: %s want: %d have: %d", addrString, nonce, nonce2)
- }
- }
- return nil
- }
- func (test *BlockTest) ValidateImportedHeaders(cm *core.BlockChain, validBlocks []btBlock) error {
- // to get constant lookup when verifying block headers by hash (some tests have many blocks)
- bmap := make(map[string]btBlock, len(test.Json.Blocks))
- for _, b := range validBlocks {
- bmap[b.BlockHeader.Hash] = b
- }
- // iterate over blocks backwards from HEAD and validate imported
- // headers vs test file. some tests have reorgs, and we import
- // block-by-block, so we can only validate imported headers after
- // all blocks have been processed by ChainManager, as they may not
- // be part of the longest chain until last block is imported.
- for b := cm.CurrentBlock(); b != nil && b.NumberU64() != 0; b = cm.GetBlockByHash(b.Header().ParentHash) {
- bHash := common.Bytes2Hex(b.Hash().Bytes()) // hex without 0x prefix
- if err := validateHeader(bmap[bHash].BlockHeader, b.Header()); err != nil {
- return fmt.Errorf("Imported block header validation failed: %v", err)
- }
- }
- return nil
- }
- 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) {
- // 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, postAccounts: in.PostState, Json: in, lastblockhash: in.Lastblockhash}
- out.Genesis = mustConvertGenesis(in.GenesisBlockHeader)
- return out, err
- }
- func mustConvertGenesis(testGenesis btHeader) *types.Block {
- hdr := mustConvertHeader(testGenesis)
- hdr.Number = big.NewInt(0)
- return types.NewBlockWithHeader(hdr)
- }
- 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, 16),
- GasLimit: mustConvertBigInt(in.GasLimit, 16),
- Difficulty: mustConvertBigInt(in.Difficulty, 16),
- Time: mustConvertBigInt(in.Timestamp, 16),
- Nonce: types.EncodeNonce(mustConvertUint(in.Nonce, 16)),
- }
- return header
- }
- 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
- }
- 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))
- }
- 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
- }
- func LoadBlockTests(file string) (map[string]*BlockTest, error) {
- btjs := make(map[string]*btJSON)
- if err := readJsonFile(file, &btjs); err != nil {
- return nil, err
- }
- return convertBlockTests(btjs)
- }
- // 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
- }
|