github.com/Oyster-zx/tendermint@v0.34.24-fork/state/txindex/kv/kv_test.go (about)

     1  package kv
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"testing"
     8  
     9  	"github.com/gogo/protobuf/proto"
    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/tendermint/tendermint/abci/types"
    16  	"github.com/tendermint/tendermint/libs/pubsub/query"
    17  	tmrand "github.com/tendermint/tendermint/libs/rand"
    18  	"github.com/tendermint/tendermint/state/txindex"
    19  	"github.com/tendermint/tendermint/types"
    20  )
    21  
    22  func TestTxIndex(t *testing.T) {
    23  	indexer := NewTxIndex(db.NewMemDB())
    24  
    25  	tx := types.Tx("HELLO WORLD")
    26  	txResult := &abci.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.True(t, proto.Equal(txResult, loadedTxResult))
    47  
    48  	tx2 := types.Tx("BYE BYE WORLD")
    49  	txResult2 := &abci.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.True(t, proto.Equal(txResult2, loadedTxResult2))
    66  }
    67  
    68  func TestTxSearch(t *testing.T) {
    69  	indexer := NewTxIndex(db.NewMemDB())
    70  
    71  	txResult := txResultWithEvents([]abci.Event{
    72  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}},
    73  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("owner"), Value: []byte("Ivan"), Index: true}}},
    74  		{Type: "", Attributes: []abci.EventAttribute{{Key: []byte("not_allowed"), Value: []byte("Vlad"), Index: true}}},
    75  	})
    76  	hash := types.Tx(txResult.Tx).Hash()
    77  
    78  	err := indexer.Index(txResult)
    79  	require.NoError(t, err)
    80  
    81  	testCases := []struct {
    82  		q             string
    83  		resultsLength int
    84  	}{
    85  		// search by hash
    86  		{fmt.Sprintf("tx.hash = '%X'", hash), 1},
    87  		// search by exact match (one key)
    88  		{"account.number = 1", 1},
    89  		// search by exact match (two keys)
    90  		{"account.number = 1 AND account.owner = 'Ivan'", 1},
    91  		// search by exact match (two keys)
    92  		{"account.number = 1 AND account.owner = 'Vlad'", 0},
    93  		{"account.owner = 'Vlad' AND account.number = 1", 0},
    94  		{"account.number >= 1 AND account.owner = 'Vlad'", 0},
    95  		{"account.owner = 'Vlad' AND account.number >= 1", 0},
    96  		{"account.number <= 0", 0},
    97  		{"account.number <= 0 AND account.owner = 'Ivan'", 0},
    98  		// search using a prefix of the stored value
    99  		{"account.owner = 'Iv'", 0},
   100  		// search by range
   101  		{"account.number >= 1 AND account.number <= 5", 1},
   102  		// search by range (lower bound)
   103  		{"account.number >= 1", 1},
   104  		// search by range (upper bound)
   105  		{"account.number <= 5", 1},
   106  		// search using not allowed key
   107  		{"not_allowed = 'boom'", 0},
   108  		// search for not existing tx result
   109  		{"account.number >= 2 AND account.number <= 5", 0},
   110  		// search using not existing key
   111  		{"account.date >= TIME 2013-05-03T14:45:00Z", 0},
   112  		// search using CONTAINS
   113  		{"account.owner CONTAINS 'an'", 1},
   114  		// search for non existing value using CONTAINS
   115  		{"account.owner CONTAINS 'Vlad'", 0},
   116  		// search using the wrong key (of numeric type) using CONTAINS
   117  		{"account.number CONTAINS 'Iv'", 0},
   118  		// search using EXISTS
   119  		{"account.number EXISTS", 1},
   120  		// search using EXISTS for non existing key
   121  		{"account.date EXISTS", 0},
   122  	}
   123  
   124  	ctx := context.Background()
   125  
   126  	for _, tc := range testCases {
   127  		tc := tc
   128  		t.Run(tc.q, func(t *testing.T) {
   129  			results, err := indexer.Search(ctx, query.MustParse(tc.q))
   130  			assert.NoError(t, err)
   131  
   132  			assert.Len(t, results, tc.resultsLength)
   133  			if tc.resultsLength > 0 {
   134  				for _, txr := range results {
   135  					assert.True(t, proto.Equal(txResult, txr))
   136  				}
   137  			}
   138  		})
   139  	}
   140  }
   141  
   142  func TestTxSearchWithCancelation(t *testing.T) {
   143  	indexer := NewTxIndex(db.NewMemDB())
   144  
   145  	txResult := txResultWithEvents([]abci.Event{
   146  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}},
   147  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("owner"), Value: []byte("Ivan"), Index: true}}},
   148  		{Type: "", Attributes: []abci.EventAttribute{{Key: []byte("not_allowed"), Value: []byte("Vlad"), Index: true}}},
   149  	})
   150  	err := indexer.Index(txResult)
   151  	require.NoError(t, err)
   152  
   153  	ctx, cancel := context.WithCancel(context.Background())
   154  	cancel()
   155  	results, err := indexer.Search(ctx, query.MustParse("account.number = 1"))
   156  	assert.NoError(t, err)
   157  	assert.Empty(t, results)
   158  }
   159  
   160  func TestTxSearchDeprecatedIndexing(t *testing.T) {
   161  	indexer := NewTxIndex(db.NewMemDB())
   162  
   163  	// index tx using events indexing (composite key)
   164  	txResult1 := txResultWithEvents([]abci.Event{
   165  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}},
   166  	})
   167  	hash1 := types.Tx(txResult1.Tx).Hash()
   168  
   169  	err := indexer.Index(txResult1)
   170  	require.NoError(t, err)
   171  
   172  	// index tx also using deprecated indexing (event as key)
   173  	txResult2 := txResultWithEvents(nil)
   174  	txResult2.Tx = types.Tx("HELLO WORLD 2")
   175  
   176  	hash2 := types.Tx(txResult2.Tx).Hash()
   177  	b := indexer.store.NewBatch()
   178  
   179  	rawBytes, err := proto.Marshal(txResult2)
   180  	require.NoError(t, err)
   181  
   182  	depKey := []byte(fmt.Sprintf("%s/%s/%d/%d",
   183  		"sender",
   184  		"addr1",
   185  		txResult2.Height,
   186  		txResult2.Index,
   187  	))
   188  
   189  	err = b.Set(depKey, hash2)
   190  	require.NoError(t, err)
   191  	err = b.Set(keyForHeight(txResult2), hash2)
   192  	require.NoError(t, err)
   193  	err = b.Set(hash2, rawBytes)
   194  	require.NoError(t, err)
   195  	err = b.Write()
   196  	require.NoError(t, err)
   197  
   198  	testCases := []struct {
   199  		q       string
   200  		results []*abci.TxResult
   201  	}{
   202  		// search by hash
   203  		{fmt.Sprintf("tx.hash = '%X'", hash1), []*abci.TxResult{txResult1}},
   204  		// search by hash
   205  		{fmt.Sprintf("tx.hash = '%X'", hash2), []*abci.TxResult{txResult2}},
   206  		// search by exact match (one key)
   207  		{"account.number = 1", []*abci.TxResult{txResult1}},
   208  		{"account.number >= 1 AND account.number <= 5", []*abci.TxResult{txResult1}},
   209  		// search by range (lower bound)
   210  		{"account.number >= 1", []*abci.TxResult{txResult1}},
   211  		// search by range (upper bound)
   212  		{"account.number <= 5", []*abci.TxResult{txResult1}},
   213  		// search using not allowed key
   214  		{"not_allowed = 'boom'", []*abci.TxResult{}},
   215  		// search for not existing tx result
   216  		{"account.number >= 2 AND account.number <= 5", []*abci.TxResult{}},
   217  		// search using not existing key
   218  		{"account.date >= TIME 2013-05-03T14:45:00Z", []*abci.TxResult{}},
   219  		// search by deprecated key
   220  		{"sender = 'addr1'", []*abci.TxResult{txResult2}},
   221  	}
   222  
   223  	ctx := context.Background()
   224  
   225  	for _, tc := range testCases {
   226  		tc := tc
   227  		t.Run(tc.q, func(t *testing.T) {
   228  			results, err := indexer.Search(ctx, query.MustParse(tc.q))
   229  			require.NoError(t, err)
   230  			for _, txr := range results {
   231  				for _, tr := range tc.results {
   232  					assert.True(t, proto.Equal(tr, txr))
   233  				}
   234  			}
   235  		})
   236  	}
   237  }
   238  
   239  func TestTxSearchOneTxWithMultipleSameTagsButDifferentValues(t *testing.T) {
   240  	indexer := NewTxIndex(db.NewMemDB())
   241  
   242  	txResult := txResultWithEvents([]abci.Event{
   243  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}},
   244  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("2"), Index: true}}},
   245  	})
   246  
   247  	err := indexer.Index(txResult)
   248  	require.NoError(t, err)
   249  
   250  	ctx := context.Background()
   251  
   252  	results, err := indexer.Search(ctx, query.MustParse("account.number >= 1"))
   253  	assert.NoError(t, err)
   254  
   255  	assert.Len(t, results, 1)
   256  	for _, txr := range results {
   257  		assert.True(t, proto.Equal(txResult, txr))
   258  	}
   259  }
   260  
   261  func TestTxIndexDuplicatePreviouslySuccessful(t *testing.T) {
   262  	mockTx := types.Tx("MOCK_TX_HASH")
   263  
   264  	testCases := []struct {
   265  		name         string
   266  		tx1          *abci.TxResult
   267  		tx2          *abci.TxResult
   268  		expOverwrite bool // do we expect the second tx to overwrite the first tx
   269  	}{
   270  		{
   271  			"don't overwrite as a non-zero code was returned and the previous tx was successful",
   272  			&abci.TxResult{
   273  				Height: 1,
   274  				Index:  0,
   275  				Tx:     mockTx,
   276  				Result: abci.ResponseDeliverTx{
   277  					Code: abci.CodeTypeOK,
   278  				},
   279  			},
   280  			&abci.TxResult{
   281  				Height: 2,
   282  				Index:  0,
   283  				Tx:     mockTx,
   284  				Result: abci.ResponseDeliverTx{
   285  					Code: abci.CodeTypeOK + 1,
   286  				},
   287  			},
   288  			false,
   289  		},
   290  		{
   291  			"overwrite as the previous tx was also unsuccessful",
   292  			&abci.TxResult{
   293  				Height: 1,
   294  				Index:  0,
   295  				Tx:     mockTx,
   296  				Result: abci.ResponseDeliverTx{
   297  					Code: abci.CodeTypeOK + 1,
   298  				},
   299  			},
   300  			&abci.TxResult{
   301  				Height: 2,
   302  				Index:  0,
   303  				Tx:     mockTx,
   304  				Result: abci.ResponseDeliverTx{
   305  					Code: abci.CodeTypeOK + 1,
   306  				},
   307  			},
   308  			true,
   309  		},
   310  		{
   311  			"overwrite as the most recent tx was successful",
   312  			&abci.TxResult{
   313  				Height: 1,
   314  				Index:  0,
   315  				Tx:     mockTx,
   316  				Result: abci.ResponseDeliverTx{
   317  					Code: abci.CodeTypeOK,
   318  				},
   319  			},
   320  			&abci.TxResult{
   321  				Height: 2,
   322  				Index:  0,
   323  				Tx:     mockTx,
   324  				Result: abci.ResponseDeliverTx{
   325  					Code: abci.CodeTypeOK,
   326  				},
   327  			},
   328  			true,
   329  		},
   330  	}
   331  
   332  	hash := mockTx.Hash()
   333  
   334  	for _, tc := range testCases {
   335  		t.Run(tc.name, func(t *testing.T) {
   336  			indexer := NewTxIndex(db.NewMemDB())
   337  
   338  			// index the first tx
   339  			err := indexer.Index(tc.tx1)
   340  			require.NoError(t, err)
   341  
   342  			// index the same tx with different results
   343  			err = indexer.Index(tc.tx2)
   344  			require.NoError(t, err)
   345  
   346  			res, err := indexer.Get(hash)
   347  			require.NoError(t, err)
   348  
   349  			if tc.expOverwrite {
   350  				require.Equal(t, tc.tx2, res)
   351  			} else {
   352  				require.Equal(t, tc.tx1, res)
   353  			}
   354  		})
   355  	}
   356  }
   357  
   358  func TestTxSearchMultipleTxs(t *testing.T) {
   359  	indexer := NewTxIndex(db.NewMemDB())
   360  
   361  	// indexed first, but bigger height (to test the order of transactions)
   362  	txResult := txResultWithEvents([]abci.Event{
   363  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}},
   364  	})
   365  
   366  	txResult.Tx = types.Tx("Bob's account")
   367  	txResult.Height = 2
   368  	txResult.Index = 1
   369  	err := indexer.Index(txResult)
   370  	require.NoError(t, err)
   371  
   372  	// indexed second, but smaller height (to test the order of transactions)
   373  	txResult2 := txResultWithEvents([]abci.Event{
   374  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("2"), Index: true}}},
   375  	})
   376  	txResult2.Tx = types.Tx("Alice's account")
   377  	txResult2.Height = 1
   378  	txResult2.Index = 2
   379  
   380  	err = indexer.Index(txResult2)
   381  	require.NoError(t, err)
   382  
   383  	// indexed third (to test the order of transactions)
   384  	txResult3 := txResultWithEvents([]abci.Event{
   385  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("3"), Index: true}}},
   386  	})
   387  	txResult3.Tx = types.Tx("Jack's account")
   388  	txResult3.Height = 1
   389  	txResult3.Index = 1
   390  	err = indexer.Index(txResult3)
   391  	require.NoError(t, err)
   392  
   393  	// indexed fourth (to test we don't include txs with similar events)
   394  	// https://github.com/tendermint/tendermint/issues/2908
   395  	txResult4 := txResultWithEvents([]abci.Event{
   396  		{Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number.id"), Value: []byte("1"), Index: true}}},
   397  	})
   398  	txResult4.Tx = types.Tx("Mike's account")
   399  	txResult4.Height = 2
   400  	txResult4.Index = 2
   401  	err = indexer.Index(txResult4)
   402  	require.NoError(t, err)
   403  
   404  	ctx := context.Background()
   405  
   406  	results, err := indexer.Search(ctx, query.MustParse("account.number >= 1"))
   407  	assert.NoError(t, err)
   408  
   409  	require.Len(t, results, 3)
   410  }
   411  
   412  func txResultWithEvents(events []abci.Event) *abci.TxResult {
   413  	tx := types.Tx("HELLO WORLD")
   414  	return &abci.TxResult{
   415  		Height: 1,
   416  		Index:  0,
   417  		Tx:     tx,
   418  		Result: abci.ResponseDeliverTx{
   419  			Data:   []byte{0},
   420  			Code:   abci.CodeTypeOK,
   421  			Log:    "",
   422  			Events: events,
   423  		},
   424  	}
   425  }
   426  
   427  func benchmarkTxIndex(txsCount int64, b *testing.B) {
   428  	dir, err := os.MkdirTemp("", "tx_index_db")
   429  	require.NoError(b, err)
   430  	defer os.RemoveAll(dir)
   431  
   432  	store, err := db.NewDB("tx_index", "goleveldb", dir)
   433  	require.NoError(b, err)
   434  	indexer := NewTxIndex(store)
   435  
   436  	batch := txindex.NewBatch(txsCount)
   437  	txIndex := uint32(0)
   438  	for i := int64(0); i < txsCount; i++ {
   439  		tx := tmrand.Bytes(250)
   440  		txResult := &abci.TxResult{
   441  			Height: 1,
   442  			Index:  txIndex,
   443  			Tx:     tx,
   444  			Result: abci.ResponseDeliverTx{
   445  				Data:   []byte{0},
   446  				Code:   abci.CodeTypeOK,
   447  				Log:    "",
   448  				Events: []abci.Event{},
   449  			},
   450  		}
   451  		if err := batch.Add(txResult); err != nil {
   452  			b.Fatal(err)
   453  		}
   454  		txIndex++
   455  	}
   456  
   457  	b.ResetTimer()
   458  
   459  	for n := 0; n < b.N; n++ {
   460  		err = indexer.AddBatch(batch)
   461  	}
   462  	if err != nil {
   463  		b.Fatal(err)
   464  	}
   465  }
   466  
   467  func BenchmarkTxIndex1(b *testing.B)     { benchmarkTxIndex(1, b) }
   468  func BenchmarkTxIndex500(b *testing.B)   { benchmarkTxIndex(500, b) }
   469  func BenchmarkTxIndex1000(b *testing.B)  { benchmarkTxIndex(1000, b) }
   470  func BenchmarkTxIndex2000(b *testing.B)  { benchmarkTxIndex(2000, b) }
   471  func BenchmarkTxIndex10000(b *testing.B) { benchmarkTxIndex(10000, b) }