github.com/vipernet-xyz/tm@v0.34.24/state/execution_test.go (about)

     1  package state_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/mock"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	abci "github.com/vipernet-xyz/tm/abci/types"
    13  	"github.com/vipernet-xyz/tm/crypto"
    14  	"github.com/vipernet-xyz/tm/crypto/ed25519"
    15  	cryptoenc "github.com/vipernet-xyz/tm/crypto/encoding"
    16  	"github.com/vipernet-xyz/tm/crypto/tmhash"
    17  	"github.com/vipernet-xyz/tm/libs/log"
    18  	mmock "github.com/vipernet-xyz/tm/mempool/mock"
    19  	tmproto "github.com/vipernet-xyz/tm/proto/tendermint/types"
    20  	tmversion "github.com/vipernet-xyz/tm/proto/tendermint/version"
    21  	"github.com/vipernet-xyz/tm/proxy"
    22  	sm "github.com/vipernet-xyz/tm/state"
    23  	"github.com/vipernet-xyz/tm/state/mocks"
    24  	"github.com/vipernet-xyz/tm/types"
    25  	tmtime "github.com/vipernet-xyz/tm/types/time"
    26  	"github.com/vipernet-xyz/tm/version"
    27  )
    28  
    29  var (
    30  	chainID             = "execution_chain"
    31  	testPartSize uint32 = 65536
    32  	nTxsPerBlock        = 10
    33  )
    34  
    35  func TestApplyBlock(t *testing.T) {
    36  	app := &testApp{}
    37  	cc := proxy.NewLocalClientCreator(app)
    38  	proxyApp := proxy.NewAppConns(cc)
    39  	err := proxyApp.Start()
    40  	require.Nil(t, err)
    41  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
    42  
    43  	state, stateDB, _ := makeState(1, 1)
    44  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
    45  		DiscardABCIResponses: false,
    46  	})
    47  
    48  	blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
    49  		mmock.Mempool{}, sm.EmptyEvidencePool{})
    50  
    51  	block := makeBlock(state, 1)
    52  	blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
    53  
    54  	state, retainHeight, err := blockExec.ApplyBlock(state, blockID, block)
    55  	require.Nil(t, err)
    56  	assert.EqualValues(t, retainHeight, 1)
    57  
    58  	// TODO check state and mempool
    59  	assert.EqualValues(t, 1, state.Version.Consensus.App, "App version wasn't updated")
    60  }
    61  
    62  // TestBeginBlockValidators ensures we send absent validators list.
    63  func TestBeginBlockValidators(t *testing.T) {
    64  	app := &testApp{}
    65  	cc := proxy.NewLocalClientCreator(app)
    66  	proxyApp := proxy.NewAppConns(cc)
    67  	err := proxyApp.Start()
    68  	require.Nil(t, err)
    69  	defer proxyApp.Stop() //nolint:errcheck // no need to check error again
    70  
    71  	state, stateDB, _ := makeState(2, 2)
    72  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
    73  		DiscardABCIResponses: false,
    74  	})
    75  
    76  	prevHash := state.LastBlockID.Hash
    77  	prevParts := types.PartSetHeader{}
    78  	prevBlockID := types.BlockID{Hash: prevHash, PartSetHeader: prevParts}
    79  
    80  	var (
    81  		now        = tmtime.Now()
    82  		commitSig0 = types.NewCommitSigForBlock(
    83  			[]byte("Signature1"),
    84  			state.Validators.Validators[0].Address,
    85  			now)
    86  		commitSig1 = types.NewCommitSigForBlock(
    87  			[]byte("Signature2"),
    88  			state.Validators.Validators[1].Address,
    89  			now)
    90  		absentSig = types.NewCommitSigAbsent()
    91  	)
    92  
    93  	testCases := []struct {
    94  		desc                     string
    95  		lastCommitSigs           []types.CommitSig
    96  		expectedAbsentValidators []int
    97  	}{
    98  		{"none absent", []types.CommitSig{commitSig0, commitSig1}, []int{}},
    99  		{"one absent", []types.CommitSig{commitSig0, absentSig}, []int{1}},
   100  		{"multiple absent", []types.CommitSig{absentSig, absentSig}, []int{0, 1}},
   101  	}
   102  
   103  	for _, tc := range testCases {
   104  		lastCommit := types.NewCommit(1, 0, prevBlockID, tc.lastCommitSigs)
   105  
   106  		// block for height 2
   107  		block, _ := state.MakeBlock(2, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address)
   108  
   109  		_, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateStore, 1)
   110  		require.Nil(t, err, tc.desc)
   111  
   112  		// -> app receives a list of validators with a bool indicating if they signed
   113  		ctr := 0
   114  		for i, v := range app.CommitVotes {
   115  			if ctr < len(tc.expectedAbsentValidators) &&
   116  				tc.expectedAbsentValidators[ctr] == i {
   117  
   118  				assert.False(t, v.SignedLastBlock)
   119  				ctr++
   120  			} else {
   121  				assert.True(t, v.SignedLastBlock)
   122  			}
   123  		}
   124  	}
   125  }
   126  
   127  // TestBeginBlockByzantineValidators ensures we send byzantine validators list.
   128  func TestBeginBlockByzantineValidators(t *testing.T) {
   129  	app := &testApp{}
   130  	cc := proxy.NewLocalClientCreator(app)
   131  	proxyApp := proxy.NewAppConns(cc)
   132  	err := proxyApp.Start()
   133  	require.Nil(t, err)
   134  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
   135  
   136  	state, stateDB, privVals := makeState(1, 1)
   137  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   138  		DiscardABCIResponses: false,
   139  	})
   140  
   141  	defaultEvidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
   142  	privVal := privVals[state.Validators.Validators[0].Address.String()]
   143  	blockID := makeBlockID([]byte("headerhash"), 1000, []byte("partshash"))
   144  	header := &types.Header{
   145  		Version:            tmversion.Consensus{Block: version.BlockProtocol, App: 1},
   146  		ChainID:            state.ChainID,
   147  		Height:             10,
   148  		Time:               defaultEvidenceTime,
   149  		LastBlockID:        blockID,
   150  		LastCommitHash:     crypto.CRandBytes(tmhash.Size),
   151  		DataHash:           crypto.CRandBytes(tmhash.Size),
   152  		ValidatorsHash:     state.Validators.Hash(),
   153  		NextValidatorsHash: state.Validators.Hash(),
   154  		ConsensusHash:      crypto.CRandBytes(tmhash.Size),
   155  		AppHash:            crypto.CRandBytes(tmhash.Size),
   156  		LastResultsHash:    crypto.CRandBytes(tmhash.Size),
   157  		EvidenceHash:       crypto.CRandBytes(tmhash.Size),
   158  		ProposerAddress:    crypto.CRandBytes(crypto.AddressSize),
   159  	}
   160  
   161  	// we don't need to worry about validating the evidence as long as they pass validate basic
   162  	dve := types.NewMockDuplicateVoteEvidenceWithValidator(3, defaultEvidenceTime, privVal, state.ChainID)
   163  	dve.ValidatorPower = 1000
   164  	lcae := &types.LightClientAttackEvidence{
   165  		ConflictingBlock: &types.LightBlock{
   166  			SignedHeader: &types.SignedHeader{
   167  				Header: header,
   168  				Commit: types.NewCommit(10, 0, makeBlockID(header.Hash(), 100, []byte("partshash")), []types.CommitSig{{
   169  					BlockIDFlag:      types.BlockIDFlagNil,
   170  					ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
   171  					Timestamp:        defaultEvidenceTime,
   172  					Signature:        crypto.CRandBytes(types.MaxSignatureSize),
   173  				}}),
   174  			},
   175  			ValidatorSet: state.Validators,
   176  		},
   177  		CommonHeight:        8,
   178  		ByzantineValidators: []*types.Validator{state.Validators.Validators[0]},
   179  		TotalVotingPower:    12,
   180  		Timestamp:           defaultEvidenceTime,
   181  	}
   182  
   183  	ev := []types.Evidence{dve, lcae}
   184  
   185  	abciEv := []abci.Evidence{
   186  		{
   187  			Type:             abci.EvidenceType_DUPLICATE_VOTE,
   188  			Height:           3,
   189  			Time:             defaultEvidenceTime,
   190  			Validator:        types.TM2PB.Validator(state.Validators.Validators[0]),
   191  			TotalVotingPower: 10,
   192  		},
   193  		{
   194  			Type:             abci.EvidenceType_LIGHT_CLIENT_ATTACK,
   195  			Height:           8,
   196  			Time:             defaultEvidenceTime,
   197  			Validator:        types.TM2PB.Validator(state.Validators.Validators[0]),
   198  			TotalVotingPower: 12,
   199  		},
   200  	}
   201  
   202  	evpool := &mocks.EvidencePool{}
   203  	evpool.On("PendingEvidence", mock.AnythingOfType("int64")).Return(ev, int64(100))
   204  	evpool.On("Update", mock.AnythingOfType("state.State"), mock.AnythingOfType("types.EvidenceList")).Return()
   205  	evpool.On("CheckEvidence", mock.AnythingOfType("types.EvidenceList")).Return(nil)
   206  
   207  	blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
   208  		mmock.Mempool{}, evpool)
   209  
   210  	block := makeBlock(state, 1)
   211  	block.Evidence = types.EvidenceData{Evidence: ev}
   212  	block.Header.EvidenceHash = block.Evidence.Hash()
   213  	blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
   214  
   215  	state, retainHeight, err := blockExec.ApplyBlock(state, blockID, block)
   216  	require.Nil(t, err)
   217  	assert.EqualValues(t, retainHeight, 1)
   218  
   219  	// TODO check state and mempool
   220  	assert.Equal(t, abciEv, app.ByzantineValidators)
   221  }
   222  
   223  func TestValidateValidatorUpdates(t *testing.T) {
   224  	pubkey1 := ed25519.GenPrivKey().PubKey()
   225  	pubkey2 := ed25519.GenPrivKey().PubKey()
   226  	pk1, err := cryptoenc.PubKeyToProto(pubkey1)
   227  	assert.NoError(t, err)
   228  	pk2, err := cryptoenc.PubKeyToProto(pubkey2)
   229  	assert.NoError(t, err)
   230  
   231  	defaultValidatorParams := tmproto.ValidatorParams{PubKeyTypes: []string{types.ABCIPubKeyTypeEd25519}}
   232  
   233  	testCases := []struct {
   234  		name string
   235  
   236  		abciUpdates     []abci.ValidatorUpdate
   237  		validatorParams tmproto.ValidatorParams
   238  
   239  		shouldErr bool
   240  	}{
   241  		{
   242  			"adding a validator is OK",
   243  			[]abci.ValidatorUpdate{{PubKey: pk2, Power: 20}},
   244  			defaultValidatorParams,
   245  			false,
   246  		},
   247  		{
   248  			"updating a validator is OK",
   249  			[]abci.ValidatorUpdate{{PubKey: pk1, Power: 20}},
   250  			defaultValidatorParams,
   251  			false,
   252  		},
   253  		{
   254  			"removing a validator is OK",
   255  			[]abci.ValidatorUpdate{{PubKey: pk2, Power: 0}},
   256  			defaultValidatorParams,
   257  			false,
   258  		},
   259  		{
   260  			"adding a validator with negative power results in error",
   261  			[]abci.ValidatorUpdate{{PubKey: pk2, Power: -100}},
   262  			defaultValidatorParams,
   263  			true,
   264  		},
   265  	}
   266  
   267  	for _, tc := range testCases {
   268  		tc := tc
   269  		t.Run(tc.name, func(t *testing.T) {
   270  			err := sm.ValidateValidatorUpdates(tc.abciUpdates, tc.validatorParams)
   271  			if tc.shouldErr {
   272  				assert.Error(t, err)
   273  			} else {
   274  				assert.NoError(t, err)
   275  			}
   276  		})
   277  	}
   278  }
   279  
   280  func TestUpdateValidators(t *testing.T) {
   281  	pubkey1 := ed25519.GenPrivKey().PubKey()
   282  	val1 := types.NewValidator(pubkey1, 10)
   283  	pubkey2 := ed25519.GenPrivKey().PubKey()
   284  	val2 := types.NewValidator(pubkey2, 20)
   285  
   286  	pk, err := cryptoenc.PubKeyToProto(pubkey1)
   287  	require.NoError(t, err)
   288  	pk2, err := cryptoenc.PubKeyToProto(pubkey2)
   289  	require.NoError(t, err)
   290  
   291  	testCases := []struct {
   292  		name string
   293  
   294  		currentSet  *types.ValidatorSet
   295  		abciUpdates []abci.ValidatorUpdate
   296  
   297  		resultingSet *types.ValidatorSet
   298  		shouldErr    bool
   299  	}{
   300  		{
   301  			"adding a validator is OK",
   302  			types.NewValidatorSet([]*types.Validator{val1}),
   303  			[]abci.ValidatorUpdate{{PubKey: pk2, Power: 20}},
   304  			types.NewValidatorSet([]*types.Validator{val1, val2}),
   305  			false,
   306  		},
   307  		{
   308  			"updating a validator is OK",
   309  			types.NewValidatorSet([]*types.Validator{val1}),
   310  			[]abci.ValidatorUpdate{{PubKey: pk, Power: 20}},
   311  			types.NewValidatorSet([]*types.Validator{types.NewValidator(pubkey1, 20)}),
   312  			false,
   313  		},
   314  		{
   315  			"removing a validator is OK",
   316  			types.NewValidatorSet([]*types.Validator{val1, val2}),
   317  			[]abci.ValidatorUpdate{{PubKey: pk2, Power: 0}},
   318  			types.NewValidatorSet([]*types.Validator{val1}),
   319  			false,
   320  		},
   321  		{
   322  			"removing a non-existing validator results in error",
   323  			types.NewValidatorSet([]*types.Validator{val1}),
   324  			[]abci.ValidatorUpdate{{PubKey: pk2, Power: 0}},
   325  			types.NewValidatorSet([]*types.Validator{val1}),
   326  			true,
   327  		},
   328  	}
   329  
   330  	for _, tc := range testCases {
   331  		tc := tc
   332  		t.Run(tc.name, func(t *testing.T) {
   333  			updates, err := types.PB2TM.ValidatorUpdates(tc.abciUpdates)
   334  			assert.NoError(t, err)
   335  			err = tc.currentSet.UpdateWithChangeSet(updates)
   336  			if tc.shouldErr {
   337  				assert.Error(t, err)
   338  			} else {
   339  				assert.NoError(t, err)
   340  				require.Equal(t, tc.resultingSet.Size(), tc.currentSet.Size())
   341  
   342  				assert.Equal(t, tc.resultingSet.TotalVotingPower(), tc.currentSet.TotalVotingPower())
   343  
   344  				assert.Equal(t, tc.resultingSet.Validators[0].Address, tc.currentSet.Validators[0].Address)
   345  				if tc.resultingSet.Size() > 1 {
   346  					assert.Equal(t, tc.resultingSet.Validators[1].Address, tc.currentSet.Validators[1].Address)
   347  				}
   348  			}
   349  		})
   350  	}
   351  }
   352  
   353  // TestEndBlockValidatorUpdates ensures we update validator set and send an event.
   354  func TestEndBlockValidatorUpdates(t *testing.T) {
   355  	app := &testApp{}
   356  	cc := proxy.NewLocalClientCreator(app)
   357  	proxyApp := proxy.NewAppConns(cc)
   358  	err := proxyApp.Start()
   359  	require.Nil(t, err)
   360  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
   361  
   362  	state, stateDB, _ := makeState(1, 1)
   363  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   364  		DiscardABCIResponses: false,
   365  	})
   366  
   367  	blockExec := sm.NewBlockExecutor(
   368  		stateStore,
   369  		log.TestingLogger(),
   370  		proxyApp.Consensus(),
   371  		mmock.Mempool{},
   372  		sm.EmptyEvidencePool{},
   373  	)
   374  
   375  	eventBus := types.NewEventBus()
   376  	err = eventBus.Start()
   377  	require.NoError(t, err)
   378  	defer eventBus.Stop() //nolint:errcheck // ignore for tests
   379  
   380  	blockExec.SetEventBus(eventBus)
   381  
   382  	updatesSub, err := eventBus.Subscribe(
   383  		context.Background(),
   384  		"TestEndBlockValidatorUpdates",
   385  		types.EventQueryValidatorSetUpdates,
   386  	)
   387  	require.NoError(t, err)
   388  
   389  	block := makeBlock(state, 1)
   390  	blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
   391  
   392  	pubkey := ed25519.GenPrivKey().PubKey()
   393  	pk, err := cryptoenc.PubKeyToProto(pubkey)
   394  	require.NoError(t, err)
   395  	app.ValidatorUpdates = []abci.ValidatorUpdate{
   396  		{PubKey: pk, Power: 10},
   397  	}
   398  
   399  	state, _, err = blockExec.ApplyBlock(state, blockID, block)
   400  	require.Nil(t, err)
   401  	// test new validator was added to NextValidators
   402  	if assert.Equal(t, state.Validators.Size()+1, state.NextValidators.Size()) {
   403  		idx, _ := state.NextValidators.GetByAddress(pubkey.Address())
   404  		if idx < 0 {
   405  			t.Fatalf("can't find address %v in the set %v", pubkey.Address(), state.NextValidators)
   406  		}
   407  	}
   408  
   409  	// test we threw an event
   410  	select {
   411  	case msg := <-updatesSub.Out():
   412  		event, ok := msg.Data().(types.EventDataValidatorSetUpdates)
   413  		require.True(t, ok, "Expected event of type EventDataValidatorSetUpdates, got %T", msg.Data())
   414  		if assert.NotEmpty(t, event.ValidatorUpdates) {
   415  			assert.Equal(t, pubkey, event.ValidatorUpdates[0].PubKey)
   416  			assert.EqualValues(t, 10, event.ValidatorUpdates[0].VotingPower)
   417  		}
   418  	case <-updatesSub.Cancelled():
   419  		t.Fatalf("updatesSub was cancelled (reason: %v)", updatesSub.Err())
   420  	case <-time.After(1 * time.Second):
   421  		t.Fatal("Did not receive EventValidatorSetUpdates within 1 sec.")
   422  	}
   423  }
   424  
   425  // TestEndBlockValidatorUpdatesResultingInEmptySet checks that processing validator updates that
   426  // would result in empty set causes no panic, an error is raised and NextValidators is not updated
   427  func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) {
   428  	app := &testApp{}
   429  	cc := proxy.NewLocalClientCreator(app)
   430  	proxyApp := proxy.NewAppConns(cc)
   431  	err := proxyApp.Start()
   432  	require.Nil(t, err)
   433  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
   434  
   435  	state, stateDB, _ := makeState(1, 1)
   436  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   437  		DiscardABCIResponses: false,
   438  	})
   439  	blockExec := sm.NewBlockExecutor(
   440  		stateStore,
   441  		log.TestingLogger(),
   442  		proxyApp.Consensus(),
   443  		mmock.Mempool{},
   444  		sm.EmptyEvidencePool{},
   445  	)
   446  
   447  	block := makeBlock(state, 1)
   448  	blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()}
   449  
   450  	vp, err := cryptoenc.PubKeyToProto(state.Validators.Validators[0].PubKey)
   451  	require.NoError(t, err)
   452  	// Remove the only validator
   453  	app.ValidatorUpdates = []abci.ValidatorUpdate{
   454  		{PubKey: vp, Power: 0},
   455  	}
   456  
   457  	assert.NotPanics(t, func() { state, _, err = blockExec.ApplyBlock(state, blockID, block) })
   458  	assert.NotNil(t, err)
   459  	assert.NotEmpty(t, state.NextValidators.Validators)
   460  }
   461  
   462  func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.BlockID {
   463  	var (
   464  		h   = make([]byte, tmhash.Size)
   465  		psH = make([]byte, tmhash.Size)
   466  	)
   467  	copy(h, hash)
   468  	copy(psH, partSetHash)
   469  	return types.BlockID{
   470  		Hash: h,
   471  		PartSetHeader: types.PartSetHeader{
   472  			Total: partSetSize,
   473  			Hash:  psH,
   474  		},
   475  	}
   476  }