You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

351 lines
9.6 KiB

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