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) }