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  }