github.com/evdatsion/aphelion-dpos-bft@v0.32.1/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/require"
    10  	"github.com/evdatsion/aphelion-dpos-bft/abci/example/kvstore"
    11  	abci "github.com/evdatsion/aphelion-dpos-bft/abci/types"
    12  	"github.com/evdatsion/aphelion-dpos-bft/crypto/ed25519"
    13  	"github.com/evdatsion/aphelion-dpos-bft/crypto/secp256k1"
    14  	"github.com/evdatsion/aphelion-dpos-bft/libs/log"
    15  	"github.com/evdatsion/aphelion-dpos-bft/mock"
    16  	"github.com/evdatsion/aphelion-dpos-bft/proxy"
    17  	sm "github.com/evdatsion/aphelion-dpos-bft/state"
    18  	"github.com/evdatsion/aphelion-dpos-bft/types"
    19  	tmtime "github.com/evdatsion/aphelion-dpos-bft/types/time"
    20  )
    21  
    22  var (
    23  	chainID      = "execution_chain"
    24  	testPartSize = 65536
    25  	nTxsPerBlock = 10
    26  )
    27  
    28  func TestApplyBlock(t *testing.T) {
    29  	cc := proxy.NewLocalClientCreator(kvstore.NewKVStoreApplication())
    30  	proxyApp := proxy.NewAppConns(cc)
    31  	err := proxyApp.Start()
    32  	require.Nil(t, err)
    33  	defer proxyApp.Stop()
    34  
    35  	state, stateDB, _ := makeState(1, 1)
    36  
    37  	blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(),
    38  		mock.Mempool{}, sm.MockEvidencePool{})
    39  
    40  	block := makeBlock(state, 1)
    41  	blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()}
    42  
    43  	//nolint:ineffassign
    44  	state, err = blockExec.ApplyBlock(state, blockID, block)
    45  	require.Nil(t, err)
    46  
    47  	// TODO check state and mempool
    48  }
    49  
    50  // TestBeginBlockValidators ensures we send absent validators list.
    51  func TestBeginBlockValidators(t *testing.T) {
    52  	app := &testApp{}
    53  	cc := proxy.NewLocalClientCreator(app)
    54  	proxyApp := proxy.NewAppConns(cc)
    55  	err := proxyApp.Start()
    56  	require.Nil(t, err)
    57  	defer proxyApp.Stop()
    58  
    59  	state, stateDB, _ := makeState(2, 2)
    60  
    61  	prevHash := state.LastBlockID.Hash
    62  	prevParts := types.PartSetHeader{}
    63  	prevBlockID := types.BlockID{Hash: prevHash, PartsHeader: prevParts}
    64  
    65  	now := tmtime.Now()
    66  	commitSig0 := (&types.Vote{ValidatorIndex: 0, Timestamp: now, Type: types.PrecommitType}).CommitSig()
    67  	commitSig1 := (&types.Vote{ValidatorIndex: 1, Timestamp: now}).CommitSig()
    68  
    69  	testCases := []struct {
    70  		desc                     string
    71  		lastCommitPrecommits     []*types.CommitSig
    72  		expectedAbsentValidators []int
    73  	}{
    74  		{"none absent", []*types.CommitSig{commitSig0, commitSig1}, []int{}},
    75  		{"one absent", []*types.CommitSig{commitSig0, nil}, []int{1}},
    76  		{"multiple absent", []*types.CommitSig{nil, nil}, []int{0, 1}},
    77  	}
    78  
    79  	for _, tc := range testCases {
    80  		lastCommit := types.NewCommit(prevBlockID, tc.lastCommitPrecommits)
    81  
    82  		// block for height 2
    83  		block, _ := state.MakeBlock(2, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address)
    84  
    85  		_, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB)
    86  		require.Nil(t, err, tc.desc)
    87  
    88  		// -> app receives a list of validators with a bool indicating if they signed
    89  		ctr := 0
    90  		for i, v := range app.CommitVotes {
    91  			if ctr < len(tc.expectedAbsentValidators) &&
    92  				tc.expectedAbsentValidators[ctr] == i {
    93  
    94  				assert.False(t, v.SignedLastBlock)
    95  				ctr++
    96  			} else {
    97  				assert.True(t, v.SignedLastBlock)
    98  			}
    99  		}
   100  	}
   101  }
   102  
   103  // TestBeginBlockByzantineValidators ensures we send byzantine validators list.
   104  func TestBeginBlockByzantineValidators(t *testing.T) {
   105  	app := &testApp{}
   106  	cc := proxy.NewLocalClientCreator(app)
   107  	proxyApp := proxy.NewAppConns(cc)
   108  	err := proxyApp.Start()
   109  	require.Nil(t, err)
   110  	defer proxyApp.Stop()
   111  
   112  	state, stateDB, _ := makeState(2, 12)
   113  
   114  	prevHash := state.LastBlockID.Hash
   115  	prevParts := types.PartSetHeader{}
   116  	prevBlockID := types.BlockID{Hash: prevHash, PartsHeader: prevParts}
   117  
   118  	height1, idx1, val1 := int64(8), 0, state.Validators.Validators[0].Address
   119  	height2, idx2, val2 := int64(3), 1, state.Validators.Validators[1].Address
   120  	ev1 := types.NewMockGoodEvidence(height1, idx1, val1)
   121  	ev2 := types.NewMockGoodEvidence(height2, idx2, val2)
   122  
   123  	now := tmtime.Now()
   124  	valSet := state.Validators
   125  	testCases := []struct {
   126  		desc                        string
   127  		evidence                    []types.Evidence
   128  		expectedByzantineValidators []abci.Evidence
   129  	}{
   130  		{"none byzantine", []types.Evidence{}, []abci.Evidence{}},
   131  		{"one byzantine", []types.Evidence{ev1}, []abci.Evidence{types.TM2PB.Evidence(ev1, valSet, now)}},
   132  		{"multiple byzantine", []types.Evidence{ev1, ev2}, []abci.Evidence{
   133  			types.TM2PB.Evidence(ev1, valSet, now),
   134  			types.TM2PB.Evidence(ev2, valSet, now)}},
   135  	}
   136  
   137  	commitSig0 := (&types.Vote{ValidatorIndex: 0, Timestamp: now, Type: types.PrecommitType}).CommitSig()
   138  	commitSig1 := (&types.Vote{ValidatorIndex: 1, Timestamp: now}).CommitSig()
   139  	commitSigs := []*types.CommitSig{commitSig0, commitSig1}
   140  	lastCommit := types.NewCommit(prevBlockID, commitSigs)
   141  	for _, tc := range testCases {
   142  
   143  		block, _ := state.MakeBlock(10, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address)
   144  		block.Time = now
   145  		block.Evidence.Evidence = tc.evidence
   146  		_, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB)
   147  		require.Nil(t, err, tc.desc)
   148  
   149  		// -> app must receive an index of the byzantine validator
   150  		assert.Equal(t, tc.expectedByzantineValidators, app.ByzantineValidators, tc.desc)
   151  	}
   152  }
   153  
   154  func TestValidateValidatorUpdates(t *testing.T) {
   155  	pubkey1 := ed25519.GenPrivKey().PubKey()
   156  	pubkey2 := ed25519.GenPrivKey().PubKey()
   157  
   158  	secpKey := secp256k1.GenPrivKey().PubKey()
   159  
   160  	defaultValidatorParams := types.ValidatorParams{PubKeyTypes: []string{types.ABCIPubKeyTypeEd25519}}
   161  
   162  	testCases := []struct {
   163  		name string
   164  
   165  		abciUpdates     []abci.ValidatorUpdate
   166  		validatorParams types.ValidatorParams
   167  
   168  		shouldErr bool
   169  	}{
   170  		{
   171  			"adding a validator is OK",
   172  
   173  			[]abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey2), Power: 20}},
   174  			defaultValidatorParams,
   175  
   176  			false,
   177  		},
   178  		{
   179  			"updating a validator is OK",
   180  
   181  			[]abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey1), Power: 20}},
   182  			defaultValidatorParams,
   183  
   184  			false,
   185  		},
   186  		{
   187  			"removing a validator is OK",
   188  
   189  			[]abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey2), Power: 0}},
   190  			defaultValidatorParams,
   191  
   192  			false,
   193  		},
   194  		{
   195  			"adding a validator with negative power results in error",
   196  
   197  			[]abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey2), Power: -100}},
   198  			defaultValidatorParams,
   199  
   200  			true,
   201  		},
   202  		{
   203  			"adding a validator with pubkey thats not in validator params results in error",
   204  
   205  			[]abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(secpKey), Power: -100}},
   206  			defaultValidatorParams,
   207  
   208  			true,
   209  		},
   210  	}
   211  
   212  	for _, tc := range testCases {
   213  		t.Run(tc.name, func(t *testing.T) {
   214  			err := sm.ValidateValidatorUpdates(tc.abciUpdates, tc.validatorParams)
   215  			if tc.shouldErr {
   216  				assert.Error(t, err)
   217  			} else {
   218  				assert.NoError(t, err)
   219  			}
   220  		})
   221  	}
   222  }
   223  
   224  func TestUpdateValidators(t *testing.T) {
   225  	pubkey1 := ed25519.GenPrivKey().PubKey()
   226  	val1 := types.NewValidator(pubkey1, 10)
   227  	pubkey2 := ed25519.GenPrivKey().PubKey()
   228  	val2 := types.NewValidator(pubkey2, 20)
   229  
   230  	testCases := []struct {
   231  		name string
   232  
   233  		currentSet  *types.ValidatorSet
   234  		abciUpdates []abci.ValidatorUpdate
   235  
   236  		resultingSet *types.ValidatorSet
   237  		shouldErr    bool
   238  	}{
   239  		{
   240  			"adding a validator is OK",
   241  
   242  			types.NewValidatorSet([]*types.Validator{val1}),
   243  			[]abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey2), Power: 20}},
   244  
   245  			types.NewValidatorSet([]*types.Validator{val1, val2}),
   246  			false,
   247  		},
   248  		{
   249  			"updating a validator is OK",
   250  
   251  			types.NewValidatorSet([]*types.Validator{val1}),
   252  			[]abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey1), Power: 20}},
   253  
   254  			types.NewValidatorSet([]*types.Validator{types.NewValidator(pubkey1, 20)}),
   255  			false,
   256  		},
   257  		{
   258  			"removing a validator is OK",
   259  
   260  			types.NewValidatorSet([]*types.Validator{val1, val2}),
   261  			[]abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey2), Power: 0}},
   262  
   263  			types.NewValidatorSet([]*types.Validator{val1}),
   264  			false,
   265  		},
   266  		{
   267  			"removing a non-existing validator results in error",
   268  
   269  			types.NewValidatorSet([]*types.Validator{val1}),
   270  			[]abci.ValidatorUpdate{{PubKey: types.TM2PB.PubKey(pubkey2), Power: 0}},
   271  
   272  			types.NewValidatorSet([]*types.Validator{val1}),
   273  			true,
   274  		},
   275  	}
   276  
   277  	for _, tc := range testCases {
   278  		t.Run(tc.name, func(t *testing.T) {
   279  			updates, err := types.PB2TM.ValidatorUpdates(tc.abciUpdates)
   280  			assert.NoError(t, err)
   281  			err = tc.currentSet.UpdateWithChangeSet(updates)
   282  			if tc.shouldErr {
   283  				assert.Error(t, err)
   284  			} else {
   285  				assert.NoError(t, err)
   286  				require.Equal(t, tc.resultingSet.Size(), tc.currentSet.Size())
   287  
   288  				assert.Equal(t, tc.resultingSet.TotalVotingPower(), tc.currentSet.TotalVotingPower())
   289  
   290  				assert.Equal(t, tc.resultingSet.Validators[0].Address, tc.currentSet.Validators[0].Address)
   291  				if tc.resultingSet.Size() > 1 {
   292  					assert.Equal(t, tc.resultingSet.Validators[1].Address, tc.currentSet.Validators[1].Address)
   293  				}
   294  			}
   295  		})
   296  	}
   297  }
   298  
   299  // TestEndBlockValidatorUpdates ensures we update validator set and send an event.
   300  func TestEndBlockValidatorUpdates(t *testing.T) {
   301  	app := &testApp{}
   302  	cc := proxy.NewLocalClientCreator(app)
   303  	proxyApp := proxy.NewAppConns(cc)
   304  	err := proxyApp.Start()
   305  	require.Nil(t, err)
   306  	defer proxyApp.Stop()
   307  
   308  	state, stateDB, _ := makeState(1, 1)
   309  
   310  	blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, sm.MockEvidencePool{})
   311  
   312  	eventBus := types.NewEventBus()
   313  	err = eventBus.Start()
   314  	require.NoError(t, err)
   315  	defer eventBus.Stop()
   316  	blockExec.SetEventBus(eventBus)
   317  
   318  	updatesSub, err := eventBus.Subscribe(context.Background(), "TestEndBlockValidatorUpdates", types.EventQueryValidatorSetUpdates)
   319  	require.NoError(t, err)
   320  
   321  	block := makeBlock(state, 1)
   322  	blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()}
   323  
   324  	pubkey := ed25519.GenPrivKey().PubKey()
   325  	app.ValidatorUpdates = []abci.ValidatorUpdate{
   326  		{PubKey: types.TM2PB.PubKey(pubkey), Power: 10},
   327  	}
   328  
   329  	state, err = blockExec.ApplyBlock(state, blockID, block)
   330  	require.Nil(t, err)
   331  
   332  	// test new validator was added to NextValidators
   333  	if assert.Equal(t, state.Validators.Size()+1, state.NextValidators.Size()) {
   334  		idx, _ := state.NextValidators.GetByAddress(pubkey.Address())
   335  		if idx < 0 {
   336  			t.Fatalf("can't find address %v in the set %v", pubkey.Address(), state.NextValidators)
   337  		}
   338  	}
   339  
   340  	// test we threw an event
   341  	select {
   342  	case msg := <-updatesSub.Out():
   343  		event, ok := msg.Data().(types.EventDataValidatorSetUpdates)
   344  		require.True(t, ok, "Expected event of type EventDataValidatorSetUpdates, got %T", msg.Data())
   345  		if assert.NotEmpty(t, event.ValidatorUpdates) {
   346  			assert.Equal(t, pubkey, event.ValidatorUpdates[0].PubKey)
   347  			assert.EqualValues(t, 10, event.ValidatorUpdates[0].VotingPower)
   348  		}
   349  	case <-updatesSub.Cancelled():
   350  		t.Fatalf("updatesSub was cancelled (reason: %v)", updatesSub.Err())
   351  	case <-time.After(1 * time.Second):
   352  		t.Fatal("Did not receive EventValidatorSetUpdates within 1 sec.")
   353  	}
   354  }
   355  
   356  // TestEndBlockValidatorUpdatesResultingInEmptySet checks that processing validator updates that
   357  // would result in empty set causes no panic, an error is raised and NextValidators is not updated
   358  func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) {
   359  	app := &testApp{}
   360  	cc := proxy.NewLocalClientCreator(app)
   361  	proxyApp := proxy.NewAppConns(cc)
   362  	err := proxyApp.Start()
   363  	require.Nil(t, err)
   364  	defer proxyApp.Stop()
   365  
   366  	state, stateDB, _ := makeState(1, 1)
   367  	blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, sm.MockEvidencePool{})
   368  
   369  	block := makeBlock(state, 1)
   370  	blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()}
   371  
   372  	// Remove the only validator
   373  	app.ValidatorUpdates = []abci.ValidatorUpdate{
   374  		{PubKey: types.TM2PB.PubKey(state.Validators.Validators[0].PubKey), Power: 0},
   375  	}
   376  
   377  	assert.NotPanics(t, func() { state, err = blockExec.ApplyBlock(state, blockID, block) })
   378  	assert.NotNil(t, err)
   379  	assert.NotEmpty(t, state.NextValidators.Validators)
   380  
   381  }