github.com/okex/exchain@v1.8.0/libs/tendermint/state/txindex/kv/kv_test.go (about)

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