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