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

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