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  }