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 }