github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/internal/state/indexer/sink/kv/kv_test.go (about)

     1  package kv
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	dbm "github.com/tendermint/tm-db"
     9  
    10  	"github.com/gogo/protobuf/proto"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	abci "github.com/ari-anchor/sei-tendermint/abci/types"
    15  	"github.com/ari-anchor/sei-tendermint/internal/pubsub/query"
    16  	"github.com/ari-anchor/sei-tendermint/internal/state/indexer"
    17  	kvtx "github.com/ari-anchor/sei-tendermint/internal/state/indexer/tx/kv"
    18  	"github.com/ari-anchor/sei-tendermint/types"
    19  )
    20  
    21  func TestType(t *testing.T) {
    22  	kvSink := NewEventSink(dbm.NewMemDB())
    23  	assert.Equal(t, indexer.KV, kvSink.Type())
    24  }
    25  
    26  func TestStop(t *testing.T) {
    27  	kvSink := NewEventSink(dbm.NewMemDB())
    28  	assert.Nil(t, kvSink.Stop())
    29  }
    30  
    31  func TestBlockFuncs(t *testing.T) {
    32  	store := dbm.NewPrefixDB(dbm.NewMemDB(), []byte("block_events"))
    33  	indexer := NewEventSink(store)
    34  
    35  	require.NoError(t, indexer.IndexBlockEvents(types.EventDataNewBlockHeader{
    36  		Header: types.Header{Height: 1},
    37  		ResultFinalizeBlock: abci.ResponseFinalizeBlock{
    38  			Events: []abci.Event{
    39  				{
    40  					Type: "finalize_eventA",
    41  					Attributes: []abci.EventAttribute{
    42  						{
    43  							Key:   []byte("proposer"),
    44  							Value: []byte("FCAA001"),
    45  							Index: true,
    46  						},
    47  					},
    48  				},
    49  				{
    50  					Type: "finalize_eventB",
    51  					Attributes: []abci.EventAttribute{
    52  						{
    53  							Key:   []byte("foo"),
    54  							Value: []byte("100"),
    55  							Index: true,
    56  						},
    57  					},
    58  				},
    59  			},
    60  		},
    61  	}))
    62  
    63  	b, e := indexer.HasBlock(1)
    64  	assert.Nil(t, e)
    65  	assert.True(t, b)
    66  
    67  	for i := 2; i < 12; i++ {
    68  		var index bool
    69  		if i%2 == 0 {
    70  			index = true
    71  		}
    72  
    73  		require.NoError(t, indexer.IndexBlockEvents(types.EventDataNewBlockHeader{
    74  			Header: types.Header{Height: int64(i)},
    75  			ResultFinalizeBlock: abci.ResponseFinalizeBlock{
    76  				Events: []abci.Event{
    77  					{
    78  						Type: "finalize_eventA",
    79  						Attributes: []abci.EventAttribute{
    80  							{
    81  								Key:   []byte("proposer"),
    82  								Value: []byte("FCAA001"),
    83  								Index: true,
    84  							},
    85  						},
    86  					},
    87  					{
    88  						Type: "finalize_eventB",
    89  						Attributes: []abci.EventAttribute{
    90  							{
    91  								Key:   []byte("foo"),
    92  								Value: []byte(fmt.Sprintf("%d", i)),
    93  								Index: index,
    94  							},
    95  						},
    96  					},
    97  				},
    98  			},
    99  		}))
   100  	}
   101  
   102  	testCases := map[string]struct {
   103  		q       *query.Query
   104  		results []int64
   105  	}{
   106  		"block.height = 100": {
   107  			q:       query.MustCompile(`block.height = 100`),
   108  			results: []int64{},
   109  		},
   110  		"block.height = 5": {
   111  			q:       query.MustCompile(`block.height = 5`),
   112  			results: []int64{5},
   113  		},
   114  		"finalize_eventA.key1 = 'value1'": {
   115  			q:       query.MustCompile(`finalize_eventA.key1 = 'value1'`),
   116  			results: []int64{},
   117  		},
   118  		"finalize_eventA.proposer = 'FCAA001'": {
   119  			q:       query.MustCompile(`finalize_eventA.proposer = 'FCAA001'`),
   120  			results: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
   121  		},
   122  		"finalize_eventB.foo <= 5": {
   123  			q:       query.MustCompile(`finalize_eventB.foo <= 5`),
   124  			results: []int64{2, 4},
   125  		},
   126  		"finalize_eventB.foo >= 100": {
   127  			q:       query.MustCompile(`finalize_eventB.foo >= 100`),
   128  			results: []int64{1},
   129  		},
   130  		"block.height > 2 AND finalize_eventB.foo <= 8": {
   131  			q:       query.MustCompile(`block.height > 2 AND finalize_eventB.foo <= 8`),
   132  			results: []int64{4, 6, 8},
   133  		},
   134  		"finalize_eventA.proposer CONTAINS 'FFFFFFF'": {
   135  			q:       query.MustCompile(`finalize_eventA.proposer CONTAINS 'FFFFFFF'`),
   136  			results: []int64{},
   137  		},
   138  		"finalize_eventA.proposer CONTAINS 'FCAA001'": {
   139  			q:       query.MustCompile(`finalize_eventA.proposer CONTAINS 'FCAA001'`),
   140  			results: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
   141  		},
   142  	}
   143  
   144  	for name, tc := range testCases {
   145  		tc := tc
   146  		t.Run(name, func(t *testing.T) {
   147  			ctx, cancel := context.WithCancel(context.Background())
   148  			defer cancel()
   149  
   150  			results, err := indexer.SearchBlockEvents(ctx, tc.q)
   151  			require.NoError(t, err)
   152  			require.Equal(t, tc.results, results)
   153  		})
   154  	}
   155  }
   156  
   157  func TestTxSearchWithCancelation(t *testing.T) {
   158  	indexer := NewEventSink(dbm.NewMemDB())
   159  
   160  	txResult := txResultWithEvents([]abci.Event{
   161  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}},
   162  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("owner"), Value: []byte("Ivan"), Index: true}}},
   163  		{Type: "", Attributes: []abci.EventAttribute{{Key: []byte("not_allowed"), Value: []byte("Vlad"), Index: true}}},
   164  	})
   165  	err := indexer.IndexTxEvents([]*abci.TxResult{txResult})
   166  	require.NoError(t, err)
   167  
   168  	r, e := indexer.GetTxByHash(types.Tx("HELLO WORLD").Hash())
   169  	assert.Nil(t, e)
   170  	assert.Equal(t, r, txResult)
   171  
   172  	ctx, cancel := context.WithCancel(context.Background())
   173  	cancel()
   174  	results, err := indexer.SearchTxEvents(ctx, query.MustCompile(`account.number = 1`))
   175  	assert.NoError(t, err)
   176  	assert.Empty(t, results)
   177  }
   178  
   179  func TestTxSearchDeprecatedIndexing(t *testing.T) {
   180  	esdb := dbm.NewMemDB()
   181  	indexer := NewEventSink(esdb)
   182  
   183  	// index tx using events indexing (composite key)
   184  	txResult1 := txResultWithEvents([]abci.Event{
   185  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}},
   186  	})
   187  	hash1 := types.Tx(txResult1.Tx).Hash()
   188  
   189  	err := indexer.IndexTxEvents([]*abci.TxResult{txResult1})
   190  	require.NoError(t, err)
   191  
   192  	// index tx also using deprecated indexing (event as key)
   193  	txResult2 := txResultWithEvents(nil)
   194  	txResult2.Tx = types.Tx("HELLO WORLD 2")
   195  
   196  	hash2 := types.Tx(txResult2.Tx).Hash()
   197  	b := esdb.NewBatch()
   198  
   199  	rawBytes, err := proto.Marshal(txResult2)
   200  	require.NoError(t, err)
   201  
   202  	depKey := []byte(fmt.Sprintf("%s/%s/%d/%d",
   203  		"sender",
   204  		"addr1",
   205  		txResult2.Height,
   206  		txResult2.Index,
   207  	))
   208  
   209  	err = b.Set(depKey, hash2)
   210  	require.NoError(t, err)
   211  	err = b.Set(kvtx.KeyFromHeight(txResult2), hash2)
   212  	require.NoError(t, err)
   213  	err = b.Set(hash2, rawBytes)
   214  	require.NoError(t, err)
   215  	err = b.Write()
   216  	require.NoError(t, err)
   217  
   218  	testCases := []struct {
   219  		q       string
   220  		results []*abci.TxResult
   221  	}{
   222  		// search by hash
   223  		{fmt.Sprintf("tx.hash = '%X'", hash1), []*abci.TxResult{txResult1}},
   224  		// search by hash
   225  		{fmt.Sprintf("tx.hash = '%X'", hash2), []*abci.TxResult{txResult2}},
   226  		// search by exact match (one key)
   227  		{"account.number = 1", []*abci.TxResult{txResult1}},
   228  		{"account.number >= 1 AND account.number <= 5", []*abci.TxResult{txResult1}},
   229  		// search by range (lower bound)
   230  		{"account.number >= 1", []*abci.TxResult{txResult1}},
   231  		// search by range (upper bound)
   232  		{"account.number <= 5", []*abci.TxResult{txResult1}},
   233  		// search using not allowed key
   234  		{"not_allowed = 'boom'", []*abci.TxResult{}},
   235  		// search for not existing tx result
   236  		{"account.number >= 2 AND account.number <= 5", []*abci.TxResult{}},
   237  		// search using not existing key
   238  		{"account.date >= TIME 2013-05-03T14:45:00Z", []*abci.TxResult{}},
   239  		// search by deprecated key
   240  		{"sender = 'addr1'", []*abci.TxResult{txResult2}},
   241  	}
   242  
   243  	ctx := context.Background()
   244  
   245  	for _, tc := range testCases {
   246  		tc := tc
   247  		t.Run(tc.q, func(t *testing.T) {
   248  			results, err := indexer.SearchTxEvents(ctx, query.MustCompile(tc.q))
   249  			require.NoError(t, err)
   250  			for _, txr := range results {
   251  				for _, tr := range tc.results {
   252  					assert.True(t, proto.Equal(tr, txr))
   253  				}
   254  			}
   255  		})
   256  	}
   257  }
   258  
   259  func TestTxSearchOneTxWithMultipleSameTagsButDifferentValues(t *testing.T) {
   260  	indexer := NewEventSink(dbm.NewMemDB())
   261  
   262  	txResult := txResultWithEvents([]abci.Event{
   263  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}},
   264  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("2"), Index: true}}},
   265  	})
   266  
   267  	err := indexer.IndexTxEvents([]*abci.TxResult{txResult})
   268  	require.NoError(t, err)
   269  
   270  	ctx := context.Background()
   271  
   272  	results, err := indexer.SearchTxEvents(ctx, query.MustCompile(`account.number >= 1`))
   273  	assert.NoError(t, err)
   274  
   275  	assert.Len(t, results, 1)
   276  	for _, txr := range results {
   277  		assert.True(t, proto.Equal(txResult, txr))
   278  	}
   279  }
   280  
   281  func TestTxSearchMultipleTxs(t *testing.T) {
   282  	indexer := NewEventSink(dbm.NewMemDB())
   283  
   284  	// indexed first, but bigger height (to test the order of transactions)
   285  	txResult := txResultWithEvents([]abci.Event{
   286  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}},
   287  	})
   288  
   289  	txResult.Tx = types.Tx("Bob's account")
   290  	txResult.Height = 2
   291  	txResult.Index = 1
   292  	err := indexer.IndexTxEvents([]*abci.TxResult{txResult})
   293  	require.NoError(t, err)
   294  
   295  	// indexed second, but smaller height (to test the order of transactions)
   296  	txResult2 := txResultWithEvents([]abci.Event{
   297  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("2"), Index: true}}},
   298  	})
   299  	txResult2.Tx = types.Tx("Alice's account")
   300  	txResult2.Height = 1
   301  	txResult2.Index = 2
   302  
   303  	err = indexer.IndexTxEvents([]*abci.TxResult{txResult2})
   304  	require.NoError(t, err)
   305  
   306  	// indexed third (to test the order of transactions)
   307  	txResult3 := txResultWithEvents([]abci.Event{
   308  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("3"), Index: true}}},
   309  	})
   310  	txResult3.Tx = types.Tx("Jack's account")
   311  	txResult3.Height = 1
   312  	txResult3.Index = 1
   313  	err = indexer.IndexTxEvents([]*abci.TxResult{txResult3})
   314  	require.NoError(t, err)
   315  
   316  	// indexed fourth (to test we don't include txs with similar events)
   317  	// https://github.com/ari-anchor/sei-tendermint/issues/2908
   318  	txResult4 := txResultWithEvents([]abci.Event{
   319  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number.id"), Value: []byte("1"), Index: true}}},
   320  	})
   321  	txResult4.Tx = types.Tx("Mike's account")
   322  	txResult4.Height = 2
   323  	txResult4.Index = 2
   324  	err = indexer.IndexTxEvents([]*abci.TxResult{txResult4})
   325  	require.NoError(t, err)
   326  
   327  	ctx := context.Background()
   328  
   329  	results, err := indexer.SearchTxEvents(ctx, query.MustCompile(`account.number >= 1`))
   330  	assert.NoError(t, err)
   331  
   332  	require.Len(t, results, 3)
   333  }
   334  
   335  func txResultWithEvents(events []abci.Event) *abci.TxResult {
   336  	tx := types.Tx("HELLO WORLD")
   337  	return &abci.TxResult{
   338  		Height: 1,
   339  		Index:  0,
   340  		Tx:     tx,
   341  		Result: abci.ExecTxResult{
   342  			Data:   []byte{0},
   343  			Code:   abci.CodeTypeOK,
   344  			Log:    "",
   345  			Events: events,
   346  		},
   347  	}
   348  }