github.com/DFWallet/tendermint-cosmos@v0.0.2/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/DFWallet/tendermint-cosmos/abci/types"
    14  	tmpubsub "github.com/DFWallet/tendermint-cosmos/libs/pubsub"
    15  	tmquery "github.com/DFWallet/tendermint-cosmos/libs/pubsub/query"
    16  	tmrand "github.com/DFWallet/tendermint-cosmos/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  }