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