github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/internal/state/indexer/sink/kv/kv_test.go (about) 1 package kv 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 8 dbm "github.com/tendermint/tm-db" 9 10 "github.com/gogo/protobuf/proto" 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 14 abci "github.com/ari-anchor/sei-tendermint/abci/types" 15 "github.com/ari-anchor/sei-tendermint/internal/pubsub/query" 16 "github.com/ari-anchor/sei-tendermint/internal/state/indexer" 17 kvtx "github.com/ari-anchor/sei-tendermint/internal/state/indexer/tx/kv" 18 "github.com/ari-anchor/sei-tendermint/types" 19 ) 20 21 func TestType(t *testing.T) { 22 kvSink := NewEventSink(dbm.NewMemDB()) 23 assert.Equal(t, indexer.KV, kvSink.Type()) 24 } 25 26 func TestStop(t *testing.T) { 27 kvSink := NewEventSink(dbm.NewMemDB()) 28 assert.Nil(t, kvSink.Stop()) 29 } 30 31 func TestBlockFuncs(t *testing.T) { 32 store := dbm.NewPrefixDB(dbm.NewMemDB(), []byte("block_events")) 33 indexer := NewEventSink(store) 34 35 require.NoError(t, indexer.IndexBlockEvents(types.EventDataNewBlockHeader{ 36 Header: types.Header{Height: 1}, 37 ResultFinalizeBlock: abci.ResponseFinalizeBlock{ 38 Events: []abci.Event{ 39 { 40 Type: "finalize_eventA", 41 Attributes: []abci.EventAttribute{ 42 { 43 Key: []byte("proposer"), 44 Value: []byte("FCAA001"), 45 Index: true, 46 }, 47 }, 48 }, 49 { 50 Type: "finalize_eventB", 51 Attributes: []abci.EventAttribute{ 52 { 53 Key: []byte("foo"), 54 Value: []byte("100"), 55 Index: true, 56 }, 57 }, 58 }, 59 }, 60 }, 61 })) 62 63 b, e := indexer.HasBlock(1) 64 assert.Nil(t, e) 65 assert.True(t, b) 66 67 for i := 2; i < 12; i++ { 68 var index bool 69 if i%2 == 0 { 70 index = true 71 } 72 73 require.NoError(t, indexer.IndexBlockEvents(types.EventDataNewBlockHeader{ 74 Header: types.Header{Height: int64(i)}, 75 ResultFinalizeBlock: abci.ResponseFinalizeBlock{ 76 Events: []abci.Event{ 77 { 78 Type: "finalize_eventA", 79 Attributes: []abci.EventAttribute{ 80 { 81 Key: []byte("proposer"), 82 Value: []byte("FCAA001"), 83 Index: true, 84 }, 85 }, 86 }, 87 { 88 Type: "finalize_eventB", 89 Attributes: []abci.EventAttribute{ 90 { 91 Key: []byte("foo"), 92 Value: []byte(fmt.Sprintf("%d", i)), 93 Index: index, 94 }, 95 }, 96 }, 97 }, 98 }, 99 })) 100 } 101 102 testCases := map[string]struct { 103 q *query.Query 104 results []int64 105 }{ 106 "block.height = 100": { 107 q: query.MustCompile(`block.height = 100`), 108 results: []int64{}, 109 }, 110 "block.height = 5": { 111 q: query.MustCompile(`block.height = 5`), 112 results: []int64{5}, 113 }, 114 "finalize_eventA.key1 = 'value1'": { 115 q: query.MustCompile(`finalize_eventA.key1 = 'value1'`), 116 results: []int64{}, 117 }, 118 "finalize_eventA.proposer = 'FCAA001'": { 119 q: query.MustCompile(`finalize_eventA.proposer = 'FCAA001'`), 120 results: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 121 }, 122 "finalize_eventB.foo <= 5": { 123 q: query.MustCompile(`finalize_eventB.foo <= 5`), 124 results: []int64{2, 4}, 125 }, 126 "finalize_eventB.foo >= 100": { 127 q: query.MustCompile(`finalize_eventB.foo >= 100`), 128 results: []int64{1}, 129 }, 130 "block.height > 2 AND finalize_eventB.foo <= 8": { 131 q: query.MustCompile(`block.height > 2 AND finalize_eventB.foo <= 8`), 132 results: []int64{4, 6, 8}, 133 }, 134 "finalize_eventA.proposer CONTAINS 'FFFFFFF'": { 135 q: query.MustCompile(`finalize_eventA.proposer CONTAINS 'FFFFFFF'`), 136 results: []int64{}, 137 }, 138 "finalize_eventA.proposer CONTAINS 'FCAA001'": { 139 q: query.MustCompile(`finalize_eventA.proposer CONTAINS 'FCAA001'`), 140 results: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 141 }, 142 } 143 144 for name, tc := range testCases { 145 tc := tc 146 t.Run(name, func(t *testing.T) { 147 ctx, cancel := context.WithCancel(context.Background()) 148 defer cancel() 149 150 results, err := indexer.SearchBlockEvents(ctx, tc.q) 151 require.NoError(t, err) 152 require.Equal(t, tc.results, results) 153 }) 154 } 155 } 156 157 func TestTxSearchWithCancelation(t *testing.T) { 158 indexer := NewEventSink(dbm.NewMemDB()) 159 160 txResult := txResultWithEvents([]abci.Event{ 161 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}}, 162 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("owner"), Value: []byte("Ivan"), Index: true}}}, 163 {Type: "", Attributes: []abci.EventAttribute{{Key: []byte("not_allowed"), Value: []byte("Vlad"), Index: true}}}, 164 }) 165 err := indexer.IndexTxEvents([]*abci.TxResult{txResult}) 166 require.NoError(t, err) 167 168 r, e := indexer.GetTxByHash(types.Tx("HELLO WORLD").Hash()) 169 assert.Nil(t, e) 170 assert.Equal(t, r, txResult) 171 172 ctx, cancel := context.WithCancel(context.Background()) 173 cancel() 174 results, err := indexer.SearchTxEvents(ctx, query.MustCompile(`account.number = 1`)) 175 assert.NoError(t, err) 176 assert.Empty(t, results) 177 } 178 179 func TestTxSearchDeprecatedIndexing(t *testing.T) { 180 esdb := dbm.NewMemDB() 181 indexer := NewEventSink(esdb) 182 183 // index tx using events indexing (composite key) 184 txResult1 := txResultWithEvents([]abci.Event{ 185 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}}, 186 }) 187 hash1 := types.Tx(txResult1.Tx).Hash() 188 189 err := indexer.IndexTxEvents([]*abci.TxResult{txResult1}) 190 require.NoError(t, err) 191 192 // index tx also using deprecated indexing (event as key) 193 txResult2 := txResultWithEvents(nil) 194 txResult2.Tx = types.Tx("HELLO WORLD 2") 195 196 hash2 := types.Tx(txResult2.Tx).Hash() 197 b := esdb.NewBatch() 198 199 rawBytes, err := proto.Marshal(txResult2) 200 require.NoError(t, err) 201 202 depKey := []byte(fmt.Sprintf("%s/%s/%d/%d", 203 "sender", 204 "addr1", 205 txResult2.Height, 206 txResult2.Index, 207 )) 208 209 err = b.Set(depKey, hash2) 210 require.NoError(t, err) 211 err = b.Set(kvtx.KeyFromHeight(txResult2), hash2) 212 require.NoError(t, err) 213 err = b.Set(hash2, rawBytes) 214 require.NoError(t, err) 215 err = b.Write() 216 require.NoError(t, err) 217 218 testCases := []struct { 219 q string 220 results []*abci.TxResult 221 }{ 222 // search by hash 223 {fmt.Sprintf("tx.hash = '%X'", hash1), []*abci.TxResult{txResult1}}, 224 // search by hash 225 {fmt.Sprintf("tx.hash = '%X'", hash2), []*abci.TxResult{txResult2}}, 226 // search by exact match (one key) 227 {"account.number = 1", []*abci.TxResult{txResult1}}, 228 {"account.number >= 1 AND account.number <= 5", []*abci.TxResult{txResult1}}, 229 // search by range (lower bound) 230 {"account.number >= 1", []*abci.TxResult{txResult1}}, 231 // search by range (upper bound) 232 {"account.number <= 5", []*abci.TxResult{txResult1}}, 233 // search using not allowed key 234 {"not_allowed = 'boom'", []*abci.TxResult{}}, 235 // search for not existing tx result 236 {"account.number >= 2 AND account.number <= 5", []*abci.TxResult{}}, 237 // search using not existing key 238 {"account.date >= TIME 2013-05-03T14:45:00Z", []*abci.TxResult{}}, 239 // search by deprecated key 240 {"sender = 'addr1'", []*abci.TxResult{txResult2}}, 241 } 242 243 ctx := context.Background() 244 245 for _, tc := range testCases { 246 tc := tc 247 t.Run(tc.q, func(t *testing.T) { 248 results, err := indexer.SearchTxEvents(ctx, query.MustCompile(tc.q)) 249 require.NoError(t, err) 250 for _, txr := range results { 251 for _, tr := range tc.results { 252 assert.True(t, proto.Equal(tr, txr)) 253 } 254 } 255 }) 256 } 257 } 258 259 func TestTxSearchOneTxWithMultipleSameTagsButDifferentValues(t *testing.T) { 260 indexer := NewEventSink(dbm.NewMemDB()) 261 262 txResult := txResultWithEvents([]abci.Event{ 263 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}}, 264 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("2"), Index: true}}}, 265 }) 266 267 err := indexer.IndexTxEvents([]*abci.TxResult{txResult}) 268 require.NoError(t, err) 269 270 ctx := context.Background() 271 272 results, err := indexer.SearchTxEvents(ctx, query.MustCompile(`account.number >= 1`)) 273 assert.NoError(t, err) 274 275 assert.Len(t, results, 1) 276 for _, txr := range results { 277 assert.True(t, proto.Equal(txResult, txr)) 278 } 279 } 280 281 func TestTxSearchMultipleTxs(t *testing.T) { 282 indexer := NewEventSink(dbm.NewMemDB()) 283 284 // indexed first, but bigger height (to test the order of transactions) 285 txResult := txResultWithEvents([]abci.Event{ 286 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("1"), Index: true}}}, 287 }) 288 289 txResult.Tx = types.Tx("Bob's account") 290 txResult.Height = 2 291 txResult.Index = 1 292 err := indexer.IndexTxEvents([]*abci.TxResult{txResult}) 293 require.NoError(t, err) 294 295 // indexed second, but smaller height (to test the order of transactions) 296 txResult2 := txResultWithEvents([]abci.Event{ 297 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("2"), Index: true}}}, 298 }) 299 txResult2.Tx = types.Tx("Alice's account") 300 txResult2.Height = 1 301 txResult2.Index = 2 302 303 err = indexer.IndexTxEvents([]*abci.TxResult{txResult2}) 304 require.NoError(t, err) 305 306 // indexed third (to test the order of transactions) 307 txResult3 := txResultWithEvents([]abci.Event{ 308 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number"), Value: []byte("3"), Index: true}}}, 309 }) 310 txResult3.Tx = types.Tx("Jack's account") 311 txResult3.Height = 1 312 txResult3.Index = 1 313 err = indexer.IndexTxEvents([]*abci.TxResult{txResult3}) 314 require.NoError(t, err) 315 316 // indexed fourth (to test we don't include txs with similar events) 317 // https://github.com/ari-anchor/sei-tendermint/issues/2908 318 txResult4 := txResultWithEvents([]abci.Event{ 319 {Type: "account", Attributes: []abci.EventAttribute{{Key: []byte("number.id"), Value: []byte("1"), Index: true}}}, 320 }) 321 txResult4.Tx = types.Tx("Mike's account") 322 txResult4.Height = 2 323 txResult4.Index = 2 324 err = indexer.IndexTxEvents([]*abci.TxResult{txResult4}) 325 require.NoError(t, err) 326 327 ctx := context.Background() 328 329 results, err := indexer.SearchTxEvents(ctx, query.MustCompile(`account.number >= 1`)) 330 assert.NoError(t, err) 331 332 require.Len(t, results, 3) 333 } 334 335 func txResultWithEvents(events []abci.Event) *abci.TxResult { 336 tx := types.Tx("HELLO WORLD") 337 return &abci.TxResult{ 338 Height: 1, 339 Index: 0, 340 Tx: tx, 341 Result: abci.ExecTxResult{ 342 Data: []byte{0}, 343 Code: abci.CodeTypeOK, 344 Log: "", 345 Events: events, 346 }, 347 } 348 }