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.

257 lines
7.6 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. "fmt"
  4. "sort"
  5. tmmath "github.com/tendermint/tendermint/libs/math"
  6. tmquery "github.com/tendermint/tendermint/libs/pubsub/query"
  7. ctypes "github.com/tendermint/tendermint/rpc/core/types"
  8. rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
  9. "github.com/tendermint/tendermint/state/indexer"
  10. "github.com/tendermint/tendermint/types"
  11. )
  12. // BlockchainInfo gets block headers for minHeight <= height <= maxHeight.
  13. //
  14. // If maxHeight does not yet exist, blocks up to the current height will be
  15. // returned. If minHeight does not exist (due to pruning), earliest existing
  16. // height will be used.
  17. //
  18. // At most 20 items will be returned. Block headers are returned in descending
  19. // order (highest first).
  20. //
  21. // More: https://docs.tendermint.com/master/rpc/#/Info/blockchain
  22. func (env *Environment) BlockchainInfo(
  23. ctx *rpctypes.Context,
  24. minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
  25. const limit int64 = 20
  26. var err error
  27. minHeight, maxHeight, err = filterMinMax(
  28. env.BlockStore.Base(),
  29. env.BlockStore.Height(),
  30. minHeight,
  31. maxHeight,
  32. limit)
  33. if err != nil {
  34. return nil, err
  35. }
  36. env.Logger.Debug("BlockchainInfo", "maxHeight", maxHeight, "minHeight", minHeight)
  37. blockMetas := make([]*types.BlockMeta, 0, maxHeight-minHeight+1)
  38. for height := maxHeight; height >= minHeight; height-- {
  39. blockMeta := env.BlockStore.LoadBlockMeta(height)
  40. if blockMeta != nil {
  41. blockMetas = append(blockMetas, blockMeta)
  42. }
  43. }
  44. return &ctypes.ResultBlockchainInfo{
  45. LastHeight: env.BlockStore.Height(),
  46. BlockMetas: blockMetas}, nil
  47. }
  48. // error if either min or max are negative or min > max
  49. // if 0, use blockstore base for min, latest block height for max
  50. // enforce limit.
  51. func filterMinMax(base, height, min, max, limit int64) (int64, int64, error) {
  52. // filter negatives
  53. if min < 0 || max < 0 {
  54. return min, max, ctypes.ErrZeroOrNegativeHeight
  55. }
  56. // adjust for default values
  57. if min == 0 {
  58. min = 1
  59. }
  60. if max == 0 {
  61. max = height
  62. }
  63. // limit max to the height
  64. max = tmmath.MinInt64(height, max)
  65. // limit min to the base
  66. min = tmmath.MaxInt64(base, min)
  67. // limit min to within `limit` of max
  68. // so the total number of blocks returned will be `limit`
  69. min = tmmath.MaxInt64(min, max-limit+1)
  70. if min > max {
  71. return min, max, fmt.Errorf("%w: min height %d can't be greater than max height %d",
  72. ctypes.ErrInvalidRequest, min, max)
  73. }
  74. return min, max, nil
  75. }
  76. // Block gets block at a given height.
  77. // If no height is provided, it will fetch the latest block.
  78. // More: https://docs.tendermint.com/master/rpc/#/Info/block
  79. func (env *Environment) Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) {
  80. height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
  81. if err != nil {
  82. return nil, err
  83. }
  84. blockMeta := env.BlockStore.LoadBlockMeta(height)
  85. if blockMeta == nil {
  86. return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: nil}, nil
  87. }
  88. block := env.BlockStore.LoadBlock(height)
  89. return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil
  90. }
  91. // BlockByHash gets block by hash.
  92. // More: https://docs.tendermint.com/master/rpc/#/Info/block_by_hash
  93. func (env *Environment) BlockByHash(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error) {
  94. block := env.BlockStore.LoadBlockByHash(hash)
  95. if block == nil {
  96. return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: nil}, nil
  97. }
  98. // If block is not nil, then blockMeta can't be nil.
  99. blockMeta := env.BlockStore.LoadBlockMeta(block.Height)
  100. return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil
  101. }
  102. // Commit gets block commit at a given height.
  103. // If no height is provided, it will fetch the commit for the latest block.
  104. // More: https://docs.tendermint.com/master/rpc/#/Info/commit
  105. func (env *Environment) Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, error) {
  106. height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
  107. if err != nil {
  108. return nil, err
  109. }
  110. blockMeta := env.BlockStore.LoadBlockMeta(height)
  111. if blockMeta == nil {
  112. return nil, nil
  113. }
  114. header := blockMeta.Header
  115. // If the next block has not been committed yet,
  116. // use a non-canonical commit
  117. if height == env.BlockStore.Height() {
  118. commit := env.BlockStore.LoadSeenCommit()
  119. // NOTE: we can't yet ensure atomicity of operations in asserting
  120. // whether this is the latest height and retrieving the seen commit
  121. if commit != nil && commit.Height == height {
  122. return ctypes.NewResultCommit(&header, commit, false), nil
  123. }
  124. }
  125. // Return the canonical commit (comes from the block at height+1)
  126. commit := env.BlockStore.LoadBlockCommit(height)
  127. if commit == nil {
  128. return nil, nil
  129. }
  130. return ctypes.NewResultCommit(&header, commit, true), nil
  131. }
  132. // BlockResults gets ABCIResults at a given height.
  133. // If no height is provided, it will fetch results for the latest block.
  134. //
  135. // Results are for the height of the block containing the txs.
  136. // Thus response.results.deliver_tx[5] is the results of executing
  137. // getBlock(h).Txs[5]
  138. // More: https://docs.tendermint.com/master/rpc/#/Info/block_results
  139. func (env *Environment) BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockResults, error) {
  140. height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
  141. if err != nil {
  142. return nil, err
  143. }
  144. results, err := env.StateStore.LoadABCIResponses(height)
  145. if err != nil {
  146. return nil, err
  147. }
  148. var totalGasUsed int64
  149. for _, tx := range results.GetDeliverTxs() {
  150. totalGasUsed += tx.GetGasUsed()
  151. }
  152. return &ctypes.ResultBlockResults{
  153. Height: height,
  154. TxsResults: results.DeliverTxs,
  155. TotalGasUsed: totalGasUsed,
  156. BeginBlockEvents: results.BeginBlock.Events,
  157. EndBlockEvents: results.EndBlock.Events,
  158. ValidatorUpdates: results.EndBlock.ValidatorUpdates,
  159. ConsensusParamUpdates: results.EndBlock.ConsensusParamUpdates,
  160. }, nil
  161. }
  162. // BlockSearch searches for a paginated set of blocks matching BeginBlock and
  163. // EndBlock event search criteria.
  164. func (env *Environment) BlockSearch(
  165. ctx *rpctypes.Context,
  166. query string,
  167. pagePtr, perPagePtr *int,
  168. orderBy string,
  169. ) (*ctypes.ResultBlockSearch, error) {
  170. if !indexer.KVSinkEnabled(env.EventSinks) {
  171. return nil, fmt.Errorf("block searching is disabled due to no kvEventSink")
  172. }
  173. q, err := tmquery.New(query)
  174. if err != nil {
  175. return nil, err
  176. }
  177. var kvsink indexer.EventSink
  178. for _, sink := range env.EventSinks {
  179. if sink.Type() == indexer.KV {
  180. kvsink = sink
  181. }
  182. }
  183. results, err := kvsink.SearchBlockEvents(ctx.Context(), q)
  184. if err != nil {
  185. return nil, err
  186. }
  187. // sort results (must be done before pagination)
  188. switch orderBy {
  189. case "desc", "":
  190. sort.Slice(results, func(i, j int) bool { return results[i] > results[j] })
  191. case "asc":
  192. sort.Slice(results, func(i, j int) bool { return results[i] < results[j] })
  193. default:
  194. return nil, fmt.Errorf("expected order_by to be either `asc` or `desc` or empty: %w", ctypes.ErrInvalidRequest)
  195. }
  196. // paginate results
  197. totalCount := len(results)
  198. perPage := env.validatePerPage(perPagePtr)
  199. page, err := validatePage(pagePtr, perPage, totalCount)
  200. if err != nil {
  201. return nil, err
  202. }
  203. skipCount := validateSkipCount(page, perPage)
  204. pageSize := tmmath.MinInt(perPage, totalCount-skipCount)
  205. apiResults := make([]*ctypes.ResultBlock, 0, pageSize)
  206. for i := skipCount; i < skipCount+pageSize; i++ {
  207. block := env.BlockStore.LoadBlock(results[i])
  208. if block != nil {
  209. blockMeta := env.BlockStore.LoadBlockMeta(block.Height)
  210. if blockMeta != nil {
  211. apiResults = append(apiResults, &ctypes.ResultBlock{
  212. Block: block,
  213. BlockID: blockMeta.BlockID,
  214. })
  215. }
  216. }
  217. }
  218. return &ctypes.ResultBlockSearch{Blocks: apiResults, TotalCount: totalCount}, nil
  219. }