github.com/pfcoder/quorum@v2.0.3-0.20180501191142-d4a1b0958135+incompatible/consensus/istanbul/backend/engine_test.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package backend
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/ecdsa"
    22  	"math/big"
    23  	"reflect"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/common/hexutil"
    29  	"github.com/ethereum/go-ethereum/consensus"
    30  	"github.com/ethereum/go-ethereum/consensus/istanbul"
    31  	"github.com/ethereum/go-ethereum/core"
    32  	"github.com/ethereum/go-ethereum/core/types"
    33  	"github.com/ethereum/go-ethereum/core/vm"
    34  	"github.com/ethereum/go-ethereum/crypto"
    35  	"github.com/ethereum/go-ethereum/ethdb"
    36  	"github.com/ethereum/go-ethereum/params"
    37  	"github.com/ethereum/go-ethereum/rlp"
    38  )
    39  
    40  // in this test, we can set n to 1, and it means we can process Istanbul and commit a
    41  // block by one node. Otherwise, if n is larger than 1, we have to generate
    42  // other fake events to process Istanbul.
    43  func newBlockChain(n int) (*core.BlockChain, *backend) {
    44  	genesis, nodeKeys := getGenesisAndKeys(n)
    45  	memDB, _ := ethdb.NewMemDatabase()
    46  	config := istanbul.DefaultConfig
    47  	// Use the first key as private key
    48  	b, _ := New(config, nodeKeys[0], memDB).(*backend)
    49  	genesis.MustCommit(memDB)
    50  	blockchain, err := core.NewBlockChain(memDB, genesis.Config, b, vm.Config{})
    51  	if err != nil {
    52  		panic(err)
    53  	}
    54  	b.Start(blockchain, blockchain.CurrentBlock, blockchain.HasBadBlock)
    55  	snap, err := b.snapshot(blockchain, 0, common.Hash{}, nil)
    56  	if err != nil {
    57  		panic(err)
    58  	}
    59  	if snap == nil {
    60  		panic("failed to get snapshot")
    61  	}
    62  	proposerAddr := snap.ValSet.GetProposer().Address()
    63  
    64  	// find proposer key
    65  	for _, key := range nodeKeys {
    66  		addr := crypto.PubkeyToAddress(key.PublicKey)
    67  		if addr.String() == proposerAddr.String() {
    68  			b.privateKey = key
    69  			b.address = addr
    70  		}
    71  	}
    72  
    73  	return blockchain, b
    74  }
    75  
    76  func getGenesisAndKeys(n int) (*core.Genesis, []*ecdsa.PrivateKey) {
    77  	// Setup validators
    78  	var nodeKeys = make([]*ecdsa.PrivateKey, n)
    79  	var addrs = make([]common.Address, n)
    80  	for i := 0; i < n; i++ {
    81  		nodeKeys[i], _ = crypto.GenerateKey()
    82  		addrs[i] = crypto.PubkeyToAddress(nodeKeys[i].PublicKey)
    83  	}
    84  
    85  	// generate genesis block
    86  	genesis := core.DefaultGenesisBlock()
    87  	genesis.Config = params.TestChainConfig
    88  	// force enable Istanbul engine
    89  	genesis.Config.Istanbul = &params.IstanbulConfig{}
    90  	genesis.Config.Ethash = nil
    91  	genesis.Difficulty = defaultDifficulty
    92  	genesis.Nonce = emptyNonce.Uint64()
    93  	genesis.Mixhash = types.IstanbulDigest
    94  
    95  	appendValidators(genesis, addrs)
    96  	return genesis, nodeKeys
    97  }
    98  
    99  func appendValidators(genesis *core.Genesis, addrs []common.Address) {
   100  
   101  	if len(genesis.ExtraData) < types.IstanbulExtraVanity {
   102  		genesis.ExtraData = append(genesis.ExtraData, bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity)...)
   103  	}
   104  	genesis.ExtraData = genesis.ExtraData[:types.IstanbulExtraVanity]
   105  
   106  	ist := &types.IstanbulExtra{
   107  		Validators:    addrs,
   108  		Seal:          []byte{},
   109  		CommittedSeal: [][]byte{},
   110  	}
   111  
   112  	istPayload, err := rlp.EncodeToBytes(&ist)
   113  	if err != nil {
   114  		panic("failed to encode istanbul extra")
   115  	}
   116  	genesis.ExtraData = append(genesis.ExtraData, istPayload...)
   117  }
   118  
   119  func makeHeader(parent *types.Block, config *istanbul.Config) *types.Header {
   120  	header := &types.Header{
   121  		ParentHash: parent.Hash(),
   122  		Number:     parent.Number().Add(parent.Number(), common.Big1),
   123  		GasLimit:   core.CalcGasLimit(parent),
   124  		GasUsed:    new(big.Int),
   125  		Extra:      parent.Extra(),
   126  		Time:       new(big.Int).Add(parent.Time(), new(big.Int).SetUint64(config.BlockPeriod)),
   127  		Difficulty: defaultDifficulty,
   128  	}
   129  	return header
   130  }
   131  
   132  func makeBlock(chain *core.BlockChain, engine *backend, parent *types.Block) *types.Block {
   133  	block := makeBlockWithoutSeal(chain, engine, parent)
   134  	block, _ = engine.Seal(chain, block, nil)
   135  	return block
   136  }
   137  
   138  func makeBlockWithoutSeal(chain *core.BlockChain, engine *backend, parent *types.Block) *types.Block {
   139  	header := makeHeader(parent, engine.config)
   140  	engine.Prepare(chain, header)
   141  	state, _,_ := chain.StateAt(parent.Root())
   142  	block, _ := engine.Finalize(chain, header, state, nil, nil, nil)
   143  	return block
   144  }
   145  
   146  func TestPrepare(t *testing.T) {
   147  	chain, engine := newBlockChain(1)
   148  	header := makeHeader(chain.Genesis(), engine.config)
   149  	err := engine.Prepare(chain, header)
   150  	if err != nil {
   151  		t.Errorf("error mismatch: have %v, want nil", err)
   152  	}
   153  	header.ParentHash = common.StringToHash("1234567890")
   154  	err = engine.Prepare(chain, header)
   155  	if err != consensus.ErrUnknownAncestor {
   156  		t.Errorf("error mismatch: have %v, want %v", err, consensus.ErrUnknownAncestor)
   157  	}
   158  }
   159  
   160  func TestSealStopChannel(t *testing.T) {
   161  	chain, engine := newBlockChain(4)
   162  	block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
   163  	stop := make(chan struct{}, 1)
   164  	eventSub := engine.EventMux().Subscribe(istanbul.RequestEvent{})
   165  	eventLoop := func() {
   166  		select {
   167  		case ev := <-eventSub.Chan():
   168  			_, ok := ev.Data.(istanbul.RequestEvent)
   169  			if !ok {
   170  				t.Errorf("unexpected event comes: %v", reflect.TypeOf(ev.Data))
   171  			}
   172  			stop <- struct{}{}
   173  		}
   174  		eventSub.Unsubscribe()
   175  	}
   176  	go eventLoop()
   177  	finalBlock, err := engine.Seal(chain, block, stop)
   178  	if err != nil {
   179  		t.Errorf("error mismatch: have %v, want nil", err)
   180  	}
   181  	if finalBlock != nil {
   182  		t.Errorf("block mismatch: have %v, want nil", finalBlock)
   183  	}
   184  }
   185  
   186  func TestSealCommittedOtherHash(t *testing.T) {
   187  	chain, engine := newBlockChain(4)
   188  	block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
   189  	otherBlock := makeBlockWithoutSeal(chain, engine, block)
   190  	eventSub := engine.EventMux().Subscribe(istanbul.RequestEvent{})
   191  	eventLoop := func() {
   192  		select {
   193  		case ev := <-eventSub.Chan():
   194  			_, ok := ev.Data.(istanbul.RequestEvent)
   195  			if !ok {
   196  				t.Errorf("unexpected event comes: %v", reflect.TypeOf(ev.Data))
   197  			}
   198  			engine.Commit(otherBlock, [][]byte{})
   199  		}
   200  		eventSub.Unsubscribe()
   201  	}
   202  	go eventLoop()
   203  	seal := func() {
   204  		engine.Seal(chain, block, nil)
   205  		t.Error("seal should not be completed")
   206  	}
   207  	go seal()
   208  
   209  	const timeoutDura = 2 * time.Second
   210  	timeout := time.NewTimer(timeoutDura)
   211  	select {
   212  	case <-timeout.C:
   213  		// wait 2 seconds to ensure we cannot get any blocks from Istanbul
   214  	}
   215  }
   216  
   217  func TestSealCommitted(t *testing.T) {
   218  	chain, engine := newBlockChain(1)
   219  	block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
   220  	expectedBlock, _ := engine.updateBlock(engine.chain.GetHeader(block.ParentHash(), block.NumberU64()-1), block)
   221  
   222  	finalBlock, err := engine.Seal(chain, block, nil)
   223  	if err != nil {
   224  		t.Errorf("error mismatch: have %v, want nil", err)
   225  	}
   226  	if finalBlock.Hash() != expectedBlock.Hash() {
   227  		t.Errorf("hash mismatch: have %v, want %v", finalBlock.Hash(), expectedBlock.Hash())
   228  	}
   229  }
   230  
   231  func TestVerifyHeader(t *testing.T) {
   232  	chain, engine := newBlockChain(1)
   233  
   234  	// errEmptyCommittedSeals case
   235  	block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
   236  	block, _ = engine.updateBlock(chain.Genesis().Header(), block)
   237  	err := engine.VerifyHeader(chain, block.Header(), false)
   238  	if err != errEmptyCommittedSeals {
   239  		t.Errorf("error mismatch: have %v, want %v", err, errEmptyCommittedSeals)
   240  	}
   241  
   242  	// short extra data
   243  	header := block.Header()
   244  	header.Extra = []byte{}
   245  	err = engine.VerifyHeader(chain, header, false)
   246  	if err != errInvalidExtraDataFormat {
   247  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidExtraDataFormat)
   248  	}
   249  	// incorrect extra format
   250  	header.Extra = []byte("0000000000000000000000000000000012300000000000000000000000000000000000000000000000000000000000000000")
   251  	err = engine.VerifyHeader(chain, header, false)
   252  	if err != errInvalidExtraDataFormat {
   253  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidExtraDataFormat)
   254  	}
   255  
   256  	// non zero MixDigest
   257  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   258  	header = block.Header()
   259  	header.MixDigest = common.StringToHash("123456789")
   260  	err = engine.VerifyHeader(chain, header, false)
   261  	if err != errInvalidMixDigest {
   262  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidMixDigest)
   263  	}
   264  
   265  	// invalid uncles hash
   266  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   267  	header = block.Header()
   268  	header.UncleHash = common.StringToHash("123456789")
   269  	err = engine.VerifyHeader(chain, header, false)
   270  	if err != errInvalidUncleHash {
   271  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidUncleHash)
   272  	}
   273  
   274  	// invalid difficulty
   275  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   276  	header = block.Header()
   277  	header.Difficulty = big.NewInt(2)
   278  	err = engine.VerifyHeader(chain, header, false)
   279  	if err != errInvalidDifficulty {
   280  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidDifficulty)
   281  	}
   282  
   283  	// invalid timestamp
   284  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   285  	header = block.Header()
   286  	header.Time = new(big.Int).Add(chain.Genesis().Time(), new(big.Int).SetUint64(engine.config.BlockPeriod-1))
   287  	err = engine.VerifyHeader(chain, header, false)
   288  	if err != errInvalidTimestamp {
   289  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidTimestamp)
   290  	}
   291  
   292  	// future block
   293  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   294  	header = block.Header()
   295  	header.Time = new(big.Int).Add(big.NewInt(now().Unix()), new(big.Int).SetUint64(10))
   296  	err = engine.VerifyHeader(chain, header, false)
   297  	if err != consensus.ErrFutureBlock {
   298  		t.Errorf("error mismatch: have %v, want %v", err, consensus.ErrFutureBlock)
   299  	}
   300  
   301  	// invalid nonce
   302  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   303  	header = block.Header()
   304  	copy(header.Nonce[:], hexutil.MustDecode("0x111111111111"))
   305  	header.Number = big.NewInt(int64(engine.config.Epoch))
   306  	err = engine.VerifyHeader(chain, header, false)
   307  	if err != errInvalidNonce {
   308  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidNonce)
   309  	}
   310  }
   311  
   312  func TestVerifySeal(t *testing.T) {
   313  	chain, engine := newBlockChain(1)
   314  	genesis := chain.Genesis()
   315  	// cannot verify genesis
   316  	err := engine.VerifySeal(chain, genesis.Header())
   317  	if err != errUnknownBlock {
   318  		t.Errorf("error mismatch: have %v, want %v", err, errUnknownBlock)
   319  	}
   320  
   321  	block := makeBlock(chain, engine, genesis)
   322  	// change block content
   323  	header := block.Header()
   324  	header.Number = big.NewInt(4)
   325  	block1 := block.WithSeal(header)
   326  	err = engine.VerifySeal(chain, block1.Header())
   327  	if err != errUnauthorized {
   328  		t.Errorf("error mismatch: have %v, want %v", err, errUnauthorized)
   329  	}
   330  
   331  	// unauthorized users but still can get correct signer address
   332  	engine.privateKey, _ = crypto.GenerateKey()
   333  	err = engine.VerifySeal(chain, block.Header())
   334  	if err != nil {
   335  		t.Errorf("error mismatch: have %v, want nil", err)
   336  	}
   337  }
   338  
   339  func TestVerifyHeaders(t *testing.T) {
   340  	chain, engine := newBlockChain(1)
   341  	genesis := chain.Genesis()
   342  
   343  	// success case
   344  	headers := []*types.Header{}
   345  	blocks := []*types.Block{}
   346  	size := 100
   347  
   348  	for i := 0; i < size; i++ {
   349  		var b *types.Block
   350  		if i == 0 {
   351  			b = makeBlockWithoutSeal(chain, engine, genesis)
   352  			b, _ = engine.updateBlock(genesis.Header(), b)
   353  		} else {
   354  			b = makeBlockWithoutSeal(chain, engine, blocks[i-1])
   355  			b, _ = engine.updateBlock(blocks[i-1].Header(), b)
   356  		}
   357  		blocks = append(blocks, b)
   358  		headers = append(headers, blocks[i].Header())
   359  	}
   360  	now = func() time.Time {
   361  		return time.Unix(headers[size-1].Time.Int64(), 0)
   362  	}
   363  	_, results := engine.VerifyHeaders(chain, headers, nil)
   364  	const timeoutDura = 2 * time.Second
   365  	timeout := time.NewTimer(timeoutDura)
   366  	index := 0
   367  OUT1:
   368  	for {
   369  		select {
   370  		case err := <-results:
   371  			if err != nil {
   372  				if err != errEmptyCommittedSeals && err != errInvalidCommittedSeals {
   373  					t.Errorf("error mismatch: have %v, want errEmptyCommittedSeals|errInvalidCommittedSeals", err)
   374  					break OUT1
   375  				}
   376  			}
   377  			index++
   378  			if index == size {
   379  				break OUT1
   380  			}
   381  		case <-timeout.C:
   382  			break OUT1
   383  		}
   384  	}
   385  	// abort cases
   386  	abort, results := engine.VerifyHeaders(chain, headers, nil)
   387  	timeout = time.NewTimer(timeoutDura)
   388  	index = 0
   389  OUT2:
   390  	for {
   391  		select {
   392  		case err := <-results:
   393  			if err != nil {
   394  				if err != errEmptyCommittedSeals && err != errInvalidCommittedSeals {
   395  					t.Errorf("error mismatch: have %v, want errEmptyCommittedSeals|errInvalidCommittedSeals", err)
   396  					break OUT2
   397  				}
   398  			}
   399  			index++
   400  			if index == 5 {
   401  				abort <- struct{}{}
   402  			}
   403  			if index >= size {
   404  				t.Errorf("verifyheaders should be aborted")
   405  				break OUT2
   406  			}
   407  		case <-timeout.C:
   408  			break OUT2
   409  		}
   410  	}
   411  	// error header cases
   412  	headers[2].Number = big.NewInt(100)
   413  	abort, results = engine.VerifyHeaders(chain, headers, nil)
   414  	timeout = time.NewTimer(timeoutDura)
   415  	index = 0
   416  	errors := 0
   417  	expectedErrors := 2
   418  OUT3:
   419  	for {
   420  		select {
   421  		case err := <-results:
   422  			if err != nil {
   423  				if err != errEmptyCommittedSeals && err != errInvalidCommittedSeals {
   424  					errors++
   425  				}
   426  			}
   427  			index++
   428  			if index == size {
   429  				if errors != expectedErrors {
   430  					t.Errorf("error mismatch: have %v, want %v", err, expectedErrors)
   431  				}
   432  				break OUT3
   433  			}
   434  		case <-timeout.C:
   435  			break OUT3
   436  		}
   437  	}
   438  }
   439  
   440  func TestPrepareExtra(t *testing.T) {
   441  	validators := make([]common.Address, 4)
   442  	validators[0] = common.BytesToAddress(hexutil.MustDecode("0x44add0ec310f115a0e603b2d7db9f067778eaf8a"))
   443  	validators[1] = common.BytesToAddress(hexutil.MustDecode("0x294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212"))
   444  	validators[2] = common.BytesToAddress(hexutil.MustDecode("0x6beaaed781d2d2ab6350f5c4566a2c6eaac407a6"))
   445  	validators[3] = common.BytesToAddress(hexutil.MustDecode("0x8be76812f765c24641ec63dc2852b378aba2b440"))
   446  
   447  	vanity := make([]byte, types.IstanbulExtraVanity)
   448  	expectedResult := append(vanity, hexutil.MustDecode("0xf858f8549444add0ec310f115a0e603b2d7db9f067778eaf8a94294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212946beaaed781d2d2ab6350f5c4566a2c6eaac407a6948be76812f765c24641ec63dc2852b378aba2b44080c0")...)
   449  
   450  	h := &types.Header{
   451  		Extra: vanity,
   452  	}
   453  
   454  	payload, err := prepareExtra(h, validators)
   455  	if err != nil {
   456  		t.Errorf("error mismatch: have %v, want: nil", err)
   457  	}
   458  	if !reflect.DeepEqual(payload, expectedResult) {
   459  		t.Errorf("payload mismatch: have %v, want %v", payload, expectedResult)
   460  	}
   461  
   462  	// append useless information to extra-data
   463  	h.Extra = append(vanity, make([]byte, 15)...)
   464  
   465  	payload, err = prepareExtra(h, validators)
   466  	if !reflect.DeepEqual(payload, expectedResult) {
   467  		t.Errorf("payload mismatch: have %v, want %v", payload, expectedResult)
   468  	}
   469  }
   470  
   471  func TestWriteSeal(t *testing.T) {
   472  	vanity := bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity)
   473  	istRawData := hexutil.MustDecode("0xf858f8549444add0ec310f115a0e603b2d7db9f067778eaf8a94294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212946beaaed781d2d2ab6350f5c4566a2c6eaac407a6948be76812f765c24641ec63dc2852b378aba2b44080c0")
   474  	expectedSeal := append([]byte{1, 2, 3}, bytes.Repeat([]byte{0x00}, types.IstanbulExtraSeal-3)...)
   475  	expectedIstExtra := &types.IstanbulExtra{
   476  		Validators: []common.Address{
   477  			common.BytesToAddress(hexutil.MustDecode("0x44add0ec310f115a0e603b2d7db9f067778eaf8a")),
   478  			common.BytesToAddress(hexutil.MustDecode("0x294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212")),
   479  			common.BytesToAddress(hexutil.MustDecode("0x6beaaed781d2d2ab6350f5c4566a2c6eaac407a6")),
   480  			common.BytesToAddress(hexutil.MustDecode("0x8be76812f765c24641ec63dc2852b378aba2b440")),
   481  		},
   482  		Seal:          expectedSeal,
   483  		CommittedSeal: [][]byte{},
   484  	}
   485  	var expectedErr error
   486  
   487  	h := &types.Header{
   488  		Extra: append(vanity, istRawData...),
   489  	}
   490  
   491  	// normal case
   492  	err := writeSeal(h, expectedSeal)
   493  	if err != expectedErr {
   494  		t.Errorf("error mismatch: have %v, want %v", err, expectedErr)
   495  	}
   496  
   497  	// verify istanbul extra-data
   498  	istExtra, err := types.ExtractIstanbulExtra(h)
   499  	if err != nil {
   500  		t.Errorf("error mismatch: have %v, want nil", err)
   501  	}
   502  	if !reflect.DeepEqual(istExtra, expectedIstExtra) {
   503  		t.Errorf("extra data mismatch: have %v, want %v", istExtra, expectedIstExtra)
   504  	}
   505  
   506  	// invalid seal
   507  	unexpectedSeal := append(expectedSeal, make([]byte, 1)...)
   508  	err = writeSeal(h, unexpectedSeal)
   509  	if err != errInvalidSignature {
   510  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidSignature)
   511  	}
   512  }
   513  
   514  func TestWriteCommittedSeals(t *testing.T) {
   515  	vanity := bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity)
   516  	istRawData := hexutil.MustDecode("0xf858f8549444add0ec310f115a0e603b2d7db9f067778eaf8a94294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212946beaaed781d2d2ab6350f5c4566a2c6eaac407a6948be76812f765c24641ec63dc2852b378aba2b44080c0")
   517  	expectedCommittedSeal := append([]byte{1, 2, 3}, bytes.Repeat([]byte{0x00}, types.IstanbulExtraSeal-3)...)
   518  	expectedIstExtra := &types.IstanbulExtra{
   519  		Validators: []common.Address{
   520  			common.BytesToAddress(hexutil.MustDecode("0x44add0ec310f115a0e603b2d7db9f067778eaf8a")),
   521  			common.BytesToAddress(hexutil.MustDecode("0x294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212")),
   522  			common.BytesToAddress(hexutil.MustDecode("0x6beaaed781d2d2ab6350f5c4566a2c6eaac407a6")),
   523  			common.BytesToAddress(hexutil.MustDecode("0x8be76812f765c24641ec63dc2852b378aba2b440")),
   524  		},
   525  		Seal:          []byte{},
   526  		CommittedSeal: [][]byte{expectedCommittedSeal},
   527  	}
   528  	var expectedErr error
   529  
   530  	h := &types.Header{
   531  		Extra: append(vanity, istRawData...),
   532  	}
   533  
   534  	// normal case
   535  	err := writeCommittedSeals(h, [][]byte{expectedCommittedSeal})
   536  	if err != expectedErr {
   537  		t.Errorf("error mismatch: have %v, want %v", err, expectedErr)
   538  	}
   539  
   540  	// verify istanbul extra-data
   541  	istExtra, err := types.ExtractIstanbulExtra(h)
   542  	if err != nil {
   543  		t.Errorf("error mismatch: have %v, want nil", err)
   544  	}
   545  	if !reflect.DeepEqual(istExtra, expectedIstExtra) {
   546  		t.Errorf("extra data mismatch: have %v, want %v", istExtra, expectedIstExtra)
   547  	}
   548  
   549  	// invalid seal
   550  	unexpectedCommittedSeal := append(expectedCommittedSeal, make([]byte, 1)...)
   551  	err = writeCommittedSeals(h, [][]byte{unexpectedCommittedSeal})
   552  	if err != errInvalidCommittedSeals {
   553  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidCommittedSeals)
   554  	}
   555  }