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.

238 lines
7.2 KiB

8 years ago
add support for block pruning via ABCI Commit response (#4588) * Added BlockStore.DeleteBlock() * Added initial block pruner prototype * wip * Added BlockStore.PruneBlocks() * Added consensus setting for block pruning * Added BlockStore base * Error on replay if base does not have blocks * Handle missing blocks when sending VoteSetMaj23Message * Error message tweak * Properly update blockstore state * Error message fix again * blockchain: ignore peer missing blocks * Added FIXME * Added test for block replay with truncated history * Handle peer base in blockchain reactor * Improved replay error handling * Added tests for Store.PruneBlocks() * Fix non-RPC handling of truncated block history * Panic on missing block meta in needProofBlock() * Updated changelog * Handle truncated block history in RPC layer * Added info about earliest block in /status RPC * Reorder height and base in blockchain reactor messages * Updated changelog * Fix tests * Appease linter * Minor review fixes * Non-empty BlockStores should always have base > 0 * Update code to assume base > 0 invariant * Added blockstore tests for pruning to 0 * Make sure we don't prune below the current base * Added BlockStore.Size() * config: added retain_blocks recommendations * Update v1 blockchain reactor to handle blockstore base * Added state database pruning * Propagate errors on missing validator sets * Comment tweaks * Improved error message Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * use ABCI field ResponseCommit.retain_height instead of retain-blocks config option * remove State.RetainHeight, return value instead * fix minor issues * rename pruneHeights() to pruneBlocks() * noop to fix GitHub borkage Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
5 years ago
add support for block pruning via ABCI Commit response (#4588) * Added BlockStore.DeleteBlock() * Added initial block pruner prototype * wip * Added BlockStore.PruneBlocks() * Added consensus setting for block pruning * Added BlockStore base * Error on replay if base does not have blocks * Handle missing blocks when sending VoteSetMaj23Message * Error message tweak * Properly update blockstore state * Error message fix again * blockchain: ignore peer missing blocks * Added FIXME * Added test for block replay with truncated history * Handle peer base in blockchain reactor * Improved replay error handling * Added tests for Store.PruneBlocks() * Fix non-RPC handling of truncated block history * Panic on missing block meta in needProofBlock() * Updated changelog * Handle truncated block history in RPC layer * Added info about earliest block in /status RPC * Reorder height and base in blockchain reactor messages * Updated changelog * Fix tests * Appease linter * Minor review fixes * Non-empty BlockStores should always have base > 0 * Update code to assume base > 0 invariant * Added blockstore tests for pruning to 0 * Make sure we don't prune below the current base * Added BlockStore.Size() * config: added retain_blocks recommendations * Update v1 blockchain reactor to handle blockstore base * Added state database pruning * Propagate errors on missing validator sets * Comment tweaks * Improved error message Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * use ABCI field ResponseCommit.retain_height instead of retain-blocks config option * remove State.RetainHeight, return value instead * fix minor issues * rename pruneHeights() to pruneBlocks() * noop to fix GitHub borkage Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
5 years ago
add support for block pruning via ABCI Commit response (#4588) * Added BlockStore.DeleteBlock() * Added initial block pruner prototype * wip * Added BlockStore.PruneBlocks() * Added consensus setting for block pruning * Added BlockStore base * Error on replay if base does not have blocks * Handle missing blocks when sending VoteSetMaj23Message * Error message tweak * Properly update blockstore state * Error message fix again * blockchain: ignore peer missing blocks * Added FIXME * Added test for block replay with truncated history * Handle peer base in blockchain reactor * Improved replay error handling * Added tests for Store.PruneBlocks() * Fix non-RPC handling of truncated block history * Panic on missing block meta in needProofBlock() * Updated changelog * Handle truncated block history in RPC layer * Added info about earliest block in /status RPC * Reorder height and base in blockchain reactor messages * Updated changelog * Fix tests * Appease linter * Minor review fixes * Non-empty BlockStores should always have base > 0 * Update code to assume base > 0 invariant * Added blockstore tests for pruning to 0 * Make sure we don't prune below the current base * Added BlockStore.Size() * config: added retain_blocks recommendations * Update v1 blockchain reactor to handle blockstore base * Added state database pruning * Propagate errors on missing validator sets * Comment tweaks * Improved error message Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * use ABCI field ResponseCommit.retain_height instead of retain-blocks config option * remove State.RetainHeight, return value instead * fix minor issues * rename pruneHeights() to pruneBlocks() * noop to fix GitHub borkage Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
5 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
7 years ago
7 years ago
  1. package core
  2. import (
  3. "errors"
  4. "fmt"
  5. "sort"
  6. tmmath "github.com/tendermint/tendermint/libs/math"
  7. tmquery "github.com/tendermint/tendermint/libs/pubsub/query"
  8. ctypes "github.com/tendermint/tendermint/rpc/core/types"
  9. rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
  10. blockidxnull "github.com/tendermint/tendermint/state/indexer/block/null"
  11. "github.com/tendermint/tendermint/types"
  12. )
  13. // BlockchainInfo gets block headers for minHeight <= height <= maxHeight.
  14. //
  15. // If maxHeight does not yet exist, blocks up to the current height will be
  16. // returned. If minHeight does not exist (due to pruning), earliest existing
  17. // height will be used.
  18. //
  19. // At most 20 items will be returned. Block headers are returned in descending
  20. // order (highest first).
  21. //
  22. // More: https://docs.tendermint.com/master/rpc/#/Info/blockchain
  23. func (env *Environment) BlockchainInfo(
  24. ctx *rpctypes.Context,
  25. minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
  26. const limit int64 = 20
  27. var err error
  28. minHeight, maxHeight, err = filterMinMax(
  29. env.BlockStore.Base(),
  30. env.BlockStore.Height(),
  31. minHeight,
  32. maxHeight,
  33. limit)
  34. if err != nil {
  35. return nil, err
  36. }
  37. env.Logger.Debug("BlockchainInfo", "maxHeight", maxHeight, "minHeight", minHeight)
  38. blockMetas := make([]*types.BlockMeta, 0, maxHeight-minHeight+1)
  39. for height := maxHeight; height >= minHeight; height-- {
  40. blockMeta := env.BlockStore.LoadBlockMeta(height)
  41. if blockMeta != nil {
  42. blockMetas = append(blockMetas, blockMeta)
  43. }
  44. }
  45. return &ctypes.ResultBlockchainInfo{
  46. LastHeight: env.BlockStore.Height(),
  47. BlockMetas: blockMetas}, nil
  48. }
  49. // error if either min or max are negative or min > max
  50. // if 0, use blockstore base for min, latest block height for max
  51. // enforce limit.
  52. func filterMinMax(base, height, min, max, limit int64) (int64, int64, error) {
  53. // filter negatives
  54. if min < 0 || max < 0 {
  55. return min, max, ctypes.ErrZeroOrNegativeHeight
  56. }
  57. // adjust for default values
  58. if min == 0 {
  59. min = 1
  60. }
  61. if max == 0 {
  62. max = height
  63. }
  64. // limit max to the height
  65. max = tmmath.MinInt64(height, max)
  66. // limit min to the base
  67. min = tmmath.MaxInt64(base, min)
  68. // limit min to within `limit` of max
  69. // so the total number of blocks returned will be `limit`
  70. min = tmmath.MaxInt64(min, max-limit+1)
  71. if min > max {
  72. return min, max, fmt.Errorf("%w: min height %d can't be greater than max height %d",
  73. ctypes.ErrInvalidRequest, min, max)
  74. }
  75. return min, max, nil
  76. }
  77. // Block gets block at a given height.
  78. // If no height is provided, it will fetch the latest block.
  79. // More: https://docs.tendermint.com/master/rpc/#/Info/block
  80. func (env *Environment) Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) {
  81. height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
  82. if err != nil {
  83. return nil, err
  84. }
  85. block := env.BlockStore.LoadBlock(height)
  86. blockMeta := env.BlockStore.LoadBlockMeta(height)
  87. if blockMeta == nil {
  88. return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: block}, nil
  89. }
  90. return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil
  91. }
  92. // BlockByHash gets block by hash.
  93. // More: https://docs.tendermint.com/master/rpc/#/Info/block_by_hash
  94. func (env *Environment) BlockByHash(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error) {
  95. block := env.BlockStore.LoadBlockByHash(hash)
  96. if block == nil {
  97. return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: nil}, nil
  98. }
  99. // If block is not nil, then blockMeta can't be nil.
  100. blockMeta := env.BlockStore.LoadBlockMeta(block.Height)
  101. return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil
  102. }
  103. // Commit gets block commit at a given height.
  104. // If no height is provided, it will fetch the commit for the latest block.
  105. // More: https://docs.tendermint.com/master/rpc/#/Info/commit
  106. func (env *Environment) Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, error) {
  107. height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
  108. if err != nil {
  109. return nil, err
  110. }
  111. blockMeta := env.BlockStore.LoadBlockMeta(height)
  112. if blockMeta == nil {
  113. return nil, nil
  114. }
  115. header := blockMeta.Header
  116. // If the next block has not been committed yet,
  117. // use a non-canonical commit
  118. if height == env.BlockStore.Height() {
  119. commit := env.BlockStore.LoadSeenCommit(height)
  120. return ctypes.NewResultCommit(&header, commit, false), nil
  121. }
  122. // Return the canonical commit (comes from the block at height+1)
  123. commit := env.BlockStore.LoadBlockCommit(height)
  124. return ctypes.NewResultCommit(&header, commit, true), nil
  125. }
  126. // BlockResults gets ABCIResults at a given height.
  127. // If no height is provided, it will fetch results for the latest block.
  128. //
  129. // Results are for the height of the block containing the txs.
  130. // Thus response.results.deliver_tx[5] is the results of executing
  131. // getBlock(h).Txs[5]
  132. // More: https://docs.tendermint.com/master/rpc/#/Info/block_results
  133. func (env *Environment) BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockResults, error) {
  134. height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
  135. if err != nil {
  136. return nil, err
  137. }
  138. results, err := env.StateStore.LoadABCIResponses(height)
  139. if err != nil {
  140. return nil, err
  141. }
  142. return &ctypes.ResultBlockResults{
  143. Height: height,
  144. TxsResults: results.DeliverTxs,
  145. BeginBlockEvents: results.BeginBlock.Events,
  146. EndBlockEvents: results.EndBlock.Events,
  147. ValidatorUpdates: results.EndBlock.ValidatorUpdates,
  148. ConsensusParamUpdates: results.EndBlock.ConsensusParamUpdates,
  149. }, nil
  150. }
  151. // BlockSearch searches for a paginated set of blocks matching BeginBlock and
  152. // EndBlock event search criteria.
  153. func (env *Environment) BlockSearch(
  154. ctx *rpctypes.Context,
  155. query string,
  156. pagePtr, perPagePtr *int,
  157. orderBy string,
  158. ) (*ctypes.ResultBlockSearch, error) {
  159. // skip if block indexing is disabled
  160. if _, ok := env.BlockIndexer.(*blockidxnull.BlockerIndexer); ok {
  161. return nil, errors.New("block indexing is disabled")
  162. }
  163. q, err := tmquery.New(query)
  164. if err != nil {
  165. return nil, err
  166. }
  167. results, err := env.BlockIndexer.Search(ctx.Context(), q)
  168. if err != nil {
  169. return nil, err
  170. }
  171. // sort results (must be done before pagination)
  172. switch orderBy {
  173. case "desc", "":
  174. sort.Slice(results, func(i, j int) bool { return results[i] > results[j] })
  175. case "asc":
  176. sort.Slice(results, func(i, j int) bool { return results[i] < results[j] })
  177. default:
  178. return nil, fmt.Errorf("%w: expected order_by to be either `asc` or `desc` or empty", ctypes.ErrInvalidRequest)
  179. }
  180. // paginate results
  181. totalCount := len(results)
  182. perPage := env.validatePerPage(perPagePtr)
  183. page, err := validatePage(pagePtr, perPage, totalCount)
  184. if err != nil {
  185. return nil, err
  186. }
  187. skipCount := validateSkipCount(page, perPage)
  188. pageSize := tmmath.MinInt(perPage, totalCount-skipCount)
  189. apiResults := make([]*ctypes.ResultBlock, 0, pageSize)
  190. for i := skipCount; i < skipCount+pageSize; i++ {
  191. block := env.BlockStore.LoadBlock(results[i])
  192. if block != nil {
  193. blockMeta := env.BlockStore.LoadBlockMeta(block.Height)
  194. if blockMeta != nil {
  195. apiResults = append(apiResults, &ctypes.ResultBlock{
  196. Block: block,
  197. BlockID: blockMeta.BlockID,
  198. })
  199. }
  200. }
  201. }
  202. return &ctypes.ResultBlockSearch{Blocks: apiResults, TotalCount: totalCount}, nil
  203. }