github.com/badrootd/celestia-core@v0.0.0-20240305091328-aa4207a4b25d/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/cometbft/cometbft-db" 14 15 abci "github.com/badrootd/celestia-core/abci/types" 16 "github.com/badrootd/celestia-core/libs/pubsub/query" 17 cmtrand "github.com/badrootd/celestia-core/libs/rand" 18 "github.com/badrootd/celestia-core/state/txindex" 19 "github.com/badrootd/celestia-core/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 TestWrappedTxIndex(t *testing.T) { 69 indexer := NewTxIndex(db.NewMemDB()) 70 71 tx := types.Tx("HELLO WORLD") 72 wrappedTx, err := types.MarshalIndexWrapper(tx, 11) 73 require.NoError(t, err) 74 txResult := &abci.TxResult{ 75 Height: 1, 76 Index: 0, 77 Tx: wrappedTx, 78 Result: abci.ResponseDeliverTx{ 79 Data: []byte{0}, 80 Code: abci.CodeTypeOK, Log: "", Events: nil, 81 }, 82 } 83 hash := tx.Hash() 84 85 batch := txindex.NewBatch(1) 86 if err := batch.Add(txResult); err != nil { 87 t.Error(err) 88 } 89 err = indexer.AddBatch(batch) 90 require.NoError(t, err) 91 92 loadedTxResult, err := indexer.Get(hash) 93 require.NoError(t, err) 94 assert.True(t, proto.Equal(txResult, loadedTxResult)) 95 } 96 97 func TestTxSearch(t *testing.T) { 98 indexer := NewTxIndex(db.NewMemDB()) 99 100 txResult := txResultWithEvents([]abci.Event{ 101 {Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "1", Index: true}}}, 102 {Type: "account", Attributes: []abci.EventAttribute{{Key: "owner", Value: "Ivan", Index: true}}}, 103 {Type: "account", Attributes: []abci.EventAttribute{{Key: "owner", Value: "/Ivan/", Index: true}, {Key: "number", Value: "10", Index: true}}}, 104 {Type: "", Attributes: []abci.EventAttribute{{Key: "not_allowed", Value: "Vlad", Index: true}}}, 105 }) 106 hash := types.Tx(txResult.Tx).Hash() 107 108 err := indexer.Index(txResult) 109 require.NoError(t, err) 110 111 testCases := []struct { 112 q string 113 resultsLength int 114 }{ 115 // search by hash 116 {fmt.Sprintf("tx.hash = '%X'", hash), 1}, 117 // search by hash (lower) 118 {fmt.Sprintf("tx.hash = '%x'", hash), 1}, 119 // search by exact match (one key) 120 {"account.number = 1", 1}, 121 // search by exact match (two keys) 122 {"account.number = 1 AND account.owner = 'Ivan'", 1}, 123 {"account.owner = 'Ivan' AND account.number = 1", 1}, 124 // search by exact match (two keys) 125 {"account.number = 1 AND account.owner = 'Vlad'", 0}, 126 {"account.owner = 'Vlad' AND account.number = 1", 0}, 127 {"account.number >= 1 AND account.owner = 'Vlad'", 0}, 128 {"account.owner = 'Vlad' AND account.number >= 1", 0}, 129 {"account.number <= 0", 0}, 130 {"account.number <= 0 AND account.owner = 'Ivan'", 0}, 131 {"account.number < 10000 AND account.owner = 'Ivan'", 1}, 132 // search using a prefix of the stored value 133 {"account.owner = 'Iv'", 0}, 134 // search for owner with slash in name 135 {"account.owner = '/Ivan/'", 1}, 136 // search for owner with slash in name and match events 137 {"match.events = 1 AND account.owner = '/Ivan/' AND account.number = 10", 1}, 138 // search for owner with slash in name and match events with no match 139 {"match.events = 1 AND account.owner = '/Ivan/' AND account.number = 1", 0}, 140 // search for owner with slash in name with CONTAINS 141 {"account.owner CONTAINS 'an'", 1}, 142 // search for owner with slash in name with CONTAINS and match events 143 {"match.events = 1 AND account.owner CONTAINS 'an'", 1}, 144 // search by range 145 {"account.number >= 1 AND account.number <= 5", 1}, 146 // search by range and another key 147 {"account.number >= 1 AND account.owner = 'Ivan' AND account.number <= 5", 1}, 148 // search by range (lower bound) 149 {"account.number >= 1", 1}, 150 // search by range (upper bound) 151 {"account.number <= 5", 1}, 152 {"account.number <= 1", 1}, 153 // search using not allowed key 154 {"not_allowed = 'boom'", 0}, 155 {"not_allowed = 'Vlad'", 0}, 156 // search for not existing tx result 157 {"account.number >= 2 AND account.number <= 5", 0}, 158 // search using not existing key 159 {"account.date >= TIME 2013-05-03T14:45:00Z", 0}, 160 // search using CONTAINS 161 {"account.owner CONTAINS 'an'", 1}, 162 // search for non existing value using CONTAINS 163 {"account.owner CONTAINS 'Vlad'", 0}, 164 {"account.owner CONTAINS 'Ivann'", 0}, 165 {"account.owner CONTAINS 'IIvan'", 0}, 166 {"account.owner CONTAINS 'Iva n'", 0}, 167 {"account.owner CONTAINS ' Ivan'", 0}, 168 {"account.owner CONTAINS 'Ivan '", 0}, 169 // search using the wrong key (of numeric type) using CONTAINS 170 {"account.number CONTAINS 'Iv'", 0}, 171 // search using EXISTS 172 {"account.number EXISTS", 1}, 173 // search using EXISTS for non existing key 174 {"account.date EXISTS", 0}, 175 {"not_allowed EXISTS", 0}, 176 } 177 178 ctx := context.Background() 179 180 for _, tc := range testCases { 181 tc := tc 182 t.Run(tc.q, func(t *testing.T) { 183 results, err := indexer.Search(ctx, query.MustParse(tc.q)) 184 assert.NoError(t, err) 185 186 assert.Len(t, results, tc.resultsLength) 187 if tc.resultsLength > 0 { 188 for _, txr := range results { 189 assert.True(t, proto.Equal(txResult, txr)) 190 } 191 } 192 }) 193 } 194 } 195 196 func TestTxSearchEventMatch(t *testing.T) { 197 198 indexer := NewTxIndex(db.NewMemDB()) 199 200 txResult := txResultWithEvents([]abci.Event{ 201 {Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "1", Index: true}, {Key: "owner", Value: "Ana", Index: true}}}, 202 {Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "2", Index: true}, {Key: "owner", Value: "Ivan", Index: true}}}, 203 {Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "3", Index: false}, {Key: "owner", Value: "Mickey", Index: false}}}, 204 {Type: "", Attributes: []abci.EventAttribute{{Key: "not_allowed", Value: "Vlad", Index: true}}}, 205 }) 206 207 err := indexer.Index(txResult) 208 require.NoError(t, err) 209 210 testCases := map[string]struct { 211 q string 212 resultsLength int 213 }{ 214 "Don't match non-indexed events": { 215 q: "match.events = 1 AND account.number = 3 AND account.owner = 'Mickey'", 216 resultsLength: 0, 217 }, 218 "Return all events from a height with range": { 219 q: "match.events = 1 AND tx.height > 0", 220 resultsLength: 1, 221 }, 222 "Return all events from a height with range 2": { 223 q: "match.events = 1 AND tx.height <= 1", 224 resultsLength: 1, 225 }, 226 "Return all events from a height": { 227 q: "match.events = 1 AND tx.height = 1", 228 resultsLength: 1, 229 }, 230 "Return all events from a height (deduplicate height)": { 231 q: "match.events = 1 AND tx.height = 1 AND tx.height = 1", 232 resultsLength: 1, 233 }, 234 "Match attributes with height range and event": { 235 q: "match.events = 1 AND tx.height < 2 AND tx.height > 0 AND account.number = 1 AND account.owner CONTAINS 'Ana' AND account.owner CONTAINS 'An'", 236 resultsLength: 1, 237 }, 238 "Match attributes with height range and event - no match": { 239 q: "match.events = 1 AND tx.height < 2 AND tx.height > 0 AND account.number = 2 AND account.owner = 'Ana'", 240 resultsLength: 0, 241 }, 242 "Deduplucation test - match events only at the beginning": { 243 q: "tx.height < 2 AND tx.height > 0 AND account.number = 2 AND account.owner = 'Ana' AND match.events = 1", 244 resultsLength: 1, 245 }, 246 "Deduplucation test - should return nothing if attribute repeats multiple times": { 247 q: "match.events = 0 AND tx.height < 2 AND account.number = 3 AND account.number = 2 AND account.number = 5", 248 resultsLength: 0, 249 }, 250 "Deduplucation test - should return nothing if attribute repeats multiple times with match events": { 251 q: "match.events = 1 AND tx.height < 2 AND account.number = 3 AND account.number = 2 AND account.number = 5", 252 resultsLength: 0, 253 }, 254 "Deduplucation test - match events multiple": { 255 q: "match.events = 1 AND tx.height < 2 AND tx.height > 0 AND account.number = 2 AND account.owner = 'Ana' AND match.events = 1", 256 resultsLength: 0, 257 }, 258 "Match attributes with event": { 259 q: "account.number = 2 AND account.owner = 'Ana' AND tx.height = 1", 260 resultsLength: 1, 261 }, 262 "Match range w/o match events": { 263 q: "account.number < 2 AND account.owner = 'Ivan'", 264 resultsLength: 1, 265 }, 266 " Match range with match events set to 0": { 267 q: "match.events = 0 AND account.number < 2 AND account.owner = 'Ivan' AND tx.height > 0", 268 resultsLength: 1, 269 }, 270 " Match range with match events": { 271 q: "match.events = 1 AND account.number < 2 AND account.owner = 'Ivan' AND tx.height > 0", 272 resultsLength: 0, 273 }, 274 " Match range with match events 2": { 275 q: "match.events = 1 AND account.number <= 2 AND account.owner = 'Ivan' AND tx.height > 0", 276 resultsLength: 1, 277 }, 278 } 279 280 ctx := context.Background() 281 282 for _, tc := range testCases { 283 tc := tc 284 t.Run(tc.q, func(t *testing.T) { 285 results, err := indexer.Search(ctx, query.MustParse(tc.q)) 286 assert.NoError(t, err) 287 288 assert.Len(t, results, tc.resultsLength) 289 if tc.resultsLength > 0 { 290 for _, txr := range results { 291 assert.True(t, proto.Equal(txResult, txr)) 292 } 293 } 294 }) 295 } 296 } 297 func TestTxSearchWithCancelation(t *testing.T) { 298 indexer := NewTxIndex(db.NewMemDB()) 299 300 txResult := txResultWithEvents([]abci.Event{ 301 {Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "1", Index: true}}}, 302 {Type: "account", Attributes: []abci.EventAttribute{{Key: "owner", Value: "Ivan", Index: true}}}, 303 {Type: "", Attributes: []abci.EventAttribute{{Key: "not_allowed", Value: "Vlad", Index: true}}}, 304 }) 305 err := indexer.Index(txResult) 306 require.NoError(t, err) 307 308 ctx, cancel := context.WithCancel(context.Background()) 309 cancel() 310 results, err := indexer.Search(ctx, query.MustParse("account.number = 1")) 311 assert.NoError(t, err) 312 assert.Empty(t, results) 313 } 314 315 func TestTxSearchDeprecatedIndexing(t *testing.T) { 316 indexer := NewTxIndex(db.NewMemDB()) 317 318 // index tx using events indexing (composite key) 319 txResult1 := txResultWithEvents([]abci.Event{ 320 {Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "1", Index: true}}}, 321 }) 322 hash1 := types.Tx(txResult1.Tx).Hash() 323 324 err := indexer.Index(txResult1) 325 require.NoError(t, err) 326 327 // index tx also using deprecated indexing (event as key) 328 txResult2 := txResultWithEvents(nil) 329 txResult2.Tx = types.Tx("HELLO WORLD 2") 330 331 hash2 := types.Tx(txResult2.Tx).Hash() 332 b := indexer.store.NewBatch() 333 334 rawBytes, err := proto.Marshal(txResult2) 335 require.NoError(t, err) 336 337 depKey := []byte(fmt.Sprintf("%s/%s/%d/%d", 338 "sender", 339 "addr1", 340 txResult2.Height, 341 txResult2.Index, 342 )) 343 344 err = b.Set(depKey, hash2) 345 require.NoError(t, err) 346 err = b.Set(keyForHeight(txResult2), hash2) 347 require.NoError(t, err) 348 err = b.Set(hash2, rawBytes) 349 require.NoError(t, err) 350 err = b.Write() 351 require.NoError(t, err) 352 353 testCases := []struct { 354 q string 355 results []*abci.TxResult 356 }{ 357 // search by hash 358 {fmt.Sprintf("tx.hash = '%X'", hash1), []*abci.TxResult{txResult1}}, 359 // search by hash 360 {fmt.Sprintf("tx.hash = '%X'", hash2), []*abci.TxResult{txResult2}}, 361 // search by exact match (one key) 362 {"account.number = 1", []*abci.TxResult{txResult1}}, 363 {"account.number >= 1 AND account.number <= 5", []*abci.TxResult{txResult1}}, 364 // search by range (lower bound) 365 {"account.number >= 1", []*abci.TxResult{txResult1}}, 366 // search by range (upper bound) 367 {"account.number <= 5", []*abci.TxResult{txResult1}}, 368 // search using not allowed key 369 {"not_allowed = 'boom'", []*abci.TxResult{}}, 370 // search for not existing tx result 371 {"account.number >= 2 AND account.number <= 5", []*abci.TxResult{}}, 372 // search using not existing key 373 {"account.date >= TIME 2013-05-03T14:45:00Z", []*abci.TxResult{}}, 374 // search by deprecated key 375 {"sender = 'addr1'", []*abci.TxResult{txResult2}}, 376 } 377 378 ctx := context.Background() 379 380 for _, tc := range testCases { 381 tc := tc 382 t.Run(tc.q, func(t *testing.T) { 383 results, err := indexer.Search(ctx, query.MustParse(tc.q)) 384 require.NoError(t, err) 385 for _, txr := range results { 386 for _, tr := range tc.results { 387 assert.True(t, proto.Equal(tr, txr)) 388 } 389 } 390 }) 391 } 392 } 393 394 func TestTxSearchOneTxWithMultipleSameTagsButDifferentValues(t *testing.T) { 395 indexer := NewTxIndex(db.NewMemDB()) 396 397 txResult := txResultWithEvents([]abci.Event{ 398 {Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "1", Index: true}}}, 399 {Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "2", Index: true}}}, 400 {Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "3", Index: false}}}, 401 }) 402 403 err := indexer.Index(txResult) 404 require.NoError(t, err) 405 406 testCases := []struct { 407 q string 408 found bool 409 }{ 410 { 411 q: "match.events = 1 AND account.number >= 1", 412 found: true, 413 }, 414 { 415 q: "match.events = 1 AND account.number > 2", 416 found: false, 417 }, 418 { 419 q: "match.events = 1 AND account.number >= 1 AND tx.height = 3 AND tx.height > 0", 420 found: true, 421 }, 422 { 423 q: "match.events = 1 AND account.number >= 1 AND tx.height > 0 AND tx.height = 3", 424 found: true, 425 }, 426 427 // { 428 // q: "match.events = 1 AND account.number >= 1 AND tx.height = 3 AND tx.height = 2 AND tx.height = 1", 429 // found: true, 430 // }, 431 432 { 433 q: "match.events = 1 AND account.number >= 1 AND tx.height = 1 AND tx.height = 2 AND tx.height = 1", 434 found: true, 435 }, 436 { 437 q: "match.events = 1 AND account.number >= 1 AND tx.height = 3", 438 found: false, 439 }, 440 { 441 q: "match.events = 1 AND account.number > 1 AND tx.height < 2", 442 found: true, 443 }, 444 { 445 q: "match.events = 1 AND account.number >= 2", 446 found: true, 447 }, 448 { 449 q: "match.events = 1 AND account.number <= 1", 450 found: true, 451 }, 452 { 453 q: "match.events = 1 AND account.number = 'something'", 454 found: false, 455 }, 456 { 457 q: "match.events = 1 AND account.number CONTAINS 'bla'", 458 found: false, 459 }, 460 } 461 462 ctx := context.Background() 463 464 for _, tc := range testCases { 465 results, err := indexer.Search(ctx, query.MustParse(tc.q)) 466 assert.NoError(t, err) 467 468 len := 0 469 if tc.found { 470 len = 1 471 } 472 assert.Len(t, results, len) 473 assert.True(t, !tc.found || proto.Equal(txResult, results[0])) 474 } 475 } 476 477 func TestTxIndexDuplicatePreviouslySuccessful(t *testing.T) { 478 mockTx := types.Tx("MOCK_TX_HASH") 479 480 testCases := []struct { 481 name string 482 tx1 *abci.TxResult 483 tx2 *abci.TxResult 484 expOverwrite bool // do we expect the second tx to overwrite the first tx 485 }{ 486 { 487 "don't overwrite as a non-zero code was returned and the previous tx was successful", 488 &abci.TxResult{ 489 Height: 1, 490 Index: 0, 491 Tx: mockTx, 492 Result: abci.ResponseDeliverTx{ 493 Code: abci.CodeTypeOK, 494 }, 495 }, 496 &abci.TxResult{ 497 Height: 2, 498 Index: 0, 499 Tx: mockTx, 500 Result: abci.ResponseDeliverTx{ 501 Code: abci.CodeTypeOK + 1, 502 }, 503 }, 504 false, 505 }, 506 { 507 "overwrite as the previous tx was also unsuccessful", 508 &abci.TxResult{ 509 Height: 1, 510 Index: 0, 511 Tx: mockTx, 512 Result: abci.ResponseDeliverTx{ 513 Code: abci.CodeTypeOK + 1, 514 }, 515 }, 516 &abci.TxResult{ 517 Height: 2, 518 Index: 0, 519 Tx: mockTx, 520 Result: abci.ResponseDeliverTx{ 521 Code: abci.CodeTypeOK + 1, 522 }, 523 }, 524 true, 525 }, 526 { 527 "overwrite as the most recent tx was successful", 528 &abci.TxResult{ 529 Height: 1, 530 Index: 0, 531 Tx: mockTx, 532 Result: abci.ResponseDeliverTx{ 533 Code: abci.CodeTypeOK, 534 }, 535 }, 536 &abci.TxResult{ 537 Height: 2, 538 Index: 0, 539 Tx: mockTx, 540 Result: abci.ResponseDeliverTx{ 541 Code: abci.CodeTypeOK, 542 }, 543 }, 544 true, 545 }, 546 } 547 548 hash := mockTx.Hash() 549 550 for _, tc := range testCases { 551 t.Run(tc.name, func(t *testing.T) { 552 indexer := NewTxIndex(db.NewMemDB()) 553 554 // index the first tx 555 err := indexer.Index(tc.tx1) 556 require.NoError(t, err) 557 558 // index the same tx with different results 559 err = indexer.Index(tc.tx2) 560 require.NoError(t, err) 561 562 res, err := indexer.Get(hash) 563 require.NoError(t, err) 564 565 if tc.expOverwrite { 566 require.Equal(t, tc.tx2, res) 567 } else { 568 require.Equal(t, tc.tx1, res) 569 } 570 }) 571 } 572 } 573 574 func TestTxSearchMultipleTxs(t *testing.T) { 575 indexer := NewTxIndex(db.NewMemDB()) 576 577 // indexed first, but bigger height (to test the order of transactions) 578 txResult := txResultWithEvents([]abci.Event{ 579 {Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "1", Index: true}}}, 580 }) 581 582 txResult.Tx = types.Tx("Bob's account") 583 txResult.Height = 2 584 txResult.Index = 1 585 err := indexer.Index(txResult) 586 require.NoError(t, err) 587 588 // indexed second, but smaller height (to test the order of transactions) 589 txResult2 := txResultWithEvents([]abci.Event{ 590 {Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "2", Index: true}}}, 591 }) 592 txResult2.Tx = types.Tx("Alice's account") 593 txResult2.Height = 1 594 txResult2.Index = 2 595 596 err = indexer.Index(txResult2) 597 require.NoError(t, err) 598 599 // indexed third (to test the order of transactions) 600 txResult3 := txResultWithEvents([]abci.Event{ 601 {Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "3", Index: true}}}, 602 }) 603 txResult3.Tx = types.Tx("Jack's account") 604 txResult3.Height = 1 605 txResult3.Index = 1 606 err = indexer.Index(txResult3) 607 require.NoError(t, err) 608 609 // indexed fourth (to test we don't include txs with similar events) 610 // https://github.com/cometbft/cometbft/issues/2908 611 txResult4 := txResultWithEvents([]abci.Event{ 612 {Type: "account", Attributes: []abci.EventAttribute{{Key: "number.id", Value: "1", Index: true}}}, 613 }) 614 txResult4.Tx = types.Tx("Mike's account") 615 txResult4.Height = 2 616 txResult4.Index = 2 617 err = indexer.Index(txResult4) 618 require.NoError(t, err) 619 620 ctx := context.Background() 621 622 results, err := indexer.Search(ctx, query.MustParse("account.number >= 1")) 623 assert.NoError(t, err) 624 625 require.Len(t, results, 3) 626 } 627 628 func txResultWithEvents(events []abci.Event) *abci.TxResult { 629 tx := types.Tx("HELLO WORLD") 630 return &abci.TxResult{ 631 Height: 1, 632 Index: 0, 633 Tx: tx, 634 Result: abci.ResponseDeliverTx{ 635 Data: []byte{0}, 636 Code: abci.CodeTypeOK, 637 Log: "", 638 Events: events, 639 }, 640 } 641 } 642 643 func benchmarkTxIndex(txsCount int64, b *testing.B) { 644 dir, err := os.MkdirTemp("", "tx_index_db") 645 require.NoError(b, err) 646 defer os.RemoveAll(dir) 647 648 store, err := db.NewDB("tx_index", "goleveldb", dir) 649 require.NoError(b, err) 650 indexer := NewTxIndex(store) 651 652 batch := txindex.NewBatch(txsCount) 653 txIndex := uint32(0) 654 for i := int64(0); i < txsCount; i++ { 655 tx := cmtrand.Bytes(250) 656 txResult := &abci.TxResult{ 657 Height: 1, 658 Index: txIndex, 659 Tx: tx, 660 Result: abci.ResponseDeliverTx{ 661 Data: []byte{0}, 662 Code: abci.CodeTypeOK, 663 Log: "", 664 Events: []abci.Event{}, 665 }, 666 } 667 if err := batch.Add(txResult); err != nil { 668 b.Fatal(err) 669 } 670 txIndex++ 671 } 672 673 b.ResetTimer() 674 675 for n := 0; n < b.N; n++ { 676 err = indexer.AddBatch(batch) 677 } 678 if err != nil { 679 b.Fatal(err) 680 } 681 } 682 683 func BenchmarkTxIndex1(b *testing.B) { benchmarkTxIndex(1, b) } 684 func BenchmarkTxIndex500(b *testing.B) { benchmarkTxIndex(500, b) } 685 func BenchmarkTxIndex1000(b *testing.B) { benchmarkTxIndex(1000, b) } 686 func BenchmarkTxIndex2000(b *testing.B) { benchmarkTxIndex(2000, b) } 687 func BenchmarkTxIndex10000(b *testing.B) { benchmarkTxIndex(10000, b) }