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.

490 lines
12 KiB

  1. package kv
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "sort"
  7. "strconv"
  8. "strings"
  9. "github.com/google/orderedcode"
  10. dbm "github.com/tendermint/tm-db"
  11. abci "github.com/tendermint/tendermint/abci/types"
  12. "github.com/tendermint/tendermint/libs/pubsub/query"
  13. "github.com/tendermint/tendermint/state/indexer"
  14. "github.com/tendermint/tendermint/types"
  15. )
  16. var _ indexer.BlockIndexer = (*BlockerIndexer)(nil)
  17. // BlockerIndexer implements a block indexer, indexing BeginBlock and EndBlock
  18. // events with an underlying KV store. Block events are indexed by their height,
  19. // such that matching search criteria returns the respective block height(s).
  20. type BlockerIndexer struct {
  21. store dbm.DB
  22. }
  23. func New(store dbm.DB) *BlockerIndexer {
  24. return &BlockerIndexer{
  25. store: store,
  26. }
  27. }
  28. // Has returns true if the given height has been indexed. An error is returned
  29. // upon database query failure.
  30. func (idx *BlockerIndexer) Has(height int64) (bool, error) {
  31. key, err := heightKey(height)
  32. if err != nil {
  33. return false, fmt.Errorf("failed to create block height index key: %w", err)
  34. }
  35. return idx.store.Has(key)
  36. }
  37. // Index indexes BeginBlock and EndBlock events for a given block by its height.
  38. // The following is indexed:
  39. //
  40. // primary key: encode(block.height | height) => encode(height)
  41. // BeginBlock events: encode(eventType.eventAttr|eventValue|height|begin_block) => encode(height)
  42. // EndBlock events: encode(eventType.eventAttr|eventValue|height|end_block) => encode(height)
  43. func (idx *BlockerIndexer) Index(bh types.EventDataNewBlockHeader) error {
  44. batch := idx.store.NewBatch()
  45. defer batch.Close()
  46. height := bh.Header.Height
  47. // 1. index by height
  48. key, err := heightKey(height)
  49. if err != nil {
  50. return fmt.Errorf("failed to create block height index key: %w", err)
  51. }
  52. if err := batch.Set(key, int64ToBytes(height)); err != nil {
  53. return err
  54. }
  55. // 2. index BeginBlock events
  56. if err := idx.indexEvents(batch, bh.ResultBeginBlock.Events, "begin_block", height); err != nil {
  57. return fmt.Errorf("failed to index BeginBlock events: %w", err)
  58. }
  59. // 3. index EndBlock events
  60. if err := idx.indexEvents(batch, bh.ResultEndBlock.Events, "end_block", height); err != nil {
  61. return fmt.Errorf("failed to index EndBlock events: %w", err)
  62. }
  63. return batch.WriteSync()
  64. }
  65. // Search performs a query for block heights that match a given BeginBlock
  66. // and Endblock event search criteria. The given query can match against zero,
  67. // one or more block heights. In the case of height queries, i.e. block.height=H,
  68. // if the height is indexed, that height alone will be returned. An error and
  69. // nil slice is returned. Otherwise, a non-nil slice and nil error is returned.
  70. func (idx *BlockerIndexer) Search(ctx context.Context, q *query.Query) ([]int64, error) {
  71. results := make([]int64, 0)
  72. select {
  73. case <-ctx.Done():
  74. return results, nil
  75. default:
  76. }
  77. conditions, err := q.Conditions()
  78. if err != nil {
  79. return nil, fmt.Errorf("failed to parse query conditions: %w", err)
  80. }
  81. // If there is an exact height query, return the result immediately
  82. // (if it exists).
  83. height, ok := lookForHeight(conditions)
  84. if ok {
  85. ok, err := idx.Has(height)
  86. if err != nil {
  87. return nil, err
  88. }
  89. if ok {
  90. return []int64{height}, nil
  91. }
  92. return results, nil
  93. }
  94. var heightsInitialized bool
  95. filteredHeights := make(map[string][]byte)
  96. // conditions to skip because they're handled before "everything else"
  97. skipIndexes := make([]int, 0)
  98. // Extract ranges. If both upper and lower bounds exist, it's better to get
  99. // them in order as to not iterate over kvs that are not within range.
  100. ranges, rangeIndexes := indexer.LookForRanges(conditions)
  101. if len(ranges) > 0 {
  102. skipIndexes = append(skipIndexes, rangeIndexes...)
  103. for _, qr := range ranges {
  104. prefix, err := orderedcode.Append(nil, qr.Key)
  105. if err != nil {
  106. return nil, fmt.Errorf("failed to create prefix key: %w", err)
  107. }
  108. if !heightsInitialized {
  109. filteredHeights, err = idx.matchRange(ctx, qr, prefix, filteredHeights, true)
  110. if err != nil {
  111. return nil, err
  112. }
  113. heightsInitialized = true
  114. // Ignore any remaining conditions if the first condition resulted in no
  115. // matches (assuming implicit AND operand).
  116. if len(filteredHeights) == 0 {
  117. break
  118. }
  119. } else {
  120. filteredHeights, err = idx.matchRange(ctx, qr, prefix, filteredHeights, false)
  121. if err != nil {
  122. return nil, err
  123. }
  124. }
  125. }
  126. }
  127. // for all other conditions
  128. for i, c := range conditions {
  129. if intInSlice(i, skipIndexes) {
  130. continue
  131. }
  132. startKey, err := orderedcode.Append(nil, c.CompositeKey, fmt.Sprintf("%v", c.Operand))
  133. if err != nil {
  134. return nil, err
  135. }
  136. if !heightsInitialized {
  137. filteredHeights, err = idx.match(ctx, c, startKey, filteredHeights, true)
  138. if err != nil {
  139. return nil, err
  140. }
  141. heightsInitialized = true
  142. // Ignore any remaining conditions if the first condition resulted in no
  143. // matches (assuming implicit AND operand).
  144. if len(filteredHeights) == 0 {
  145. break
  146. }
  147. } else {
  148. filteredHeights, err = idx.match(ctx, c, startKey, filteredHeights, false)
  149. if err != nil {
  150. return nil, err
  151. }
  152. }
  153. }
  154. // fetch matching heights
  155. results = make([]int64, 0, len(filteredHeights))
  156. for _, hBz := range filteredHeights {
  157. h := int64FromBytes(hBz)
  158. ok, err := idx.Has(h)
  159. if err != nil {
  160. return nil, err
  161. }
  162. if ok {
  163. results = append(results, h)
  164. }
  165. select {
  166. case <-ctx.Done():
  167. break
  168. default:
  169. }
  170. }
  171. sort.Slice(results, func(i, j int) bool { return results[i] < results[j] })
  172. return results, nil
  173. }
  174. // matchRange returns all matching block heights that match a given QueryRange
  175. // and start key. An already filtered result (filteredHeights) is provided such
  176. // that any non-intersecting matches are removed.
  177. //
  178. // NOTE: The provided filteredHeights may be empty if no previous condition has
  179. // matched.
  180. func (idx *BlockerIndexer) matchRange(
  181. ctx context.Context,
  182. qr indexer.QueryRange,
  183. startKey []byte,
  184. filteredHeights map[string][]byte,
  185. firstRun bool,
  186. ) (map[string][]byte, error) {
  187. // A previous match was attempted but resulted in no matches, so we return
  188. // no matches (assuming AND operand).
  189. if !firstRun && len(filteredHeights) == 0 {
  190. return filteredHeights, nil
  191. }
  192. tmpHeights := make(map[string][]byte)
  193. lowerBound := qr.LowerBoundValue()
  194. upperBound := qr.UpperBoundValue()
  195. it, err := dbm.IteratePrefix(idx.store, startKey)
  196. if err != nil {
  197. return nil, fmt.Errorf("failed to create prefix iterator: %w", err)
  198. }
  199. defer it.Close()
  200. LOOP:
  201. for ; it.Valid(); it.Next() {
  202. var (
  203. eventValue string
  204. err error
  205. )
  206. if qr.Key == types.BlockHeightKey {
  207. eventValue, err = parseValueFromPrimaryKey(it.Key())
  208. } else {
  209. eventValue, err = parseValueFromEventKey(it.Key())
  210. }
  211. if err != nil {
  212. continue
  213. }
  214. if _, ok := qr.AnyBound().(int64); ok {
  215. v, err := strconv.ParseInt(eventValue, 10, 64)
  216. if err != nil {
  217. continue LOOP
  218. }
  219. include := true
  220. if lowerBound != nil && v < lowerBound.(int64) {
  221. include = false
  222. }
  223. if upperBound != nil && v > upperBound.(int64) {
  224. include = false
  225. }
  226. if include {
  227. tmpHeights[string(it.Value())] = it.Value()
  228. }
  229. }
  230. select {
  231. case <-ctx.Done():
  232. break
  233. default:
  234. }
  235. }
  236. if err := it.Error(); err != nil {
  237. return nil, err
  238. }
  239. if len(tmpHeights) == 0 || firstRun {
  240. // Either:
  241. //
  242. // 1. Regardless if a previous match was attempted, which may have had
  243. // results, but no match was found for the current condition, then we
  244. // return no matches (assuming AND operand).
  245. //
  246. // 2. A previous match was not attempted, so we return all results.
  247. return tmpHeights, nil
  248. }
  249. // Remove/reduce matches in filteredHashes that were not found in this
  250. // match (tmpHashes).
  251. for k := range filteredHeights {
  252. if tmpHeights[k] == nil {
  253. delete(filteredHeights, k)
  254. select {
  255. case <-ctx.Done():
  256. break
  257. default:
  258. }
  259. }
  260. }
  261. return filteredHeights, nil
  262. }
  263. // match returns all matching heights that meet a given query condition and start
  264. // key. An already filtered result (filteredHeights) is provided such that any
  265. // non-intersecting matches are removed.
  266. //
  267. // NOTE: The provided filteredHeights may be empty if no previous condition has
  268. // matched.
  269. func (idx *BlockerIndexer) match(
  270. ctx context.Context,
  271. c query.Condition,
  272. startKeyBz []byte,
  273. filteredHeights map[string][]byte,
  274. firstRun bool,
  275. ) (map[string][]byte, error) {
  276. // A previous match was attempted but resulted in no matches, so we return
  277. // no matches (assuming AND operand).
  278. if !firstRun && len(filteredHeights) == 0 {
  279. return filteredHeights, nil
  280. }
  281. tmpHeights := make(map[string][]byte)
  282. switch {
  283. case c.Op == query.OpEqual:
  284. it, err := dbm.IteratePrefix(idx.store, startKeyBz)
  285. if err != nil {
  286. return nil, fmt.Errorf("failed to create prefix iterator: %w", err)
  287. }
  288. defer it.Close()
  289. for ; it.Valid(); it.Next() {
  290. tmpHeights[string(it.Value())] = it.Value()
  291. if err := ctx.Err(); err != nil {
  292. break
  293. }
  294. }
  295. if err := it.Error(); err != nil {
  296. return nil, err
  297. }
  298. case c.Op == query.OpExists:
  299. prefix, err := orderedcode.Append(nil, c.CompositeKey)
  300. if err != nil {
  301. return nil, err
  302. }
  303. it, err := dbm.IteratePrefix(idx.store, prefix)
  304. if err != nil {
  305. return nil, fmt.Errorf("failed to create prefix iterator: %w", err)
  306. }
  307. defer it.Close()
  308. for ; it.Valid(); it.Next() {
  309. tmpHeights[string(it.Value())] = it.Value()
  310. select {
  311. case <-ctx.Done():
  312. break
  313. default:
  314. }
  315. }
  316. if err := it.Error(); err != nil {
  317. return nil, err
  318. }
  319. case c.Op == query.OpContains:
  320. prefix, err := orderedcode.Append(nil, c.CompositeKey)
  321. if err != nil {
  322. return nil, err
  323. }
  324. it, err := dbm.IteratePrefix(idx.store, prefix)
  325. if err != nil {
  326. return nil, fmt.Errorf("failed to create prefix iterator: %w", err)
  327. }
  328. defer it.Close()
  329. for ; it.Valid(); it.Next() {
  330. eventValue, err := parseValueFromEventKey(it.Key())
  331. if err != nil {
  332. continue
  333. }
  334. if strings.Contains(eventValue, c.Operand.(string)) {
  335. tmpHeights[string(it.Value())] = it.Value()
  336. }
  337. select {
  338. case <-ctx.Done():
  339. break
  340. default:
  341. }
  342. }
  343. if err := it.Error(); err != nil {
  344. return nil, err
  345. }
  346. default:
  347. return nil, errors.New("other operators should be handled already")
  348. }
  349. if len(tmpHeights) == 0 || firstRun {
  350. // Either:
  351. //
  352. // 1. Regardless if a previous match was attempted, which may have had
  353. // results, but no match was found for the current condition, then we
  354. // return no matches (assuming AND operand).
  355. //
  356. // 2. A previous match was not attempted, so we return all results.
  357. return tmpHeights, nil
  358. }
  359. // Remove/reduce matches in filteredHeights that were not found in this
  360. // match (tmpHeights).
  361. for k := range filteredHeights {
  362. if tmpHeights[k] == nil {
  363. delete(filteredHeights, k)
  364. select {
  365. case <-ctx.Done():
  366. break
  367. default:
  368. }
  369. }
  370. }
  371. return filteredHeights, nil
  372. }
  373. func (idx *BlockerIndexer) indexEvents(batch dbm.Batch, events []abci.Event, typ string, height int64) error {
  374. heightBz := int64ToBytes(height)
  375. for _, event := range events {
  376. // only index events with a non-empty type
  377. if len(event.Type) == 0 {
  378. continue
  379. }
  380. for _, attr := range event.Attributes {
  381. if len(attr.Key) == 0 {
  382. continue
  383. }
  384. // index iff the event specified index:true and it's not a reserved event
  385. compositeKey := fmt.Sprintf("%s.%s", event.Type, string(attr.Key))
  386. if compositeKey == types.BlockHeightKey {
  387. return fmt.Errorf("event type and attribute key \"%s\" is reserved; please use a different key", compositeKey)
  388. }
  389. if attr.GetIndex() {
  390. key, err := eventKey(compositeKey, typ, string(attr.Value), height)
  391. if err != nil {
  392. return fmt.Errorf("failed to create block index key: %w", err)
  393. }
  394. if err := batch.Set(key, heightBz); err != nil {
  395. return err
  396. }
  397. }
  398. }
  399. }
  400. return nil
  401. }