github.com/noirx94/tendermintmp@v0.0.1/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/gogo/protobuf/proto" 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 14 db "github.com/tendermint/tm-db" 15 16 abci "github.com/tendermint/tendermint/abci/types" 17 "github.com/tendermint/tendermint/libs/pubsub/query" 18 tmrand "github.com/tendermint/tendermint/libs/rand" 19 "github.com/tendermint/tendermint/state/txindex" 20 "github.com/tendermint/tendermint/types" 21 ) 22 23 func TestTxIndex(t *testing.T) { 24 indexer := NewTxIndex(db.NewMemDB()) 25 26 tx := types.Tx("HELLO WORLD") 27 txResult := &abci.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() 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.True(t, proto.Equal(txResult, loadedTxResult)) 48 49 tx2 := types.Tx("BYE BYE WORLD") 50 txResult2 := &abci.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() 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.True(t, proto.Equal(txResult2, loadedTxResult2)) 67 } 68 69 func TestTxSearch(t *testing.T) { 70 indexer := NewTxIndex(db.NewMemDB()) 71 72 txResult := txResultWithEvents([]abci.Event{ 73 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}}, 74 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("owner"), Value: []byte("Ivan"), Index: true}}}, 75 {Type: "", Attributes: []abci.EventAttribute{{Key: []byte("not_allowed"), Value: []byte("Vlad"), Index: true}}}, 76 }) 77 hash := types.Tx(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 // search using EXISTS 120 {"account.number EXISTS", 1}, 121 // search using EXISTS for non existing key 122 {"account.date EXISTS", 0}, 123 } 124 125 ctx := context.Background() 126 127 for _, tc := range testCases { 128 tc := tc 129 t.Run(tc.q, func(t *testing.T) { 130 results, err := indexer.Search(ctx, query.MustParse(tc.q)) 131 assert.NoError(t, err) 132 133 assert.Len(t, results, tc.resultsLength) 134 if tc.resultsLength > 0 { 135 for _, txr := range results { 136 assert.True(t, proto.Equal(txResult, txr)) 137 } 138 } 139 }) 140 } 141 } 142 143 func TestTxSearchWithCancelation(t *testing.T) { 144 indexer := NewTxIndex(db.NewMemDB()) 145 146 txResult := txResultWithEvents([]abci.Event{ 147 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}}, 148 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("owner"), Value: []byte("Ivan"), Index: true}}}, 149 {Type: "", Attributes: []abci.EventAttribute{{Key: []byte("not_allowed"), Value: []byte("Vlad"), Index: true}}}, 150 }) 151 err := indexer.Index(txResult) 152 require.NoError(t, err) 153 154 ctx, cancel := context.WithCancel(context.Background()) 155 cancel() 156 results, err := indexer.Search(ctx, query.MustParse("account.number = 1")) 157 assert.NoError(t, err) 158 assert.Empty(t, results) 159 } 160 161 func TestTxSearchDeprecatedIndexing(t *testing.T) { 162 indexer := NewTxIndex(db.NewMemDB()) 163 164 // index tx using events indexing (composite key) 165 txResult1 := txResultWithEvents([]abci.Event{ 166 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}}, 167 }) 168 hash1 := types.Tx(txResult1.Tx).Hash() 169 170 err := indexer.Index(txResult1) 171 require.NoError(t, err) 172 173 // index tx also using deprecated indexing (event as key) 174 txResult2 := txResultWithEvents(nil) 175 txResult2.Tx = types.Tx("HELLO WORLD 2") 176 177 hash2 := types.Tx(txResult2.Tx).Hash() 178 b := indexer.store.NewBatch() 179 180 rawBytes, err := proto.Marshal(txResult2) 181 require.NoError(t, err) 182 183 depKey := []byte(fmt.Sprintf("%s/%s/%d/%d", 184 "sender", 185 "addr1", 186 txResult2.Height, 187 txResult2.Index, 188 )) 189 190 err = b.Set(depKey, hash2) 191 require.NoError(t, err) 192 err = b.Set(keyForHeight(txResult2), hash2) 193 require.NoError(t, err) 194 err = b.Set(hash2, rawBytes) 195 require.NoError(t, err) 196 err = b.Write() 197 require.NoError(t, err) 198 199 testCases := []struct { 200 q string 201 results []*abci.TxResult 202 }{ 203 // search by hash 204 {fmt.Sprintf("tx.hash = '%X'", hash1), []*abci.TxResult{txResult1}}, 205 // search by hash 206 {fmt.Sprintf("tx.hash = '%X'", hash2), []*abci.TxResult{txResult2}}, 207 // search by exact match (one key) 208 {"account.number = 1", []*abci.TxResult{txResult1}}, 209 {"account.number >= 1 AND account.number <= 5", []*abci.TxResult{txResult1}}, 210 // search by range (lower bound) 211 {"account.number >= 1", []*abci.TxResult{txResult1}}, 212 // search by range (upper bound) 213 {"account.number <= 5", []*abci.TxResult{txResult1}}, 214 // search using not allowed key 215 {"not_allowed = 'boom'", []*abci.TxResult{}}, 216 // search for not existing tx result 217 {"account.number >= 2 AND account.number <= 5", []*abci.TxResult{}}, 218 // search using not existing key 219 {"account.date >= TIME 2013-05-03T14:45:00Z", []*abci.TxResult{}}, 220 // search by deprecated key 221 {"sender = 'addr1'", []*abci.TxResult{txResult2}}, 222 } 223 224 ctx := context.Background() 225 226 for _, tc := range testCases { 227 tc := tc 228 t.Run(tc.q, func(t *testing.T) { 229 results, err := indexer.Search(ctx, query.MustParse(tc.q)) 230 require.NoError(t, err) 231 for _, txr := range results { 232 for _, tr := range tc.results { 233 assert.True(t, proto.Equal(tr, txr)) 234 } 235 } 236 }) 237 } 238 } 239 240 func TestTxSearchOneTxWithMultipleSameTagsButDifferentValues(t *testing.T) { 241 indexer := NewTxIndex(db.NewMemDB()) 242 243 txResult := txResultWithEvents([]abci.Event{ 244 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}}, 245 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("2"), Index: true}}}, 246 }) 247 248 err := indexer.Index(txResult) 249 require.NoError(t, err) 250 251 ctx := context.Background() 252 253 results, err := indexer.Search(ctx, query.MustParse("account.number >= 1")) 254 assert.NoError(t, err) 255 256 assert.Len(t, results, 1) 257 for _, txr := range results { 258 assert.True(t, proto.Equal(txResult, txr)) 259 } 260 } 261 262 func TestTxSearchMultipleTxs(t *testing.T) { 263 indexer := NewTxIndex(db.NewMemDB()) 264 265 // indexed first, but bigger height (to test the order of transactions) 266 txResult := txResultWithEvents([]abci.Event{ 267 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}}, 268 }) 269 270 txResult.Tx = types.Tx("Bob's account") 271 txResult.Height = 2 272 txResult.Index = 1 273 err := indexer.Index(txResult) 274 require.NoError(t, err) 275 276 // indexed second, but smaller height (to test the order of transactions) 277 txResult2 := txResultWithEvents([]abci.Event{ 278 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("2"), Index: true}}}, 279 }) 280 txResult2.Tx = types.Tx("Alice's account") 281 txResult2.Height = 1 282 txResult2.Index = 2 283 284 err = indexer.Index(txResult2) 285 require.NoError(t, err) 286 287 // indexed third (to test the order of transactions) 288 txResult3 := txResultWithEvents([]abci.Event{ 289 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("3"), Index: true}}}, 290 }) 291 txResult3.Tx = types.Tx("Jack's account") 292 txResult3.Height = 1 293 txResult3.Index = 1 294 err = indexer.Index(txResult3) 295 require.NoError(t, err) 296 297 // indexed fourth (to test we don't include txs with similar events) 298 // https://github.com/tendermint/tendermint/issues/2908 299 txResult4 := txResultWithEvents([]abci.Event{ 300 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number.id"), Value: []byte("1"), Index: true}}}, 301 }) 302 txResult4.Tx = types.Tx("Mike's account") 303 txResult4.Height = 2 304 txResult4.Index = 2 305 err = indexer.Index(txResult4) 306 require.NoError(t, err) 307 308 ctx := context.Background() 309 310 results, err := indexer.Search(ctx, query.MustParse("account.number >= 1")) 311 assert.NoError(t, err) 312 313 require.Len(t, results, 3) 314 } 315 316 func txResultWithEvents(events []abci.Event) *abci.TxResult { 317 tx := types.Tx("HELLO WORLD") 318 return &abci.TxResult{ 319 Height: 1, 320 Index: 0, 321 Tx: tx, 322 Result: abci.ResponseDeliverTx{ 323 Data: []byte{0}, 324 Code: abci.CodeTypeOK, 325 Log: "", 326 Events: events, 327 }, 328 } 329 } 330 331 func benchmarkTxIndex(txsCount int64, b *testing.B) { 332 dir, err := ioutil.TempDir("", "tx_index_db") 333 require.NoError(b, err) 334 defer os.RemoveAll(dir) 335 336 store, err := db.NewDB("tx_index", "goleveldb", dir) 337 require.NoError(b, err) 338 indexer := NewTxIndex(store) 339 340 batch := txindex.NewBatch(txsCount) 341 txIndex := uint32(0) 342 for i := int64(0); i < txsCount; i++ { 343 tx := tmrand.Bytes(250) 344 txResult := &abci.TxResult{ 345 Height: 1, 346 Index: txIndex, 347 Tx: tx, 348 Result: abci.ResponseDeliverTx{ 349 Data: []byte{0}, 350 Code: abci.CodeTypeOK, 351 Log: "", 352 Events: []abci.Event{}, 353 }, 354 } 355 if err := batch.Add(txResult); err != nil { 356 b.Fatal(err) 357 } 358 txIndex++ 359 } 360 361 b.ResetTimer() 362 363 for n := 0; n < b.N; n++ { 364 err = indexer.AddBatch(batch) 365 } 366 if err != nil { 367 b.Fatal(err) 368 } 369 } 370 371 func BenchmarkTxIndex1(b *testing.B) { benchmarkTxIndex(1, b) } 372 func BenchmarkTxIndex500(b *testing.B) { benchmarkTxIndex(500, b) } 373 func BenchmarkTxIndex1000(b *testing.B) { benchmarkTxIndex(1000, b) } 374 func BenchmarkTxIndex2000(b *testing.B) { benchmarkTxIndex(2000, b) } 375 func BenchmarkTxIndex10000(b *testing.B) { benchmarkTxIndex(10000, b) }