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