github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/consensus/bft/backend/engine_test.go (about)

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