github.com/Oyster-zx/tendermint@v0.34.24-fork/types/event_bus_test.go (about) 1 package types 2 3 import ( 4 "context" 5 "fmt" 6 "math/rand" 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 abci "github.com/tendermint/tendermint/abci/types" 14 tmpubsub "github.com/tendermint/tendermint/libs/pubsub" 15 tmquery "github.com/tendermint/tendermint/libs/pubsub/query" 16 tmrand "github.com/tendermint/tendermint/libs/rand" 17 ) 18 19 func TestEventBusPublishEventTx(t *testing.T) { 20 eventBus := NewEventBus() 21 err := eventBus.Start() 22 require.NoError(t, err) 23 t.Cleanup(func() { 24 if err := eventBus.Stop(); err != nil { 25 t.Error(err) 26 } 27 }) 28 29 tx := Tx("foo") 30 result := abci.ResponseDeliverTx{ 31 Data: []byte("bar"), 32 Events: []abci.Event{ 33 {Type: "testType", Attributes: []abci.EventAttribute{{Key: []byte("baz"), Value: []byte("1")}}}, 34 }, 35 } 36 37 // PublishEventTx adds 3 composite keys, so the query below should work 38 query := fmt.Sprintf("tm.event='Tx' AND tx.height=1 AND tx.hash='%X' AND testType.baz=1", tx.Hash()) 39 txsSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query)) 40 require.NoError(t, err) 41 42 done := make(chan struct{}) 43 go func() { 44 msg := <-txsSub.Out() 45 edt := msg.Data().(EventDataTx) 46 assert.Equal(t, int64(1), edt.Height) 47 assert.Equal(t, uint32(0), edt.Index) 48 assert.EqualValues(t, tx, edt.Tx) 49 assert.Equal(t, result, edt.Result) 50 close(done) 51 }() 52 53 err = eventBus.PublishEventTx(EventDataTx{abci.TxResult{ 54 Height: 1, 55 Index: 0, 56 Tx: tx, 57 Result: result, 58 }}) 59 assert.NoError(t, err) 60 61 select { 62 case <-done: 63 case <-time.After(1 * time.Second): 64 t.Fatal("did not receive a transaction after 1 sec.") 65 } 66 } 67 68 func TestEventBusPublishEventNewBlock(t *testing.T) { 69 eventBus := NewEventBus() 70 err := eventBus.Start() 71 require.NoError(t, err) 72 t.Cleanup(func() { 73 if err := eventBus.Stop(); err != nil { 74 t.Error(err) 75 } 76 }) 77 78 block := MakeBlock(0, []Tx{}, nil, []Evidence{}) 79 resultBeginBlock := abci.ResponseBeginBlock{ 80 Events: []abci.Event{ 81 {Type: "testType", Attributes: []abci.EventAttribute{{Key: []byte("baz"), Value: []byte("1")}}}, 82 }, 83 } 84 resultEndBlock := abci.ResponseEndBlock{ 85 Events: []abci.Event{ 86 {Type: "testType", Attributes: []abci.EventAttribute{{Key: []byte("foz"), Value: []byte("2")}}}, 87 }, 88 } 89 90 // PublishEventNewBlock adds the tm.event compositeKey, so the query below should work 91 query := "tm.event='NewBlock' AND testType.baz=1 AND testType.foz=2" 92 blocksSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query)) 93 require.NoError(t, err) 94 95 done := make(chan struct{}) 96 go func() { 97 msg := <-blocksSub.Out() 98 edt := msg.Data().(EventDataNewBlock) 99 assert.Equal(t, block, edt.Block) 100 assert.Equal(t, resultBeginBlock, edt.ResultBeginBlock) 101 assert.Equal(t, resultEndBlock, edt.ResultEndBlock) 102 close(done) 103 }() 104 105 err = eventBus.PublishEventNewBlock(EventDataNewBlock{ 106 Block: block, 107 ResultBeginBlock: resultBeginBlock, 108 ResultEndBlock: resultEndBlock, 109 }) 110 assert.NoError(t, err) 111 112 select { 113 case <-done: 114 case <-time.After(1 * time.Second): 115 t.Fatal("did not receive a block after 1 sec.") 116 } 117 } 118 119 func TestEventBusPublishEventTxDuplicateKeys(t *testing.T) { 120 eventBus := NewEventBus() 121 err := eventBus.Start() 122 require.NoError(t, err) 123 t.Cleanup(func() { 124 if err := eventBus.Stop(); err != nil { 125 t.Error(err) 126 } 127 }) 128 129 tx := Tx("foo") 130 result := abci.ResponseDeliverTx{ 131 Data: []byte("bar"), 132 Events: []abci.Event{ 133 { 134 Type: "transfer", 135 Attributes: []abci.EventAttribute{ 136 {Key: []byte("sender"), Value: []byte("foo")}, 137 {Key: []byte("recipient"), Value: []byte("bar")}, 138 {Key: []byte("amount"), Value: []byte("5")}, 139 }, 140 }, 141 { 142 Type: "transfer", 143 Attributes: []abci.EventAttribute{ 144 {Key: []byte("sender"), Value: []byte("baz")}, 145 {Key: []byte("recipient"), Value: []byte("cat")}, 146 {Key: []byte("amount"), Value: []byte("13")}, 147 }, 148 }, 149 { 150 Type: "withdraw.rewards", 151 Attributes: []abci.EventAttribute{ 152 {Key: []byte("address"), Value: []byte("bar")}, 153 {Key: []byte("source"), Value: []byte("iceman")}, 154 {Key: []byte("amount"), Value: []byte("33")}, 155 }, 156 }, 157 }, 158 } 159 160 testCases := []struct { 161 query string 162 expectResults bool 163 }{ 164 { 165 "tm.event='Tx' AND tx.height=1 AND transfer.sender='DoesNotExist'", 166 false, 167 }, 168 { 169 "tm.event='Tx' AND tx.height=1 AND transfer.sender='foo'", 170 true, 171 }, 172 { 173 "tm.event='Tx' AND tx.height=1 AND transfer.sender='baz'", 174 true, 175 }, 176 { 177 "tm.event='Tx' AND tx.height=1 AND transfer.sender='foo' AND transfer.sender='baz'", 178 true, 179 }, 180 { 181 "tm.event='Tx' AND tx.height=1 AND transfer.sender='foo' AND transfer.sender='DoesNotExist'", 182 false, 183 }, 184 } 185 186 for i, tc := range testCases { 187 sub, err := eventBus.Subscribe(context.Background(), fmt.Sprintf("client-%d", i), tmquery.MustParse(tc.query)) 188 require.NoError(t, err) 189 190 done := make(chan struct{}) 191 192 go func() { 193 select { 194 case msg := <-sub.Out(): 195 data := msg.Data().(EventDataTx) 196 assert.Equal(t, int64(1), data.Height) 197 assert.Equal(t, uint32(0), data.Index) 198 assert.EqualValues(t, tx, data.Tx) 199 assert.Equal(t, result, data.Result) 200 close(done) 201 case <-time.After(1 * time.Second): 202 return 203 } 204 }() 205 206 err = eventBus.PublishEventTx(EventDataTx{abci.TxResult{ 207 Height: 1, 208 Index: 0, 209 Tx: tx, 210 Result: result, 211 }}) 212 assert.NoError(t, err) 213 214 select { 215 case <-done: 216 if !tc.expectResults { 217 require.Fail(t, "unexpected transaction result(s) from subscription") 218 } 219 case <-time.After(1 * time.Second): 220 if tc.expectResults { 221 require.Fail(t, "failed to receive a transaction after 1 second") 222 } 223 } 224 } 225 } 226 227 func TestEventBusPublishEventNewBlockHeader(t *testing.T) { 228 eventBus := NewEventBus() 229 err := eventBus.Start() 230 require.NoError(t, err) 231 t.Cleanup(func() { 232 if err := eventBus.Stop(); err != nil { 233 t.Error(err) 234 } 235 }) 236 237 block := MakeBlock(0, []Tx{}, nil, []Evidence{}) 238 resultBeginBlock := abci.ResponseBeginBlock{ 239 Events: []abci.Event{ 240 {Type: "testType", Attributes: []abci.EventAttribute{{Key: []byte("baz"), Value: []byte("1")}}}, 241 }, 242 } 243 resultEndBlock := abci.ResponseEndBlock{ 244 Events: []abci.Event{ 245 {Type: "testType", Attributes: []abci.EventAttribute{{Key: []byte("foz"), Value: []byte("2")}}}, 246 }, 247 } 248 249 // PublishEventNewBlockHeader adds the tm.event compositeKey, so the query below should work 250 query := "tm.event='NewBlockHeader' AND testType.baz=1 AND testType.foz=2" 251 headersSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query)) 252 require.NoError(t, err) 253 254 done := make(chan struct{}) 255 go func() { 256 msg := <-headersSub.Out() 257 edt := msg.Data().(EventDataNewBlockHeader) 258 assert.Equal(t, block.Header, edt.Header) 259 assert.Equal(t, resultBeginBlock, edt.ResultBeginBlock) 260 assert.Equal(t, resultEndBlock, edt.ResultEndBlock) 261 close(done) 262 }() 263 264 err = eventBus.PublishEventNewBlockHeader(EventDataNewBlockHeader{ 265 Header: block.Header, 266 ResultBeginBlock: resultBeginBlock, 267 ResultEndBlock: resultEndBlock, 268 }) 269 assert.NoError(t, err) 270 271 select { 272 case <-done: 273 case <-time.After(1 * time.Second): 274 t.Fatal("did not receive a block header after 1 sec.") 275 } 276 } 277 278 func TestEventBusPublishEventNewEvidence(t *testing.T) { 279 eventBus := NewEventBus() 280 err := eventBus.Start() 281 require.NoError(t, err) 282 t.Cleanup(func() { 283 if err := eventBus.Stop(); err != nil { 284 t.Error(err) 285 } 286 }) 287 288 ev := NewMockDuplicateVoteEvidence(1, time.Now(), "test-chain-id") 289 290 query := "tm.event='NewEvidence'" 291 evSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query)) 292 require.NoError(t, err) 293 294 done := make(chan struct{}) 295 go func() { 296 msg := <-evSub.Out() 297 edt := msg.Data().(EventDataNewEvidence) 298 assert.Equal(t, ev, edt.Evidence) 299 assert.Equal(t, int64(4), edt.Height) 300 close(done) 301 }() 302 303 err = eventBus.PublishEventNewEvidence(EventDataNewEvidence{ 304 Evidence: ev, 305 Height: 4, 306 }) 307 assert.NoError(t, err) 308 309 select { 310 case <-done: 311 case <-time.After(1 * time.Second): 312 t.Fatal("did not receive a block header after 1 sec.") 313 } 314 } 315 316 func TestEventBusPublish(t *testing.T) { 317 eventBus := NewEventBus() 318 err := eventBus.Start() 319 require.NoError(t, err) 320 t.Cleanup(func() { 321 if err := eventBus.Stop(); err != nil { 322 t.Error(err) 323 } 324 }) 325 326 const numEventsExpected = 14 327 328 sub, err := eventBus.Subscribe(context.Background(), "test", tmquery.Empty{}, numEventsExpected) 329 require.NoError(t, err) 330 331 done := make(chan struct{}) 332 go func() { 333 numEvents := 0 334 for range sub.Out() { 335 numEvents++ 336 if numEvents >= numEventsExpected { 337 close(done) 338 return 339 } 340 } 341 }() 342 343 err = eventBus.Publish(EventNewBlockHeader, EventDataNewBlockHeader{}) 344 require.NoError(t, err) 345 err = eventBus.PublishEventNewBlock(EventDataNewBlock{}) 346 require.NoError(t, err) 347 err = eventBus.PublishEventNewBlockHeader(EventDataNewBlockHeader{}) 348 require.NoError(t, err) 349 err = eventBus.PublishEventVote(EventDataVote{}) 350 require.NoError(t, err) 351 err = eventBus.PublishEventNewRoundStep(EventDataRoundState{}) 352 require.NoError(t, err) 353 err = eventBus.PublishEventTimeoutPropose(EventDataRoundState{}) 354 require.NoError(t, err) 355 err = eventBus.PublishEventTimeoutWait(EventDataRoundState{}) 356 require.NoError(t, err) 357 err = eventBus.PublishEventNewRound(EventDataNewRound{}) 358 require.NoError(t, err) 359 err = eventBus.PublishEventCompleteProposal(EventDataCompleteProposal{}) 360 require.NoError(t, err) 361 err = eventBus.PublishEventPolka(EventDataRoundState{}) 362 require.NoError(t, err) 363 err = eventBus.PublishEventUnlock(EventDataRoundState{}) 364 require.NoError(t, err) 365 err = eventBus.PublishEventRelock(EventDataRoundState{}) 366 require.NoError(t, err) 367 err = eventBus.PublishEventLock(EventDataRoundState{}) 368 require.NoError(t, err) 369 err = eventBus.PublishEventValidatorSetUpdates(EventDataValidatorSetUpdates{}) 370 require.NoError(t, err) 371 372 select { 373 case <-done: 374 case <-time.After(1 * time.Second): 375 t.Fatalf("expected to receive %d events after 1 sec.", numEventsExpected) 376 } 377 } 378 379 func BenchmarkEventBus(b *testing.B) { 380 benchmarks := []struct { 381 name string 382 numClients int 383 randQueries bool 384 randEvents bool 385 }{ 386 {"10Clients1Query1Event", 10, false, false}, 387 {"100Clients", 100, false, false}, 388 {"1000Clients", 1000, false, false}, 389 390 {"10ClientsRandQueries1Event", 10, true, false}, 391 {"100Clients", 100, true, false}, 392 {"1000Clients", 1000, true, false}, 393 394 {"10ClientsRandQueriesRandEvents", 10, true, true}, 395 {"100Clients", 100, true, true}, 396 {"1000Clients", 1000, true, true}, 397 398 {"10Clients1QueryRandEvents", 10, false, true}, 399 {"100Clients", 100, false, true}, 400 {"1000Clients", 1000, false, true}, 401 } 402 403 for _, bm := range benchmarks { 404 bm := bm 405 b.Run(bm.name, func(b *testing.B) { 406 benchmarkEventBus(bm.numClients, bm.randQueries, bm.randEvents, b) 407 }) 408 } 409 } 410 411 func benchmarkEventBus(numClients int, randQueries bool, randEvents bool, b *testing.B) { 412 // for random* functions 413 rand.Seed(time.Now().Unix()) 414 415 eventBus := NewEventBusWithBufferCapacity(0) // set buffer capacity to 0 so we are not testing cache 416 err := eventBus.Start() 417 if err != nil { 418 b.Error(err) 419 } 420 b.Cleanup(func() { 421 if err := eventBus.Stop(); err != nil { 422 b.Error(err) 423 } 424 }) 425 426 ctx := context.Background() 427 q := EventQueryNewBlock 428 429 for i := 0; i < numClients; i++ { 430 if randQueries { 431 q = randQuery() 432 } 433 sub, err := eventBus.Subscribe(ctx, fmt.Sprintf("client-%d", i), q) 434 if err != nil { 435 b.Fatal(err) 436 } 437 go func() { 438 for { 439 select { 440 case <-sub.Out(): 441 case <-sub.Cancelled(): 442 return 443 } 444 } 445 }() 446 } 447 448 eventType := EventNewBlock 449 450 b.ReportAllocs() 451 b.ResetTimer() 452 for i := 0; i < b.N; i++ { 453 if randEvents { 454 eventType = randEvent() 455 } 456 457 err := eventBus.Publish(eventType, EventDataString("Gamora")) 458 if err != nil { 459 b.Error(err) 460 } 461 } 462 } 463 464 var events = []string{ 465 EventNewBlock, 466 EventNewBlockHeader, 467 EventNewRound, 468 EventNewRoundStep, 469 EventTimeoutPropose, 470 EventCompleteProposal, 471 EventPolka, 472 EventUnlock, 473 EventLock, 474 EventRelock, 475 EventTimeoutWait, 476 EventVote} 477 478 func randEvent() string { 479 return events[tmrand.Intn(len(events))] 480 } 481 482 var queries = []tmpubsub.Query{ 483 EventQueryNewBlock, 484 EventQueryNewBlockHeader, 485 EventQueryNewRound, 486 EventQueryNewRoundStep, 487 EventQueryTimeoutPropose, 488 EventQueryCompleteProposal, 489 EventQueryPolka, 490 EventQueryUnlock, 491 EventQueryLock, 492 EventQueryRelock, 493 EventQueryTimeoutWait, 494 EventQueryVote} 495 496 func randQuery() tmpubsub.Query { 497 return queries[tmrand.Intn(len(queries))] 498 }