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