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

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