github.com/ConsenSys/Quorum@v20.10.0+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/rawdb"
    33  	"github.com/ethereum/go-ethereum/core/types"
    34  	"github.com/ethereum/go-ethereum/core/vm"
    35  	"github.com/ethereum/go-ethereum/crypto"
    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 := rawdb.NewMemoryDatabase()
    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, nil, genesis.Config, b, vm.Config{}, nil)
    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, parent.GasLimit(), parent.GasLimit()),
   124  		GasUsed:    0,
   125  		Extra:      parent.Extra(),
   126  		Time:       parent.Time() + config.BlockPeriod,
   127  
   128  		Difficulty: defaultDifficulty,
   129  	}
   130  	return header
   131  }
   132  
   133  func makeBlock(chain *core.BlockChain, engine *backend, parent *types.Block) *types.Block {
   134  	block := makeBlockWithoutSeal(chain, engine, parent)
   135  	stopCh := make(chan struct{})
   136  	resultCh := make(chan *types.Block, 10)
   137  	go engine.Seal(chain, block, resultCh, stopCh)
   138  	blk := <-resultCh
   139  	return blk
   140  }
   141  
   142  func makeBlockWithoutSeal(chain *core.BlockChain, engine *backend, parent *types.Block) *types.Block {
   143  	header := makeHeader(parent, engine.config)
   144  	engine.Prepare(chain, header)
   145  	state, _, _ := chain.StateAt(parent.Root())
   146  	block, _ := engine.FinalizeAndAssemble(chain, header, state, nil, nil, nil)
   147  	return block
   148  }
   149  
   150  func TestPrepare(t *testing.T) {
   151  	chain, engine := newBlockChain(1)
   152  	header := makeHeader(chain.Genesis(), engine.config)
   153  	err := engine.Prepare(chain, header)
   154  	if err != nil {
   155  		t.Errorf("error mismatch: have %v, want nil", err)
   156  	}
   157  	header.ParentHash = common.StringToHash("1234567890")
   158  	err = engine.Prepare(chain, header)
   159  	if err != consensus.ErrUnknownAncestor {
   160  		t.Errorf("error mismatch: have %v, want %v", err, consensus.ErrUnknownAncestor)
   161  	}
   162  }
   163  
   164  func TestSealStopChannel(t *testing.T) {
   165  	chain, engine := newBlockChain(4)
   166  	block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
   167  	stop := make(chan struct{}, 1)
   168  	eventSub := engine.EventMux().Subscribe(istanbul.RequestEvent{})
   169  	eventLoop := func() {
   170  		ev := <-eventSub.Chan()
   171  		_, ok := ev.Data.(istanbul.RequestEvent)
   172  		if !ok {
   173  			t.Errorf("unexpected event comes: %v", reflect.TypeOf(ev.Data))
   174  		}
   175  		stop <- struct{}{}
   176  		eventSub.Unsubscribe()
   177  	}
   178  	go eventLoop()
   179  	resultCh := make(chan *types.Block, 10)
   180  	go func() {
   181  		err := engine.Seal(chain, block, resultCh, stop)
   182  		if err != nil {
   183  			t.Errorf("error mismatch: have %v, want nil", err)
   184  		}
   185  	}()
   186  
   187  	finalBlock := <-resultCh
   188  	if finalBlock != nil {
   189  		t.Errorf("block mismatch: have %v, want nil", finalBlock)
   190  	}
   191  }
   192  
   193  func TestSealCommittedOtherHash(t *testing.T) {
   194  	chain, engine := newBlockChain(4)
   195  	block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
   196  	otherBlock := makeBlockWithoutSeal(chain, engine, block)
   197  	expectedCommittedSeal := append([]byte{1, 2, 3}, bytes.Repeat([]byte{0x00}, types.IstanbulExtraSeal-3)...)
   198  
   199  	eventSub := engine.EventMux().Subscribe(istanbul.RequestEvent{})
   200  	blockOutputChannel := make(chan *types.Block)
   201  	stopChannel := make(chan struct{})
   202  
   203  	go func() {
   204  		ev := <-eventSub.Chan()
   205  		if _, ok := ev.Data.(istanbul.RequestEvent); !ok {
   206  			t.Errorf("unexpected event comes: %v", reflect.TypeOf(ev.Data))
   207  		}
   208  		if err := engine.Commit(otherBlock, [][]byte{expectedCommittedSeal}); err != nil {
   209  			t.Error(err.Error())
   210  		}
   211  		eventSub.Unsubscribe()
   212  	}()
   213  
   214  	go func() {
   215  		if err := engine.Seal(chain, block, blockOutputChannel, stopChannel); err != nil {
   216  			t.Error(err.Error())
   217  		}
   218  	}()
   219  
   220  	select {
   221  	case <-blockOutputChannel:
   222  		t.Error("Wrong block found!")
   223  	default:
   224  		//no block found, stop the sealing
   225  		close(stopChannel)
   226  	}
   227  
   228  	output := <-blockOutputChannel
   229  	if output != nil {
   230  		t.Error("Block not nil!")
   231  	}
   232  }
   233  
   234  func TestSealCommitted(t *testing.T) {
   235  	chain, engine := newBlockChain(1)
   236  	block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
   237  	expectedBlock, _ := engine.updateBlock(engine.chain.GetHeader(block.ParentHash(), block.NumberU64()-1), block)
   238  	resultCh := make(chan *types.Block, 10)
   239  	go func() {
   240  		err := engine.Seal(chain, block, resultCh, make(chan struct{}))
   241  
   242  		if err != nil {
   243  			t.Errorf("error mismatch: have %v, want %v", err, expectedBlock)
   244  		}
   245  	}()
   246  
   247  	finalBlock := <-resultCh
   248  	if finalBlock.Hash() != expectedBlock.Hash() {
   249  		t.Errorf("hash mismatch: have %v, want %v", finalBlock.Hash(), expectedBlock.Hash())
   250  	}
   251  }
   252  
   253  func TestVerifyHeader(t *testing.T) {
   254  	chain, engine := newBlockChain(1)
   255  
   256  	// errEmptyCommittedSeals case
   257  	block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
   258  	block, _ = engine.updateBlock(chain.Genesis().Header(), block)
   259  	err := engine.VerifyHeader(chain, block.Header(), false)
   260  	if err != errEmptyCommittedSeals {
   261  		t.Errorf("error mismatch: have %v, want %v", err, errEmptyCommittedSeals)
   262  	}
   263  
   264  	// short extra data
   265  	header := block.Header()
   266  	header.Extra = []byte{}
   267  	err = engine.VerifyHeader(chain, header, false)
   268  	if err != errInvalidExtraDataFormat {
   269  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidExtraDataFormat)
   270  	}
   271  	// incorrect extra format
   272  	header.Extra = []byte("0000000000000000000000000000000012300000000000000000000000000000000000000000000000000000000000000000")
   273  	err = engine.VerifyHeader(chain, header, false)
   274  	if err != errInvalidExtraDataFormat {
   275  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidExtraDataFormat)
   276  	}
   277  
   278  	// non zero MixDigest
   279  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   280  	header = block.Header()
   281  	header.MixDigest = common.StringToHash("123456789")
   282  	err = engine.VerifyHeader(chain, header, false)
   283  	if err != errInvalidMixDigest {
   284  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidMixDigest)
   285  	}
   286  
   287  	// invalid uncles hash
   288  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   289  	header = block.Header()
   290  	header.UncleHash = common.StringToHash("123456789")
   291  	err = engine.VerifyHeader(chain, header, false)
   292  	if err != errInvalidUncleHash {
   293  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidUncleHash)
   294  	}
   295  
   296  	// invalid difficulty
   297  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   298  	header = block.Header()
   299  	header.Difficulty = big.NewInt(2)
   300  	err = engine.VerifyHeader(chain, header, false)
   301  	if err != errInvalidDifficulty {
   302  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidDifficulty)
   303  	}
   304  
   305  	// invalid timestamp
   306  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   307  	header = block.Header()
   308  	header.Time = chain.Genesis().Time() + (engine.config.BlockPeriod - 1)
   309  	err = engine.VerifyHeader(chain, header, false)
   310  	if err != errInvalidTimestamp {
   311  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidTimestamp)
   312  	}
   313  
   314  	// future block
   315  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   316  	header = block.Header()
   317  	header.Time = uint64(now().Unix() + 10)
   318  	err = engine.VerifyHeader(chain, header, false)
   319  	if err != consensus.ErrFutureBlock {
   320  		t.Errorf("error mismatch: have %v, want %v", err, consensus.ErrFutureBlock)
   321  	}
   322  
   323  	// future block which is within AllowedFutureBlockTime
   324  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   325  	header = block.Header()
   326  	header.Time = new(big.Int).Add(big.NewInt(now().Unix()), new(big.Int).SetUint64(10)).Uint64()
   327  	priorValue := engine.config.AllowedFutureBlockTime
   328  	engine.config.AllowedFutureBlockTime = 10
   329  	err = engine.VerifyHeader(chain, header, false)
   330  	engine.config.AllowedFutureBlockTime = priorValue //restore changed value
   331  	if err == consensus.ErrFutureBlock {
   332  		t.Errorf("error mismatch: have %v, want nil", err)
   333  	}
   334  
   335  	// invalid nonce
   336  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   337  	header = block.Header()
   338  	copy(header.Nonce[:], hexutil.MustDecode("0x111111111111"))
   339  	header.Number = big.NewInt(int64(engine.config.Epoch))
   340  	err = engine.VerifyHeader(chain, header, false)
   341  	if err != errInvalidNonce {
   342  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidNonce)
   343  	}
   344  }
   345  
   346  func TestVerifySeal(t *testing.T) {
   347  	chain, engine := newBlockChain(1)
   348  	genesis := chain.Genesis()
   349  	// cannot verify genesis
   350  	err := engine.VerifySeal(chain, genesis.Header())
   351  	if err != errUnknownBlock {
   352  		t.Errorf("error mismatch: have %v, want %v", err, errUnknownBlock)
   353  	}
   354  
   355  	block := makeBlock(chain, engine, genesis)
   356  	// change block content
   357  	header := block.Header()
   358  	header.Number = big.NewInt(4)
   359  	block1 := block.WithSeal(header)
   360  	err = engine.VerifySeal(chain, block1.Header())
   361  	if err != errUnauthorized {
   362  		t.Errorf("error mismatch: have %v, want %v", err, errUnauthorized)
   363  	}
   364  
   365  	// unauthorized users but still can get correct signer address
   366  	engine.privateKey, _ = crypto.GenerateKey()
   367  	err = engine.VerifySeal(chain, block.Header())
   368  	if err != nil {
   369  		t.Errorf("error mismatch: have %v, want nil", err)
   370  	}
   371  }
   372  
   373  func TestVerifyHeaders(t *testing.T) {
   374  	chain, engine := newBlockChain(1)
   375  	genesis := chain.Genesis()
   376  
   377  	// success case
   378  	headers := []*types.Header{}
   379  	blocks := []*types.Block{}
   380  	size := 100
   381  
   382  	for i := 0; i < size; i++ {
   383  		var b *types.Block
   384  		if i == 0 {
   385  			b = makeBlockWithoutSeal(chain, engine, genesis)
   386  			b, _ = engine.updateBlock(genesis.Header(), b)
   387  		} else {
   388  			b = makeBlockWithoutSeal(chain, engine, blocks[i-1])
   389  			b, _ = engine.updateBlock(blocks[i-1].Header(), b)
   390  		}
   391  		blocks = append(blocks, b)
   392  		headers = append(headers, blocks[i].Header())
   393  	}
   394  	now = func() time.Time {
   395  		return time.Unix(int64(headers[size-1].Time), 0)
   396  	}
   397  	_, results := engine.VerifyHeaders(chain, headers, nil)
   398  	const timeoutDura = 2 * time.Second
   399  	timeout := time.NewTimer(timeoutDura)
   400  	index := 0
   401  OUT1:
   402  	for {
   403  		select {
   404  		case err := <-results:
   405  			if err != nil {
   406  				if err != errEmptyCommittedSeals && err != errInvalidCommittedSeals && err != consensus.ErrUnknownAncestor {
   407  					t.Errorf("error mismatch: have %v, want errEmptyCommittedSeals|errInvalidCommittedSeals|ErrUnknownAncestor", err)
   408  					break OUT1
   409  				}
   410  			}
   411  			index++
   412  			if index == size {
   413  				break OUT1
   414  			}
   415  		case <-timeout.C:
   416  			break OUT1
   417  		}
   418  	}
   419  	_, results = engine.VerifyHeaders(chain, headers, nil)
   420  	timeout = time.NewTimer(timeoutDura)
   421  	index = 0
   422  OUT2:
   423  	for {
   424  		select {
   425  		case err := <-results:
   426  			if err != nil {
   427  				if err != errEmptyCommittedSeals && err != errInvalidCommittedSeals && err != consensus.ErrUnknownAncestor {
   428  					t.Errorf("error mismatch: have %v, want errEmptyCommittedSeals|errInvalidCommittedSeals|ErrUnknownAncestor", err)
   429  					break OUT2
   430  				}
   431  			}
   432  		case <-timeout.C:
   433  			break OUT2
   434  		}
   435  	}
   436  	// error header cases
   437  	headers[2].Number = big.NewInt(100)
   438  	_, results = engine.VerifyHeaders(chain, headers, nil)
   439  	timeout = time.NewTimer(timeoutDura)
   440  	index = 0
   441  	errors := 0
   442  	expectedErrors := 0
   443  OUT3:
   444  	for {
   445  		select {
   446  		case err := <-results:
   447  			if err != nil {
   448  				if err != errEmptyCommittedSeals && err != errInvalidCommittedSeals && err != consensus.ErrUnknownAncestor {
   449  					errors++
   450  				}
   451  			}
   452  			index++
   453  			if index == size {
   454  				if errors != expectedErrors {
   455  					t.Errorf("error mismatch: have %v, want %v", errors, expectedErrors)
   456  				}
   457  				break OUT3
   458  			}
   459  		case <-timeout.C:
   460  			break OUT3
   461  		}
   462  	}
   463  }
   464  
   465  func TestPrepareExtra(t *testing.T) {
   466  	validators := make([]common.Address, 4)
   467  	validators[0] = common.BytesToAddress(hexutil.MustDecode("0x44add0ec310f115a0e603b2d7db9f067778eaf8a"))
   468  	validators[1] = common.BytesToAddress(hexutil.MustDecode("0x294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212"))
   469  	validators[2] = common.BytesToAddress(hexutil.MustDecode("0x6beaaed781d2d2ab6350f5c4566a2c6eaac407a6"))
   470  	validators[3] = common.BytesToAddress(hexutil.MustDecode("0x8be76812f765c24641ec63dc2852b378aba2b440"))
   471  
   472  	vanity := make([]byte, types.IstanbulExtraVanity)
   473  	expectedResult := append(vanity, hexutil.MustDecode("0xf858f8549444add0ec310f115a0e603b2d7db9f067778eaf8a94294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212946beaaed781d2d2ab6350f5c4566a2c6eaac407a6948be76812f765c24641ec63dc2852b378aba2b44080c0")...)
   474  
   475  	h := &types.Header{
   476  		Extra: vanity,
   477  	}
   478  
   479  	payload, err := prepareExtra(h, validators)
   480  	if err != nil {
   481  		t.Errorf("error mismatch: have %v, want: nil", err)
   482  	}
   483  	if !reflect.DeepEqual(payload, expectedResult) {
   484  		t.Errorf("payload mismatch: have %v, want %v", payload, expectedResult)
   485  	}
   486  
   487  	// append useless information to extra-data
   488  	h.Extra = append(vanity, make([]byte, 15)...)
   489  
   490  	payload, err = prepareExtra(h, validators)
   491  	if !reflect.DeepEqual(payload, expectedResult) {
   492  		t.Errorf("payload mismatch: have %v, want %v", payload, expectedResult)
   493  	}
   494  }
   495  
   496  func TestWriteSeal(t *testing.T) {
   497  	vanity := bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity)
   498  	istRawData := hexutil.MustDecode("0xf858f8549444add0ec310f115a0e603b2d7db9f067778eaf8a94294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212946beaaed781d2d2ab6350f5c4566a2c6eaac407a6948be76812f765c24641ec63dc2852b378aba2b44080c0")
   499  	expectedSeal := append([]byte{1, 2, 3}, bytes.Repeat([]byte{0x00}, types.IstanbulExtraSeal-3)...)
   500  	expectedIstExtra := &types.IstanbulExtra{
   501  		Validators: []common.Address{
   502  			common.BytesToAddress(hexutil.MustDecode("0x44add0ec310f115a0e603b2d7db9f067778eaf8a")),
   503  			common.BytesToAddress(hexutil.MustDecode("0x294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212")),
   504  			common.BytesToAddress(hexutil.MustDecode("0x6beaaed781d2d2ab6350f5c4566a2c6eaac407a6")),
   505  			common.BytesToAddress(hexutil.MustDecode("0x8be76812f765c24641ec63dc2852b378aba2b440")),
   506  		},
   507  		Seal:          expectedSeal,
   508  		CommittedSeal: [][]byte{},
   509  	}
   510  	var expectedErr error
   511  
   512  	h := &types.Header{
   513  		Extra: append(vanity, istRawData...),
   514  	}
   515  
   516  	// normal case
   517  	err := writeSeal(h, expectedSeal)
   518  	if err != expectedErr {
   519  		t.Errorf("error mismatch: have %v, want %v", err, expectedErr)
   520  	}
   521  
   522  	// verify istanbul extra-data
   523  	istExtra, err := types.ExtractIstanbulExtra(h)
   524  	if err != nil {
   525  		t.Errorf("error mismatch: have %v, want nil", err)
   526  	}
   527  	if !reflect.DeepEqual(istExtra, expectedIstExtra) {
   528  		t.Errorf("extra data mismatch: have %v, want %v", istExtra, expectedIstExtra)
   529  	}
   530  
   531  	// invalid seal
   532  	unexpectedSeal := append(expectedSeal, make([]byte, 1)...)
   533  	err = writeSeal(h, unexpectedSeal)
   534  	if err != errInvalidSignature {
   535  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidSignature)
   536  	}
   537  }
   538  
   539  func TestWriteCommittedSeals(t *testing.T) {
   540  	vanity := bytes.Repeat([]byte{0x00}, types.IstanbulExtraVanity)
   541  	istRawData := hexutil.MustDecode("0xf858f8549444add0ec310f115a0e603b2d7db9f067778eaf8a94294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212946beaaed781d2d2ab6350f5c4566a2c6eaac407a6948be76812f765c24641ec63dc2852b378aba2b44080c0")
   542  	expectedCommittedSeal := append([]byte{1, 2, 3}, bytes.Repeat([]byte{0x00}, types.IstanbulExtraSeal-3)...)
   543  	expectedIstExtra := &types.IstanbulExtra{
   544  		Validators: []common.Address{
   545  			common.BytesToAddress(hexutil.MustDecode("0x44add0ec310f115a0e603b2d7db9f067778eaf8a")),
   546  			common.BytesToAddress(hexutil.MustDecode("0x294fc7e8f22b3bcdcf955dd7ff3ba2ed833f8212")),
   547  			common.BytesToAddress(hexutil.MustDecode("0x6beaaed781d2d2ab6350f5c4566a2c6eaac407a6")),
   548  			common.BytesToAddress(hexutil.MustDecode("0x8be76812f765c24641ec63dc2852b378aba2b440")),
   549  		},
   550  		Seal:          []byte{},
   551  		CommittedSeal: [][]byte{expectedCommittedSeal},
   552  	}
   553  	var expectedErr error
   554  
   555  	h := &types.Header{
   556  		Extra: append(vanity, istRawData...),
   557  	}
   558  
   559  	// normal case
   560  	err := writeCommittedSeals(h, [][]byte{expectedCommittedSeal})
   561  	if err != expectedErr {
   562  		t.Errorf("error mismatch: have %v, want %v", err, expectedErr)
   563  	}
   564  
   565  	// verify istanbul extra-data
   566  	istExtra, err := types.ExtractIstanbulExtra(h)
   567  	if err != nil {
   568  		t.Errorf("error mismatch: have %v, want nil", err)
   569  	}
   570  	if !reflect.DeepEqual(istExtra, expectedIstExtra) {
   571  		t.Errorf("extra data mismatch: have %v, want %v", istExtra, expectedIstExtra)
   572  	}
   573  
   574  	// invalid seal
   575  	unexpectedCommittedSeal := append(expectedCommittedSeal, make([]byte, 1)...)
   576  	err = writeCommittedSeals(h, [][]byte{unexpectedCommittedSeal})
   577  	if err != errInvalidCommittedSeals {
   578  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidCommittedSeals)
   579  	}
   580  }