github.com/aakash4dev/cometbft@v0.38.2/state/execution_test.go (about)

     1  package state_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/mock"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	dbm "github.com/aakash4dev/cometbft-db"
    14  
    15  	abciclientmocks "github.com/aakash4dev/cometbft/abci/client/mocks"
    16  	abci "github.com/aakash4dev/cometbft/abci/types"
    17  	abcimocks "github.com/aakash4dev/cometbft/abci/types/mocks"
    18  	"github.com/aakash4dev/cometbft/crypto"
    19  	"github.com/aakash4dev/cometbft/crypto/ed25519"
    20  	cryptoenc "github.com/aakash4dev/cometbft/crypto/encoding"
    21  	"github.com/aakash4dev/cometbft/crypto/tmhash"
    22  	"github.com/aakash4dev/cometbft/internal/test"
    23  	"github.com/aakash4dev/cometbft/libs/log"
    24  	mpmocks "github.com/aakash4dev/cometbft/mempool/mocks"
    25  	cmtproto "github.com/aakash4dev/cometbft/proto/tendermint/types"
    26  	cmtversion "github.com/aakash4dev/cometbft/proto/tendermint/version"
    27  	"github.com/aakash4dev/cometbft/proxy"
    28  	pmocks "github.com/aakash4dev/cometbft/proxy/mocks"
    29  	sm "github.com/aakash4dev/cometbft/state"
    30  	"github.com/aakash4dev/cometbft/state/mocks"
    31  	"github.com/aakash4dev/cometbft/store"
    32  	"github.com/aakash4dev/cometbft/types"
    33  	cmttime "github.com/aakash4dev/cometbft/types/time"
    34  	"github.com/aakash4dev/cometbft/version"
    35  )
    36  
    37  var (
    38  	chainID             = "execution_chain"
    39  	testPartSize uint32 = 65536
    40  )
    41  
    42  func TestApplyBlock(t *testing.T) {
    43  	app := &testApp{}
    44  	cc := proxy.NewLocalClientCreator(app)
    45  	proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
    46  	err := proxyApp.Start()
    47  	require.Nil(t, err)
    48  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
    49  
    50  	state, stateDB, _ := makeState(1, 1)
    51  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
    52  		DiscardABCIResponses: false,
    53  	})
    54  	blockStore := store.NewBlockStore(dbm.NewMemDB())
    55  
    56  	mp := &mpmocks.Mempool{}
    57  	mp.On("Lock").Return()
    58  	mp.On("Unlock").Return()
    59  	mp.On("FlushAppConn", mock.Anything).Return(nil)
    60  	mp.On("Update",
    61  		mock.Anything,
    62  		mock.Anything,
    63  		mock.Anything,
    64  		mock.Anything,
    65  		mock.Anything,
    66  		mock.Anything).Return(nil)
    67  	blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
    68  		mp, sm.EmptyEvidencePool{}, blockStore)
    69  
    70  	block := makeBlock(state, 1, new(types.Commit))
    71  	bps, err := block.MakePartSet(testPartSize)
    72  	require.NoError(t, err)
    73  	blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
    74  
    75  	state, err = blockExec.ApplyBlock(state, blockID, block)
    76  	require.Nil(t, err)
    77  
    78  	// TODO check state and mempool
    79  	assert.EqualValues(t, 1, state.Version.Consensus.App, "App version wasn't updated")
    80  }
    81  
    82  // TestFinalizeBlockDecidedLastCommit ensures we correctly send the
    83  // DecidedLastCommit to the application. The test ensures that the
    84  // DecidedLastCommit properly reflects which validators signed the preceding
    85  // block.
    86  func TestFinalizeBlockDecidedLastCommit(t *testing.T) {
    87  	app := &testApp{}
    88  	baseTime := time.Now()
    89  	cc := proxy.NewLocalClientCreator(app)
    90  	proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
    91  	err := proxyApp.Start()
    92  	require.NoError(t, err)
    93  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
    94  
    95  	state, stateDB, privVals := makeState(7, 1)
    96  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
    97  		DiscardABCIResponses: false,
    98  	})
    99  	absentSig := types.NewExtendedCommitSigAbsent()
   100  
   101  	testCases := []struct {
   102  		name             string
   103  		absentCommitSigs map[int]bool
   104  	}{
   105  		{"none absent", map[int]bool{}},
   106  		{"one absent", map[int]bool{1: true}},
   107  		{"multiple absent", map[int]bool{1: true, 3: true}},
   108  	}
   109  
   110  	for _, tc := range testCases {
   111  		t.Run(tc.name, func(t *testing.T) {
   112  			blockStore := store.NewBlockStore(dbm.NewMemDB())
   113  			evpool := &mocks.EvidencePool{}
   114  			evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, 0)
   115  			evpool.On("Update", mock.Anything, mock.Anything).Return()
   116  			evpool.On("CheckEvidence", mock.Anything).Return(nil)
   117  			mp := &mpmocks.Mempool{}
   118  			mp.On("Lock").Return()
   119  			mp.On("Unlock").Return()
   120  			mp.On("FlushAppConn", mock.Anything).Return(nil)
   121  			mp.On("Update",
   122  				mock.Anything,
   123  				mock.Anything,
   124  				mock.Anything,
   125  				mock.Anything,
   126  				mock.Anything,
   127  				mock.Anything,
   128  				mock.Anything).Return(nil)
   129  
   130  			eventBus := types.NewEventBus()
   131  			require.NoError(t, eventBus.Start())
   132  
   133  			blockExec := sm.NewBlockExecutor(stateStore, log.NewNopLogger(), proxyApp.Consensus(), mp, evpool, blockStore)
   134  			state, _, lastCommit, err := makeAndCommitGoodBlock(state, 1, new(types.Commit), state.NextValidators.Validators[0].Address, blockExec, privVals, nil)
   135  			require.NoError(t, err)
   136  
   137  			for idx, isAbsent := range tc.absentCommitSigs {
   138  				if isAbsent {
   139  					lastCommit.ExtendedSignatures[idx] = absentSig
   140  				}
   141  			}
   142  
   143  			// block for height 2
   144  			block := makeBlock(state, 2, lastCommit.ToCommit())
   145  			bps, err := block.MakePartSet(testPartSize)
   146  			require.NoError(t, err)
   147  			blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
   148  			_, err = blockExec.ApplyBlock(state, blockID, block)
   149  			require.NoError(t, err)
   150  			require.True(t, app.LastTime.After(baseTime))
   151  
   152  			// -> app receives a list of validators with a bool indicating if they signed
   153  			for i, v := range app.CommitVotes {
   154  				_, absent := tc.absentCommitSigs[i]
   155  				assert.Equal(t, !absent, v.BlockIdFlag != cmtproto.BlockIDFlagAbsent)
   156  			}
   157  		})
   158  	}
   159  }
   160  
   161  // TestFinalizeBlockValidators ensures we send absent validators list.
   162  func TestFinalizeBlockValidators(t *testing.T) {
   163  	app := &testApp{}
   164  	cc := proxy.NewLocalClientCreator(app)
   165  	proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
   166  	err := proxyApp.Start()
   167  	require.NoError(t, err)
   168  	defer proxyApp.Stop() //nolint:errcheck // no need to check error again
   169  
   170  	state, stateDB, _ := makeState(2, 2)
   171  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   172  		DiscardABCIResponses: false,
   173  	})
   174  
   175  	prevHash := state.LastBlockID.Hash
   176  	prevParts := types.PartSetHeader{}
   177  	prevBlockID := types.BlockID{Hash: prevHash, PartSetHeader: prevParts}
   178  
   179  	var (
   180  		now        = cmttime.Now()
   181  		commitSig0 = types.ExtendedCommitSig{
   182  			CommitSig: types.CommitSig{
   183  				BlockIDFlag:      types.BlockIDFlagCommit,
   184  				ValidatorAddress: state.Validators.Validators[0].Address,
   185  				Timestamp:        now,
   186  				Signature:        []byte("Signature1"),
   187  			},
   188  			Extension:          []byte("extension1"),
   189  			ExtensionSignature: []byte("extensionSig1"),
   190  		}
   191  
   192  		commitSig1 = types.ExtendedCommitSig{
   193  			CommitSig: types.CommitSig{
   194  				BlockIDFlag:      types.BlockIDFlagCommit,
   195  				ValidatorAddress: state.Validators.Validators[1].Address,
   196  				Timestamp:        now,
   197  				Signature:        []byte("Signature2"),
   198  			},
   199  			Extension:          []byte("extension2"),
   200  			ExtensionSignature: []byte("extensionSig2"),
   201  		}
   202  		absentSig = types.NewExtendedCommitSigAbsent()
   203  	)
   204  
   205  	testCases := []struct {
   206  		desc                     string
   207  		lastCommitSigs           []types.ExtendedCommitSig
   208  		expectedAbsentValidators []int
   209  		shouldHaveTime           bool
   210  	}{
   211  		{"none absent", []types.ExtendedCommitSig{commitSig0, commitSig1}, []int{}, true},
   212  		{"one absent", []types.ExtendedCommitSig{commitSig0, absentSig}, []int{1}, true},
   213  		{"multiple absent", []types.ExtendedCommitSig{absentSig, absentSig}, []int{0, 1}, false},
   214  	}
   215  
   216  	for _, tc := range testCases {
   217  		lastCommit := &types.ExtendedCommit{
   218  			Height:             1,
   219  			BlockID:            prevBlockID,
   220  			ExtendedSignatures: tc.lastCommitSigs,
   221  		}
   222  
   223  		// block for height 2
   224  		block := makeBlock(state, 2, lastCommit.ToCommit())
   225  
   226  		_, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateStore, 1)
   227  		require.NoError(t, err, tc.desc)
   228  		require.True(t,
   229  			!tc.shouldHaveTime ||
   230  				app.LastTime.Equal(now) || app.LastTime.After(now),
   231  			"'last_time' should be at or after 'now'; tc %v, last_time %v, now %v", tc.desc, app.LastTime, now,
   232  		)
   233  
   234  		// -> app receives a list of validators with a bool indicating if they signed
   235  		ctr := 0
   236  		for i, v := range app.CommitVotes {
   237  			if ctr < len(tc.expectedAbsentValidators) &&
   238  				tc.expectedAbsentValidators[ctr] == i {
   239  
   240  				assert.Equal(t, v.BlockIdFlag, cmtproto.BlockIDFlagAbsent)
   241  				ctr++
   242  			} else {
   243  				assert.NotEqual(t, v.BlockIdFlag, cmtproto.BlockIDFlagAbsent)
   244  			}
   245  		}
   246  	}
   247  }
   248  
   249  // TestFinalizeBlockMisbehavior ensures we send misbehavior list.
   250  func TestFinalizeBlockMisbehavior(t *testing.T) {
   251  	app := &testApp{}
   252  	cc := proxy.NewLocalClientCreator(app)
   253  	proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
   254  	err := proxyApp.Start()
   255  	require.NoError(t, err)
   256  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
   257  
   258  	state, stateDB, privVals := makeState(1, 1)
   259  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   260  		DiscardABCIResponses: false,
   261  	})
   262  
   263  	defaultEvidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
   264  	privVal := privVals[state.Validators.Validators[0].Address.String()]
   265  	blockID := makeBlockID([]byte("headerhash"), 1000, []byte("partshash"))
   266  	header := &types.Header{
   267  		Version:            cmtversion.Consensus{Block: version.BlockProtocol, App: 1},
   268  		ChainID:            state.ChainID,
   269  		Height:             10,
   270  		Time:               defaultEvidenceTime,
   271  		LastBlockID:        blockID,
   272  		LastCommitHash:     crypto.CRandBytes(tmhash.Size),
   273  		DataHash:           crypto.CRandBytes(tmhash.Size),
   274  		ValidatorsHash:     state.Validators.Hash(),
   275  		NextValidatorsHash: state.Validators.Hash(),
   276  		ConsensusHash:      crypto.CRandBytes(tmhash.Size),
   277  		AppHash:            crypto.CRandBytes(tmhash.Size),
   278  		LastResultsHash:    crypto.CRandBytes(tmhash.Size),
   279  		EvidenceHash:       crypto.CRandBytes(tmhash.Size),
   280  		ProposerAddress:    crypto.CRandBytes(crypto.AddressSize),
   281  	}
   282  
   283  	// we don't need to worry about validating the evidence as long as they pass validate basic
   284  	dve, err := types.NewMockDuplicateVoteEvidenceWithValidator(3, defaultEvidenceTime, privVal, state.ChainID)
   285  	require.NoError(t, err)
   286  	dve.ValidatorPower = 1000
   287  	lcae := &types.LightClientAttackEvidence{
   288  		ConflictingBlock: &types.LightBlock{
   289  			SignedHeader: &types.SignedHeader{
   290  				Header: header,
   291  				Commit: &types.Commit{
   292  					Height:  10,
   293  					BlockID: makeBlockID(header.Hash(), 100, []byte("partshash")),
   294  					Signatures: []types.CommitSig{{
   295  						BlockIDFlag:      types.BlockIDFlagNil,
   296  						ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
   297  						Timestamp:        defaultEvidenceTime,
   298  						Signature:        crypto.CRandBytes(types.MaxSignatureSize)}},
   299  				},
   300  			},
   301  			ValidatorSet: state.Validators,
   302  		},
   303  		CommonHeight:        8,
   304  		ByzantineValidators: []*types.Validator{state.Validators.Validators[0]},
   305  		TotalVotingPower:    12,
   306  		Timestamp:           defaultEvidenceTime,
   307  	}
   308  
   309  	ev := []types.Evidence{dve, lcae}
   310  
   311  	abciMb := []abci.Misbehavior{
   312  		{
   313  			Type:             abci.MisbehaviorType_DUPLICATE_VOTE,
   314  			Height:           3,
   315  			Time:             defaultEvidenceTime,
   316  			Validator:        types.TM2PB.Validator(state.Validators.Validators[0]),
   317  			TotalVotingPower: 10,
   318  		},
   319  		{
   320  			Type:             abci.MisbehaviorType_LIGHT_CLIENT_ATTACK,
   321  			Height:           8,
   322  			Time:             defaultEvidenceTime,
   323  			Validator:        types.TM2PB.Validator(state.Validators.Validators[0]),
   324  			TotalVotingPower: 12,
   325  		},
   326  	}
   327  
   328  	evpool := &mocks.EvidencePool{}
   329  	evpool.On("PendingEvidence", mock.AnythingOfType("int64")).Return(ev, int64(100))
   330  	evpool.On("Update", mock.AnythingOfType("state.State"), mock.AnythingOfType("types.EvidenceList")).Return()
   331  	evpool.On("CheckEvidence", mock.AnythingOfType("types.EvidenceList")).Return(nil)
   332  	mp := &mpmocks.Mempool{}
   333  	mp.On("Lock").Return()
   334  	mp.On("Unlock").Return()
   335  	mp.On("FlushAppConn", mock.Anything).Return(nil)
   336  	mp.On("Update",
   337  		mock.Anything,
   338  		mock.Anything,
   339  		mock.Anything,
   340  		mock.Anything,
   341  		mock.Anything,
   342  		mock.Anything).Return(nil)
   343  
   344  	blockStore := store.NewBlockStore(dbm.NewMemDB())
   345  
   346  	blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
   347  		mp, evpool, blockStore)
   348  
   349  	block := makeBlock(state, 1, new(types.Commit))
   350  	block.Evidence = types.EvidenceData{Evidence: ev}
   351  	block.Header.EvidenceHash = block.Evidence.Hash()
   352  	bps, err := block.MakePartSet(testPartSize)
   353  	require.NoError(t, err)
   354  
   355  	blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
   356  
   357  	_, err = blockExec.ApplyBlock(state, blockID, block)
   358  	require.NoError(t, err)
   359  
   360  	// TODO check state and mempool
   361  	assert.Equal(t, abciMb, app.Misbehavior)
   362  }
   363  
   364  func TestProcessProposal(t *testing.T) {
   365  	const height = 2
   366  	txs := test.MakeNTxs(height, 10)
   367  
   368  	logger := log.NewNopLogger()
   369  	app := &abcimocks.Application{}
   370  	app.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil)
   371  
   372  	cc := proxy.NewLocalClientCreator(app)
   373  	proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
   374  	err := proxyApp.Start()
   375  	require.NoError(t, err)
   376  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
   377  
   378  	state, stateDB, privVals := makeState(1, height)
   379  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   380  		DiscardABCIResponses: false,
   381  	})
   382  	blockStore := store.NewBlockStore(dbm.NewMemDB())
   383  	eventBus := types.NewEventBus()
   384  	err = eventBus.Start()
   385  	require.NoError(t, err)
   386  
   387  	blockExec := sm.NewBlockExecutor(
   388  		stateStore,
   389  		logger,
   390  		proxyApp.Consensus(),
   391  		new(mpmocks.Mempool),
   392  		sm.EmptyEvidencePool{},
   393  		blockStore,
   394  	)
   395  
   396  	block0 := makeBlock(state, height-1, new(types.Commit))
   397  	lastCommitSig := []types.CommitSig{}
   398  	partSet, err := block0.MakePartSet(types.BlockPartSizeBytes)
   399  	require.NoError(t, err)
   400  	blockID := types.BlockID{Hash: block0.Hash(), PartSetHeader: partSet.Header()}
   401  	voteInfos := []abci.VoteInfo{}
   402  	for _, privVal := range privVals {
   403  		pk, err := privVal.GetPubKey()
   404  		require.NoError(t, err)
   405  		idx, _ := state.Validators.GetByAddress(pk.Address())
   406  		vote := types.MakeVoteNoError(t, privVal, block0.Header.ChainID, idx, height-1, 0, 2, blockID, time.Now())
   407  		addr := pk.Address()
   408  		voteInfos = append(voteInfos,
   409  			abci.VoteInfo{
   410  				BlockIdFlag: cmtproto.BlockIDFlagCommit,
   411  				Validator: abci.Validator{
   412  					Address: addr,
   413  					Power:   1000,
   414  				},
   415  			})
   416  		lastCommitSig = append(lastCommitSig, vote.CommitSig())
   417  	}
   418  
   419  	block1 := makeBlock(state, height, &types.Commit{
   420  		Height:     height - 1,
   421  		Signatures: lastCommitSig,
   422  	})
   423  
   424  	block1.Txs = txs
   425  
   426  	expectedRpp := &abci.RequestProcessProposal{
   427  		Txs:         block1.Txs.ToSliceOfBytes(),
   428  		Hash:        block1.Hash(),
   429  		Height:      block1.Header.Height,
   430  		Time:        block1.Header.Time,
   431  		Misbehavior: block1.Evidence.Evidence.ToABCI(),
   432  		ProposedLastCommit: abci.CommitInfo{
   433  			Round: 0,
   434  			Votes: voteInfos,
   435  		},
   436  		NextValidatorsHash: block1.NextValidatorsHash,
   437  		ProposerAddress:    block1.ProposerAddress,
   438  	}
   439  
   440  	acceptBlock, err := blockExec.ProcessProposal(block1, state)
   441  	require.NoError(t, err)
   442  	require.True(t, acceptBlock)
   443  	app.AssertExpectations(t)
   444  	app.AssertCalled(t, "ProcessProposal", context.TODO(), expectedRpp)
   445  }
   446  
   447  func TestValidateValidatorUpdates(t *testing.T) {
   448  	pubkey1 := ed25519.GenPrivKey().PubKey()
   449  	pubkey2 := ed25519.GenPrivKey().PubKey()
   450  	pk1, err := cryptoenc.PubKeyToProto(pubkey1)
   451  	assert.NoError(t, err)
   452  	pk2, err := cryptoenc.PubKeyToProto(pubkey2)
   453  	assert.NoError(t, err)
   454  
   455  	defaultValidatorParams := types.ValidatorParams{PubKeyTypes: []string{types.ABCIPubKeyTypeEd25519}}
   456  
   457  	testCases := []struct {
   458  		name string
   459  
   460  		abciUpdates     []abci.ValidatorUpdate
   461  		validatorParams types.ValidatorParams
   462  
   463  		shouldErr bool
   464  	}{
   465  		{
   466  			"adding a validator is OK",
   467  			[]abci.ValidatorUpdate{{PubKey: pk2, Power: 20}},
   468  			defaultValidatorParams,
   469  			false,
   470  		},
   471  		{
   472  			"updating a validator is OK",
   473  			[]abci.ValidatorUpdate{{PubKey: pk1, Power: 20}},
   474  			defaultValidatorParams,
   475  			false,
   476  		},
   477  		{
   478  			"removing a validator is OK",
   479  			[]abci.ValidatorUpdate{{PubKey: pk2, Power: 0}},
   480  			defaultValidatorParams,
   481  			false,
   482  		},
   483  		{
   484  			"adding a validator with negative power results in error",
   485  			[]abci.ValidatorUpdate{{PubKey: pk2, Power: -100}},
   486  			defaultValidatorParams,
   487  			true,
   488  		},
   489  	}
   490  
   491  	for _, tc := range testCases {
   492  		tc := tc
   493  		t.Run(tc.name, func(t *testing.T) {
   494  			err := sm.ValidateValidatorUpdates(tc.abciUpdates, tc.validatorParams)
   495  			if tc.shouldErr {
   496  				assert.Error(t, err)
   497  			} else {
   498  				assert.NoError(t, err)
   499  			}
   500  		})
   501  	}
   502  }
   503  
   504  func TestUpdateValidators(t *testing.T) {
   505  	pubkey1 := ed25519.GenPrivKey().PubKey()
   506  	val1 := types.NewValidator(pubkey1, 10)
   507  	pubkey2 := ed25519.GenPrivKey().PubKey()
   508  	val2 := types.NewValidator(pubkey2, 20)
   509  
   510  	pk, err := cryptoenc.PubKeyToProto(pubkey1)
   511  	require.NoError(t, err)
   512  	pk2, err := cryptoenc.PubKeyToProto(pubkey2)
   513  	require.NoError(t, err)
   514  
   515  	testCases := []struct {
   516  		name string
   517  
   518  		currentSet  *types.ValidatorSet
   519  		abciUpdates []abci.ValidatorUpdate
   520  
   521  		resultingSet *types.ValidatorSet
   522  		shouldErr    bool
   523  	}{
   524  		{
   525  			"adding a validator is OK",
   526  			types.NewValidatorSet([]*types.Validator{val1}),
   527  			[]abci.ValidatorUpdate{{PubKey: pk2, Power: 20}},
   528  			types.NewValidatorSet([]*types.Validator{val1, val2}),
   529  			false,
   530  		},
   531  		{
   532  			"updating a validator is OK",
   533  			types.NewValidatorSet([]*types.Validator{val1}),
   534  			[]abci.ValidatorUpdate{{PubKey: pk, Power: 20}},
   535  			types.NewValidatorSet([]*types.Validator{types.NewValidator(pubkey1, 20)}),
   536  			false,
   537  		},
   538  		{
   539  			"removing a validator is OK",
   540  			types.NewValidatorSet([]*types.Validator{val1, val2}),
   541  			[]abci.ValidatorUpdate{{PubKey: pk2, Power: 0}},
   542  			types.NewValidatorSet([]*types.Validator{val1}),
   543  			false,
   544  		},
   545  		{
   546  			"removing a non-existing validator results in error",
   547  			types.NewValidatorSet([]*types.Validator{val1}),
   548  			[]abci.ValidatorUpdate{{PubKey: pk2, Power: 0}},
   549  			types.NewValidatorSet([]*types.Validator{val1}),
   550  			true,
   551  		},
   552  	}
   553  
   554  	for _, tc := range testCases {
   555  		tc := tc
   556  		t.Run(tc.name, func(t *testing.T) {
   557  			updates, err := types.PB2TM.ValidatorUpdates(tc.abciUpdates)
   558  			assert.NoError(t, err)
   559  			err = tc.currentSet.UpdateWithChangeSet(updates)
   560  			if tc.shouldErr {
   561  				assert.Error(t, err)
   562  			} else {
   563  				assert.NoError(t, err)
   564  				require.Equal(t, tc.resultingSet.Size(), tc.currentSet.Size())
   565  
   566  				assert.Equal(t, tc.resultingSet.TotalVotingPower(), tc.currentSet.TotalVotingPower())
   567  
   568  				assert.Equal(t, tc.resultingSet.Validators[0].Address, tc.currentSet.Validators[0].Address)
   569  				if tc.resultingSet.Size() > 1 {
   570  					assert.Equal(t, tc.resultingSet.Validators[1].Address, tc.currentSet.Validators[1].Address)
   571  				}
   572  			}
   573  		})
   574  	}
   575  }
   576  
   577  // TestFinalizeBlockValidatorUpdates ensures we update validator set and send an event.
   578  func TestFinalizeBlockValidatorUpdates(t *testing.T) {
   579  	app := &testApp{}
   580  	cc := proxy.NewLocalClientCreator(app)
   581  	proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
   582  	err := proxyApp.Start()
   583  	require.NoError(t, err)
   584  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
   585  
   586  	state, stateDB, _ := makeState(1, 1)
   587  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   588  		DiscardABCIResponses: false,
   589  	})
   590  	mp := &mpmocks.Mempool{}
   591  	mp.On("Lock").Return()
   592  	mp.On("Unlock").Return()
   593  	mp.On("FlushAppConn", mock.Anything).Return(nil)
   594  	mp.On("Update",
   595  		mock.Anything,
   596  		mock.Anything,
   597  		mock.Anything,
   598  		mock.Anything,
   599  		mock.Anything,
   600  		mock.Anything).Return(nil)
   601  	mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs{})
   602  
   603  	blockStore := store.NewBlockStore(dbm.NewMemDB())
   604  	blockExec := sm.NewBlockExecutor(
   605  		stateStore,
   606  		log.TestingLogger(),
   607  		proxyApp.Consensus(),
   608  		mp,
   609  		sm.EmptyEvidencePool{},
   610  		blockStore,
   611  	)
   612  
   613  	eventBus := types.NewEventBus()
   614  	err = eventBus.Start()
   615  	require.NoError(t, err)
   616  	defer eventBus.Stop() //nolint:errcheck // ignore for tests
   617  
   618  	blockExec.SetEventBus(eventBus)
   619  
   620  	updatesSub, err := eventBus.Subscribe(
   621  		context.Background(),
   622  		"TestEndBlockValidatorUpdates",
   623  		types.EventQueryValidatorSetUpdates,
   624  	)
   625  	require.NoError(t, err)
   626  
   627  	block := makeBlock(state, 1, new(types.Commit))
   628  	bps, err := block.MakePartSet(testPartSize)
   629  	require.NoError(t, err)
   630  	blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
   631  
   632  	pubkey := ed25519.GenPrivKey().PubKey()
   633  	pk, err := cryptoenc.PubKeyToProto(pubkey)
   634  	require.NoError(t, err)
   635  	app.ValidatorUpdates = []abci.ValidatorUpdate{
   636  		{PubKey: pk, Power: 10},
   637  	}
   638  
   639  	state, err = blockExec.ApplyBlock(state, blockID, block)
   640  	require.NoError(t, err)
   641  	// test new validator was added to NextValidators
   642  	if assert.Equal(t, state.Validators.Size()+1, state.NextValidators.Size()) {
   643  		idx, _ := state.NextValidators.GetByAddress(pubkey.Address())
   644  		if idx < 0 {
   645  			t.Fatalf("can't find address %v in the set %v", pubkey.Address(), state.NextValidators)
   646  		}
   647  	}
   648  
   649  	// test we threw an event
   650  	select {
   651  	case msg := <-updatesSub.Out():
   652  		event, ok := msg.Data().(types.EventDataValidatorSetUpdates)
   653  		require.True(t, ok, "Expected event of type EventDataValidatorSetUpdates, got %T", msg.Data())
   654  		if assert.NotEmpty(t, event.ValidatorUpdates) {
   655  			assert.Equal(t, pubkey, event.ValidatorUpdates[0].PubKey)
   656  			assert.EqualValues(t, 10, event.ValidatorUpdates[0].VotingPower)
   657  		}
   658  	case <-updatesSub.Canceled():
   659  		t.Fatalf("updatesSub was canceled (reason: %v)", updatesSub.Err())
   660  	case <-time.After(1 * time.Second):
   661  		t.Fatal("Did not receive EventValidatorSetUpdates within 1 sec.")
   662  	}
   663  }
   664  
   665  // TestFinalizeBlockValidatorUpdatesResultingInEmptySet checks that processing validator updates that
   666  // would result in empty set causes no panic, an error is raised and NextValidators is not updated
   667  func TestFinalizeBlockValidatorUpdatesResultingInEmptySet(t *testing.T) {
   668  	app := &testApp{}
   669  	cc := proxy.NewLocalClientCreator(app)
   670  	proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
   671  	err := proxyApp.Start()
   672  	require.NoError(t, err)
   673  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
   674  
   675  	state, stateDB, _ := makeState(1, 1)
   676  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   677  		DiscardABCIResponses: false,
   678  	})
   679  	blockStore := store.NewBlockStore(dbm.NewMemDB())
   680  	blockExec := sm.NewBlockExecutor(
   681  		stateStore,
   682  		log.TestingLogger(),
   683  		proxyApp.Consensus(),
   684  		new(mpmocks.Mempool),
   685  		sm.EmptyEvidencePool{},
   686  		blockStore,
   687  	)
   688  
   689  	block := makeBlock(state, 1, new(types.Commit))
   690  	bps, err := block.MakePartSet(testPartSize)
   691  	require.NoError(t, err)
   692  	blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
   693  
   694  	vp, err := cryptoenc.PubKeyToProto(state.Validators.Validators[0].PubKey)
   695  	require.NoError(t, err)
   696  	// Remove the only validator
   697  	app.ValidatorUpdates = []abci.ValidatorUpdate{
   698  		{PubKey: vp, Power: 0},
   699  	}
   700  
   701  	assert.NotPanics(t, func() { state, err = blockExec.ApplyBlock(state, blockID, block) })
   702  	assert.Error(t, err)
   703  	assert.NotEmpty(t, state.NextValidators.Validators)
   704  }
   705  
   706  func TestEmptyPrepareProposal(t *testing.T) {
   707  	const height = 2
   708  	ctx, cancel := context.WithCancel(context.Background())
   709  	defer cancel()
   710  
   711  	app := &abci.BaseApplication{}
   712  	cc := proxy.NewLocalClientCreator(app)
   713  	proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
   714  	err := proxyApp.Start()
   715  	require.NoError(t, err)
   716  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
   717  
   718  	state, stateDB, privVals := makeState(1, height)
   719  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   720  		DiscardABCIResponses: false,
   721  	})
   722  	mp := &mpmocks.Mempool{}
   723  	mp.On("Lock").Return()
   724  	mp.On("Unlock").Return()
   725  	mp.On("FlushAppConn", mock.Anything).Return(nil)
   726  	mp.On("Update",
   727  		mock.Anything,
   728  		mock.Anything,
   729  		mock.Anything,
   730  		mock.Anything,
   731  		mock.Anything,
   732  		mock.Anything).Return(nil)
   733  	mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs{})
   734  
   735  	blockStore := store.NewBlockStore(dbm.NewMemDB())
   736  	blockExec := sm.NewBlockExecutor(
   737  		stateStore,
   738  		log.TestingLogger(),
   739  		proxyApp.Consensus(),
   740  		mp,
   741  		sm.EmptyEvidencePool{},
   742  		blockStore,
   743  	)
   744  	pa, _ := state.Validators.GetByIndex(0)
   745  	commit, _, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
   746  	require.NoError(t, err)
   747  	_, err = blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
   748  	require.NoError(t, err)
   749  }
   750  
   751  // TestPrepareProposalTxsAllIncluded tests that any transactions included in
   752  // the prepare proposal response are included in the block.
   753  func TestPrepareProposalTxsAllIncluded(t *testing.T) {
   754  	const height = 2
   755  	ctx, cancel := context.WithCancel(context.Background())
   756  	defer cancel()
   757  
   758  	state, stateDB, privVals := makeState(1, height)
   759  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   760  		DiscardABCIResponses: false,
   761  	})
   762  
   763  	evpool := &mocks.EvidencePool{}
   764  	evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, int64(0))
   765  
   766  	txs := test.MakeNTxs(height, 10)
   767  	mp := &mpmocks.Mempool{}
   768  	mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(txs[2:])
   769  
   770  	app := &abcimocks.Application{}
   771  	app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{
   772  		Txs: txs.ToSliceOfBytes(),
   773  	}, nil)
   774  	cc := proxy.NewLocalClientCreator(app)
   775  	proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
   776  	err := proxyApp.Start()
   777  	require.NoError(t, err)
   778  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
   779  
   780  	blockStore := store.NewBlockStore(dbm.NewMemDB())
   781  	blockExec := sm.NewBlockExecutor(
   782  		stateStore,
   783  		log.TestingLogger(),
   784  		proxyApp.Consensus(),
   785  		mp,
   786  		evpool,
   787  		blockStore,
   788  	)
   789  	pa, _ := state.Validators.GetByIndex(0)
   790  	commit, _, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
   791  	require.NoError(t, err)
   792  	block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
   793  	require.NoError(t, err)
   794  
   795  	for i, tx := range block.Data.Txs {
   796  		require.Equal(t, txs[i], tx)
   797  	}
   798  
   799  	mp.AssertExpectations(t)
   800  }
   801  
   802  // TestPrepareProposalReorderTxs tests that CreateBlock produces a block with transactions
   803  // in the order matching the order they are returned from PrepareProposal.
   804  func TestPrepareProposalReorderTxs(t *testing.T) {
   805  	const height = 2
   806  	ctx, cancel := context.WithCancel(context.Background())
   807  	defer cancel()
   808  
   809  	state, stateDB, privVals := makeState(1, height)
   810  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   811  		DiscardABCIResponses: false,
   812  	})
   813  
   814  	evpool := &mocks.EvidencePool{}
   815  	evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, int64(0))
   816  
   817  	txs := test.MakeNTxs(height, 10)
   818  	mp := &mpmocks.Mempool{}
   819  	mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(txs)
   820  
   821  	txs = txs[2:]
   822  	txs = append(txs[len(txs)/2:], txs[:len(txs)/2]...)
   823  
   824  	app := &abcimocks.Application{}
   825  	app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{
   826  		Txs: txs.ToSliceOfBytes(),
   827  	}, nil)
   828  
   829  	cc := proxy.NewLocalClientCreator(app)
   830  	proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
   831  	err := proxyApp.Start()
   832  	require.NoError(t, err)
   833  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
   834  
   835  	blockStore := store.NewBlockStore(dbm.NewMemDB())
   836  	blockExec := sm.NewBlockExecutor(
   837  		stateStore,
   838  		log.TestingLogger(),
   839  		proxyApp.Consensus(),
   840  		mp,
   841  		evpool,
   842  		blockStore,
   843  	)
   844  	pa, _ := state.Validators.GetByIndex(0)
   845  	commit, _, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
   846  	require.NoError(t, err)
   847  	block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
   848  	require.NoError(t, err)
   849  	for i, tx := range block.Data.Txs {
   850  		require.Equal(t, txs[i], tx)
   851  	}
   852  
   853  	mp.AssertExpectations(t)
   854  }
   855  
   856  // TestPrepareProposalErrorOnTooManyTxs tests that the block creation logic returns
   857  // an error if the ResponsePrepareProposal returned from the application is invalid.
   858  func TestPrepareProposalErrorOnTooManyTxs(t *testing.T) {
   859  	const height = 2
   860  	ctx, cancel := context.WithCancel(context.Background())
   861  	defer cancel()
   862  
   863  	state, stateDB, privVals := makeState(1, height)
   864  	// limit max block size
   865  	state.ConsensusParams.Block.MaxBytes = 60 * 1024
   866  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   867  		DiscardABCIResponses: false,
   868  	})
   869  
   870  	evpool := &mocks.EvidencePool{}
   871  	evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, int64(0))
   872  
   873  	const nValidators = 1
   874  	var bytesPerTx int64 = 3
   875  	maxDataBytes := types.MaxDataBytes(state.ConsensusParams.Block.MaxBytes, 0, nValidators)
   876  	txs := test.MakeNTxs(height, maxDataBytes/bytesPerTx+2) // +2 so that tx don't fit
   877  	mp := &mpmocks.Mempool{}
   878  	mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(txs)
   879  
   880  	app := &abcimocks.Application{}
   881  	app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{
   882  		Txs: txs.ToSliceOfBytes(),
   883  	}, nil)
   884  
   885  	cc := proxy.NewLocalClientCreator(app)
   886  	proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
   887  	err := proxyApp.Start()
   888  	require.NoError(t, err)
   889  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
   890  
   891  	blockStore := store.NewBlockStore(dbm.NewMemDB())
   892  	blockExec := sm.NewBlockExecutor(
   893  		stateStore,
   894  		log.NewNopLogger(),
   895  		proxyApp.Consensus(),
   896  		mp,
   897  		evpool,
   898  		blockStore,
   899  	)
   900  	pa, _ := state.Validators.GetByIndex(0)
   901  	commit, _, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
   902  	require.NoError(t, err)
   903  	block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
   904  	require.Nil(t, block)
   905  	require.ErrorContains(t, err, "transaction data size exceeds maximum")
   906  
   907  	mp.AssertExpectations(t)
   908  }
   909  
   910  // TestPrepareProposalErrorOnPrepareProposalError tests when the client returns an error
   911  // upon calling PrepareProposal on it.
   912  func TestPrepareProposalErrorOnPrepareProposalError(t *testing.T) {
   913  	const height = 2
   914  	ctx, cancel := context.WithCancel(context.Background())
   915  	defer cancel()
   916  
   917  	state, stateDB, privVals := makeState(1, height)
   918  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   919  		DiscardABCIResponses: false,
   920  	})
   921  
   922  	evpool := &mocks.EvidencePool{}
   923  	evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, int64(0))
   924  
   925  	txs := test.MakeNTxs(height, 10)
   926  	mp := &mpmocks.Mempool{}
   927  	mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(txs)
   928  
   929  	cm := &abciclientmocks.Client{}
   930  	cm.On("SetLogger", mock.Anything).Return()
   931  	cm.On("Start").Return(nil)
   932  	cm.On("Quit").Return(nil)
   933  	cm.On("PrepareProposal", mock.Anything, mock.Anything).Return(nil, errors.New("an injected error")).Once()
   934  	cm.On("Stop").Return(nil)
   935  	cc := &pmocks.ClientCreator{}
   936  	cc.On("NewABCIClient").Return(cm, nil)
   937  	proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
   938  	err := proxyApp.Start()
   939  	require.NoError(t, err)
   940  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
   941  
   942  	blockStore := store.NewBlockStore(dbm.NewMemDB())
   943  	blockExec := sm.NewBlockExecutor(
   944  		stateStore,
   945  		log.NewNopLogger(),
   946  		proxyApp.Consensus(),
   947  		mp,
   948  		evpool,
   949  		blockStore,
   950  	)
   951  	pa, _ := state.Validators.GetByIndex(0)
   952  	commit, _, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
   953  	require.NoError(t, err)
   954  	block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
   955  	require.Nil(t, block)
   956  	require.ErrorContains(t, err, "an injected error")
   957  
   958  	mp.AssertExpectations(t)
   959  }
   960  
   961  // TestCreateProposalBlockPanicOnAbsentVoteExtensions ensures that the CreateProposalBlock
   962  // call correctly panics when the vote extension data is missing from the extended commit
   963  // data that the method receives.
   964  func TestCreateProposalAbsentVoteExtensions(t *testing.T) {
   965  	for _, testCase := range []struct {
   966  		name string
   967  
   968  		// The height that is about to be proposed
   969  		height int64
   970  
   971  		// The first height during which vote extensions will be required for consensus to proceed.
   972  		extensionEnableHeight int64
   973  		expectPanic           bool
   974  	}{
   975  		{
   976  			name:                  "missing extension data on first required height",
   977  			height:                2,
   978  			extensionEnableHeight: 1,
   979  			expectPanic:           true,
   980  		},
   981  		{
   982  			name:                  "missing extension during before required height",
   983  			height:                2,
   984  			extensionEnableHeight: 2,
   985  			expectPanic:           false,
   986  		},
   987  		{
   988  			name:                  "missing extension data and not required",
   989  			height:                2,
   990  			extensionEnableHeight: 0,
   991  			expectPanic:           false,
   992  		},
   993  		{
   994  			name:                  "missing extension data and required in two heights",
   995  			height:                2,
   996  			extensionEnableHeight: 3,
   997  			expectPanic:           false,
   998  		},
   999  	} {
  1000  		t.Run(testCase.name, func(t *testing.T) {
  1001  			ctx, cancel := context.WithCancel(context.Background())
  1002  			defer cancel()
  1003  
  1004  			app := abcimocks.NewApplication(t)
  1005  			if !testCase.expectPanic {
  1006  				app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil)
  1007  			}
  1008  			cc := proxy.NewLocalClientCreator(app)
  1009  			proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
  1010  			err := proxyApp.Start()
  1011  			require.NoError(t, err)
  1012  
  1013  			state, stateDB, privVals := makeState(1, int(testCase.height-1))
  1014  			stateStore := sm.NewStore(stateDB, sm.StoreOptions{
  1015  				DiscardABCIResponses: false,
  1016  			})
  1017  			state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = testCase.extensionEnableHeight
  1018  			mp := &mpmocks.Mempool{}
  1019  			mp.On("Lock").Return()
  1020  			mp.On("Unlock").Return()
  1021  			mp.On("FlushAppConn", mock.Anything).Return(nil)
  1022  			mp.On("Update",
  1023  				mock.Anything,
  1024  				mock.Anything,
  1025  				mock.Anything,
  1026  				mock.Anything,
  1027  				mock.Anything,
  1028  				mock.Anything).Return(nil)
  1029  			mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs{})
  1030  
  1031  			blockStore := store.NewBlockStore(dbm.NewMemDB())
  1032  			blockExec := sm.NewBlockExecutor(
  1033  				stateStore,
  1034  				log.NewNopLogger(),
  1035  				proxyApp.Consensus(),
  1036  				mp,
  1037  				sm.EmptyEvidencePool{},
  1038  				blockStore,
  1039  			)
  1040  			block := makeBlock(state, testCase.height, new(types.Commit))
  1041  
  1042  			bps, err := block.MakePartSet(testPartSize)
  1043  			require.NoError(t, err)
  1044  			blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
  1045  			pa, _ := state.Validators.GetByIndex(0)
  1046  			lastCommit, _, _ := makeValidCommit(testCase.height-1, blockID, state.Validators, privVals)
  1047  			stripSignatures(lastCommit)
  1048  			if testCase.expectPanic {
  1049  				require.Panics(t, func() {
  1050  					blockExec.CreateProposalBlock(ctx, testCase.height, state, lastCommit, pa) //nolint:errcheck
  1051  				})
  1052  			} else {
  1053  				_, err = blockExec.CreateProposalBlock(ctx, testCase.height, state, lastCommit, pa)
  1054  				require.NoError(t, err)
  1055  			}
  1056  		})
  1057  	}
  1058  }
  1059  
  1060  func stripSignatures(ec *types.ExtendedCommit) {
  1061  	for i, commitSig := range ec.ExtendedSignatures {
  1062  		commitSig.Extension = nil
  1063  		commitSig.ExtensionSignature = nil
  1064  		ec.ExtendedSignatures[i] = commitSig
  1065  	}
  1066  }
  1067  
  1068  func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.BlockID {
  1069  	var (
  1070  		h   = make([]byte, tmhash.Size)
  1071  		psH = make([]byte, tmhash.Size)
  1072  	)
  1073  	copy(h, hash)
  1074  	copy(psH, partSetHash)
  1075  	return types.BlockID{
  1076  		Hash: h,
  1077  		PartSetHeader: types.PartSetHeader{
  1078  			Total: partSetSize,
  1079  			Hash:  psH,
  1080  		},
  1081  	}
  1082  }