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