github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/state/execution_test.go (about)

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