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 }