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.

698 lines
21 KiB

7 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
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
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
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
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. package state
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "github.com/gogo/protobuf/proto"
  7. "github.com/google/orderedcode"
  8. dbm "github.com/tendermint/tm-db"
  9. abci "github.com/tendermint/tendermint/abci/types"
  10. tmmath "github.com/tendermint/tendermint/libs/math"
  11. tmos "github.com/tendermint/tendermint/libs/os"
  12. tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
  13. tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
  14. "github.com/tendermint/tendermint/types"
  15. )
  16. const (
  17. // persist validators every valSetCheckpointInterval blocks to avoid
  18. // LoadValidators taking too much time.
  19. // https://github.com/tendermint/tendermint/pull/3438
  20. // 100000 results in ~ 100ms to get 100 validators (see BenchmarkLoadValidators)
  21. valSetCheckpointInterval = 100000
  22. )
  23. //------------------------------------------------------------------------
  24. const (
  25. // prefixes are unique across all tm db's
  26. prefixValidators = int64(5)
  27. prefixConsensusParams = int64(6)
  28. prefixABCIResponses = int64(7)
  29. prefixState = int64(8)
  30. )
  31. func encodeKey(prefix int64, height int64) []byte {
  32. res, err := orderedcode.Append(nil, prefix, height)
  33. if err != nil {
  34. panic(err)
  35. }
  36. return res
  37. }
  38. func validatorsKey(height int64) []byte {
  39. return encodeKey(prefixValidators, height)
  40. }
  41. func consensusParamsKey(height int64) []byte {
  42. return encodeKey(prefixConsensusParams, height)
  43. }
  44. func abciResponsesKey(height int64) []byte {
  45. return encodeKey(prefixABCIResponses, height)
  46. }
  47. // stateKey should never change after being set in init()
  48. var stateKey []byte
  49. func init() {
  50. var err error
  51. stateKey, err = orderedcode.Append(nil, prefixState)
  52. if err != nil {
  53. panic(err)
  54. }
  55. }
  56. //----------------------
  57. //go:generate mockery --case underscore --name Store
  58. // Store defines the state store interface
  59. //
  60. // It is used to retrieve current state and save and load ABCI responses,
  61. // validators and consensus parameters
  62. type Store interface {
  63. // LoadFromDBOrGenesisFile loads the most recent state.
  64. // If the chain is new it will use the genesis file from the provided genesis file path as the current state.
  65. LoadFromDBOrGenesisFile(string) (State, error)
  66. // LoadFromDBOrGenesisDoc loads the most recent state.
  67. // If the chain is new it will use the genesis doc as the current state.
  68. LoadFromDBOrGenesisDoc(*types.GenesisDoc) (State, error)
  69. // Load loads the current state of the blockchain
  70. Load() (State, error)
  71. // LoadValidators loads the validator set at a given height
  72. LoadValidators(int64) (*types.ValidatorSet, error)
  73. // LoadABCIResponses loads the abciResponse for a given height
  74. LoadABCIResponses(int64) (*tmstate.ABCIResponses, error)
  75. // LoadConsensusParams loads the consensus params for a given height
  76. LoadConsensusParams(int64) (types.ConsensusParams, error)
  77. // Save overwrites the previous state with the updated one
  78. Save(State) error
  79. // SaveABCIResponses saves ABCIResponses for a given height
  80. SaveABCIResponses(int64, *tmstate.ABCIResponses) error
  81. // Bootstrap is used for bootstrapping state when not starting from a initial height.
  82. Bootstrap(State) error
  83. // PruneStates takes the height from which to prune up to (exclusive)
  84. PruneStates(int64) error
  85. }
  86. // dbStore wraps a db (github.com/tendermint/tm-db)
  87. type dbStore struct {
  88. db dbm.DB
  89. }
  90. var _ Store = (*dbStore)(nil)
  91. // NewStore creates the dbStore of the state pkg.
  92. func NewStore(db dbm.DB) Store {
  93. return dbStore{db}
  94. }
  95. // LoadStateFromDBOrGenesisFile loads the most recent state from the database,
  96. // or creates a new one from the given genesisFilePath.
  97. func (store dbStore) LoadFromDBOrGenesisFile(genesisFilePath string) (State, error) {
  98. state, err := store.Load()
  99. if err != nil {
  100. return State{}, err
  101. }
  102. if state.IsEmpty() {
  103. var err error
  104. state, err = MakeGenesisStateFromFile(genesisFilePath)
  105. if err != nil {
  106. return state, err
  107. }
  108. }
  109. return state, nil
  110. }
  111. // LoadStateFromDBOrGenesisDoc loads the most recent state from the database,
  112. // or creates a new one from the given genesisDoc.
  113. func (store dbStore) LoadFromDBOrGenesisDoc(genesisDoc *types.GenesisDoc) (State, error) {
  114. state, err := store.Load()
  115. if err != nil {
  116. return State{}, err
  117. }
  118. if state.IsEmpty() {
  119. var err error
  120. state, err = MakeGenesisState(genesisDoc)
  121. if err != nil {
  122. return state, err
  123. }
  124. }
  125. return state, nil
  126. }
  127. // LoadState loads the State from the database.
  128. func (store dbStore) Load() (State, error) {
  129. return store.loadState(stateKey)
  130. }
  131. func (store dbStore) loadState(key []byte) (state State, err error) {
  132. buf, err := store.db.Get(key)
  133. if err != nil {
  134. return state, err
  135. }
  136. if len(buf) == 0 {
  137. return state, nil
  138. }
  139. sp := new(tmstate.State)
  140. err = proto.Unmarshal(buf, sp)
  141. if err != nil {
  142. // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
  143. tmos.Exit(fmt.Sprintf(`LoadState: Data has been corrupted or its spec has changed:
  144. %v\n`, err))
  145. }
  146. sm, err := StateFromProto(sp)
  147. if err != nil {
  148. return state, err
  149. }
  150. return *sm, nil
  151. }
  152. // Save persists the State, the ValidatorsInfo, and the ConsensusParamsInfo to the database.
  153. // This flushes the writes (e.g. calls SetSync).
  154. func (store dbStore) Save(state State) error {
  155. return store.save(state, stateKey)
  156. }
  157. func (store dbStore) save(state State, key []byte) error {
  158. batch := store.db.NewBatch()
  159. defer batch.Close()
  160. nextHeight := state.LastBlockHeight + 1
  161. // If first block, save validators for the block.
  162. if nextHeight == 1 {
  163. nextHeight = state.InitialHeight
  164. // This extra logic due to Tendermint validator set changes being delayed 1 block.
  165. // It may get overwritten due to InitChain validator updates.
  166. if err := store.saveValidatorsInfo(nextHeight, nextHeight, state.Validators, batch); err != nil {
  167. return err
  168. }
  169. }
  170. // Save next validators.
  171. err := store.saveValidatorsInfo(nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators, batch)
  172. if err != nil {
  173. return err
  174. }
  175. // Save next consensus params.
  176. if err := store.saveConsensusParamsInfo(nextHeight,
  177. state.LastHeightConsensusParamsChanged, state.ConsensusParams, batch); err != nil {
  178. return err
  179. }
  180. if err := batch.Set(key, state.Bytes()); err != nil {
  181. return err
  182. }
  183. return batch.WriteSync()
  184. }
  185. // BootstrapState saves a new state, used e.g. by state sync when starting from non-zero height.
  186. func (store dbStore) Bootstrap(state State) error {
  187. height := state.LastBlockHeight + 1
  188. if height == 1 {
  189. height = state.InitialHeight
  190. }
  191. batch := store.db.NewBatch()
  192. defer batch.Close()
  193. if height > 1 && !state.LastValidators.IsNilOrEmpty() {
  194. if err := store.saveValidatorsInfo(height-1, height-1, state.LastValidators, batch); err != nil {
  195. return err
  196. }
  197. }
  198. if err := store.saveValidatorsInfo(height, height, state.Validators, batch); err != nil {
  199. return err
  200. }
  201. if err := store.saveValidatorsInfo(height+1, height+1, state.NextValidators, batch); err != nil {
  202. return err
  203. }
  204. if err := store.saveConsensusParamsInfo(height,
  205. state.LastHeightConsensusParamsChanged, state.ConsensusParams, batch); err != nil {
  206. return err
  207. }
  208. if err := batch.Set(stateKey, state.Bytes()); err != nil {
  209. return err
  210. }
  211. return batch.WriteSync()
  212. }
  213. // PruneStates deletes states up to the height specified (exclusive). It is not
  214. // guaranteed to delete all states, since the last checkpointed state and states being pointed to by
  215. // e.g. `LastHeightChanged` must remain. The state at retain height must also exist.
  216. // Pruning is done in descending order.
  217. func (store dbStore) PruneStates(retainHeight int64) error {
  218. if retainHeight <= 0 {
  219. return fmt.Errorf("height %v must be greater than 0", retainHeight)
  220. }
  221. // NOTE: We need to prune consensus params first because the validator
  222. // sets have always one extra height. If validator sets were pruned first
  223. // we could get a situation where we prune up to the last validator set
  224. // yet don't have the respective consensus params at that height and thus
  225. // return an error
  226. if err := store.pruneConsensusParams(retainHeight); err != nil {
  227. return err
  228. }
  229. if err := store.pruneValidatorSets(retainHeight); err != nil {
  230. return err
  231. }
  232. if err := store.pruneABCIResponses(retainHeight); err != nil {
  233. return err
  234. }
  235. return nil
  236. }
  237. // pruneValidatorSets calls a reverse iterator from base height to retain height (exclusive), deleting
  238. // all validator sets in between. Due to the fact that most validator sets stored reference an earlier
  239. // validator set, it is likely that there will remain one validator set left after pruning.
  240. func (store dbStore) pruneValidatorSets(retainHeight int64) error {
  241. valInfo, err := loadValidatorsInfo(store.db, retainHeight)
  242. if err != nil {
  243. return fmt.Errorf("validators at height %v not found: %w", retainHeight, err)
  244. }
  245. // We will prune up to the validator set at the given "height". As we don't save validator sets every
  246. // height but only when they change or at a check point, it is likely that the validator set at the height
  247. // we prune to is empty and thus dependent on the validator set saved at a previous height. We must find
  248. // that validator set and make sure it is not pruned.
  249. lastRecordedValSetHeight := lastStoredHeightFor(retainHeight, valInfo.LastHeightChanged)
  250. lastRecordedValSet, err := loadValidatorsInfo(store.db, lastRecordedValSetHeight)
  251. if err != nil || lastRecordedValSet.ValidatorSet == nil {
  252. return fmt.Errorf("couldn't find validators at height %d (height %d was originally requested): %w",
  253. lastStoredHeightFor(retainHeight, valInfo.LastHeightChanged),
  254. retainHeight,
  255. err,
  256. )
  257. }
  258. // if this is not equal to the retain height, prune from the retain height to the height above
  259. // the last saved validator set. This way we can skip over the dependent validator set.
  260. if lastRecordedValSetHeight < retainHeight {
  261. err := store.pruneRange(
  262. validatorsKey(lastRecordedValSetHeight+1),
  263. validatorsKey(retainHeight),
  264. )
  265. if err != nil {
  266. return err
  267. }
  268. }
  269. // prune all the validators sets up to last saved validator set
  270. return store.pruneRange(
  271. validatorsKey(1),
  272. validatorsKey(lastRecordedValSetHeight),
  273. )
  274. }
  275. // pruneConsensusParams calls a reverse iterator from base height to retain height batch deleting
  276. // all consensus params in between. If the consensus params at the new base height is dependent
  277. // on a prior height then this will keep that lower height too.
  278. func (store dbStore) pruneConsensusParams(retainHeight int64) error {
  279. paramsInfo, err := store.loadConsensusParamsInfo(retainHeight)
  280. if err != nil {
  281. return fmt.Errorf("consensus params at height %v not found: %w", retainHeight, err)
  282. }
  283. // As we don't save the consensus params at every height, only when there is a consensus params change,
  284. // we must not prune (or save) the last consensus params that the consensus params info at height
  285. // is dependent on.
  286. if paramsInfo.ConsensusParams.Equal(&tmproto.ConsensusParams{}) {
  287. // sanity check that the consensus params at the last height it was changed is there
  288. lastRecordedConsensusParams, err := store.loadConsensusParamsInfo(paramsInfo.LastHeightChanged)
  289. if err != nil || lastRecordedConsensusParams.ConsensusParams.Equal(&tmproto.ConsensusParams{}) {
  290. return fmt.Errorf(
  291. "couldn't find consensus params at height %d (height %d was originally requested): %w",
  292. paramsInfo.LastHeightChanged,
  293. retainHeight,
  294. err,
  295. )
  296. }
  297. // prune the params above the height with which it last changed and below the retain height.
  298. err = store.pruneRange(
  299. consensusParamsKey(paramsInfo.LastHeightChanged+1),
  300. consensusParamsKey(retainHeight),
  301. )
  302. if err != nil {
  303. return err
  304. }
  305. }
  306. // prune all the consensus params up to either the last height the params changed or if the params
  307. // last changed at the retain height, then up to the retain height.
  308. return store.pruneRange(
  309. consensusParamsKey(1),
  310. consensusParamsKey(paramsInfo.LastHeightChanged),
  311. )
  312. }
  313. // pruneABCIResponses calls a reverse iterator from base height to retain height batch deleting
  314. // all abci responses in between
  315. func (store dbStore) pruneABCIResponses(height int64) error {
  316. return store.pruneRange(abciResponsesKey(1), abciResponsesKey(height))
  317. }
  318. // pruneRange is a generic function for deleting a range of keys in reverse order.
  319. // we keep filling up batches of at most 1000 keys, perform a deletion and continue until
  320. // we have gone through all of keys in the range. This avoids doing any writes whilst
  321. // iterating.
  322. func (store dbStore) pruneRange(start []byte, end []byte) error {
  323. var err error
  324. batch := store.db.NewBatch()
  325. defer batch.Close()
  326. end, err = store.reverseBatchDelete(batch, start, end)
  327. if err != nil {
  328. return err
  329. }
  330. // iterate until the last batch of the pruning range in which case we will perform a
  331. // write sync
  332. for !bytes.Equal(start, end) {
  333. if err := batch.Write(); err != nil {
  334. return err
  335. }
  336. if err := batch.Close(); err != nil {
  337. return err
  338. }
  339. batch = store.db.NewBatch()
  340. // fill a new batch of keys for deletion over the remainding range
  341. end, err = store.reverseBatchDelete(batch, start, end)
  342. if err != nil {
  343. return err
  344. }
  345. }
  346. return batch.WriteSync()
  347. }
  348. // reverseBatchDelete runs a reverse iterator (from end to start) filling up a batch until either
  349. // (a) the iterator reaches the start or (b) the iterator has added a 1000 keys (this avoids the
  350. // batch from growing too large)
  351. func (store dbStore) reverseBatchDelete(batch dbm.Batch, start, end []byte) ([]byte, error) {
  352. iter, err := store.db.ReverseIterator(start, end)
  353. if err != nil {
  354. return end, fmt.Errorf("iterator error: %w", err)
  355. }
  356. defer iter.Close()
  357. size := 0
  358. for ; iter.Valid(); iter.Next() {
  359. if err := batch.Delete(iter.Key()); err != nil {
  360. return end, fmt.Errorf("pruning error at key %X: %w", iter.Key(), err)
  361. }
  362. // avoid batches growing too large by capping them
  363. size++
  364. if size == 1000 {
  365. return iter.Key(), iter.Error()
  366. }
  367. }
  368. return start, iter.Error()
  369. }
  370. //------------------------------------------------------------------------
  371. // ABCIResponsesResultsHash returns the root hash of a Merkle tree of
  372. // ResponseDeliverTx responses (see ABCIResults.Hash)
  373. //
  374. // See merkle.SimpleHashFromByteSlices
  375. func ABCIResponsesResultsHash(ar *tmstate.ABCIResponses) []byte {
  376. return types.NewResults(ar.DeliverTxs).Hash()
  377. }
  378. // LoadABCIResponses loads the ABCIResponses for the given height from the
  379. // database. If not found, ErrNoABCIResponsesForHeight is returned.
  380. //
  381. // This is useful for recovering from crashes where we called app.Commit and
  382. // before we called s.Save(). It can also be used to produce Merkle proofs of
  383. // the result of txs.
  384. func (store dbStore) LoadABCIResponses(height int64) (*tmstate.ABCIResponses, error) {
  385. buf, err := store.db.Get(abciResponsesKey(height))
  386. if err != nil {
  387. return nil, err
  388. }
  389. if len(buf) == 0 {
  390. return nil, ErrNoABCIResponsesForHeight{height}
  391. }
  392. abciResponses := new(tmstate.ABCIResponses)
  393. err = abciResponses.Unmarshal(buf)
  394. if err != nil {
  395. // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
  396. tmos.Exit(fmt.Sprintf(`LoadABCIResponses: Data has been corrupted or its spec has
  397. changed: %v\n`, err))
  398. }
  399. // TODO: ensure that buf is completely read.
  400. return abciResponses, nil
  401. }
  402. // SaveABCIResponses persists the ABCIResponses to the database.
  403. // This is useful in case we crash after app.Commit and before s.Save().
  404. // Responses are indexed by height so they can also be loaded later to produce
  405. // Merkle proofs.
  406. //
  407. // Exposed for testing.
  408. func (store dbStore) SaveABCIResponses(height int64, abciResponses *tmstate.ABCIResponses) error {
  409. return store.saveABCIResponses(height, abciResponses)
  410. }
  411. func (store dbStore) saveABCIResponses(height int64, abciResponses *tmstate.ABCIResponses) error {
  412. var dtxs []*abci.ResponseDeliverTx
  413. // strip nil values,
  414. for _, tx := range abciResponses.DeliverTxs {
  415. if tx != nil {
  416. dtxs = append(dtxs, tx)
  417. }
  418. }
  419. abciResponses.DeliverTxs = dtxs
  420. bz, err := abciResponses.Marshal()
  421. if err != nil {
  422. return err
  423. }
  424. return store.db.SetSync(abciResponsesKey(height), bz)
  425. }
  426. //-----------------------------------------------------------------------------
  427. // LoadValidators loads the ValidatorSet for a given height.
  428. // Returns ErrNoValSetForHeight if the validator set can't be found for this height.
  429. func (store dbStore) LoadValidators(height int64) (*types.ValidatorSet, error) {
  430. valInfo, err := loadValidatorsInfo(store.db, height)
  431. if err != nil {
  432. return nil, ErrNoValSetForHeight{height}
  433. }
  434. if valInfo.ValidatorSet == nil {
  435. lastStoredHeight := lastStoredHeightFor(height, valInfo.LastHeightChanged)
  436. valInfo2, err := loadValidatorsInfo(store.db, lastStoredHeight)
  437. if err != nil || valInfo2.ValidatorSet == nil {
  438. return nil,
  439. fmt.Errorf("couldn't find validators at height %d (height %d was originally requested): %w",
  440. lastStoredHeight,
  441. height,
  442. err,
  443. )
  444. }
  445. vs, err := types.ValidatorSetFromProto(valInfo2.ValidatorSet)
  446. if err != nil {
  447. return nil, err
  448. }
  449. vs.IncrementProposerPriority(tmmath.SafeConvertInt32(height - lastStoredHeight)) // mutate
  450. vi2, err := vs.ToProto()
  451. if err != nil {
  452. return nil, err
  453. }
  454. valInfo2.ValidatorSet = vi2
  455. valInfo = valInfo2
  456. }
  457. vip, err := types.ValidatorSetFromProto(valInfo.ValidatorSet)
  458. if err != nil {
  459. return nil, err
  460. }
  461. return vip, nil
  462. }
  463. func lastStoredHeightFor(height, lastHeightChanged int64) int64 {
  464. checkpointHeight := height - height%valSetCheckpointInterval
  465. return tmmath.MaxInt64(checkpointHeight, lastHeightChanged)
  466. }
  467. // CONTRACT: Returned ValidatorsInfo can be mutated.
  468. func loadValidatorsInfo(db dbm.DB, height int64) (*tmstate.ValidatorsInfo, error) {
  469. buf, err := db.Get(validatorsKey(height))
  470. if err != nil {
  471. return nil, err
  472. }
  473. if len(buf) == 0 {
  474. return nil, errors.New("value retrieved from db is empty")
  475. }
  476. v := new(tmstate.ValidatorsInfo)
  477. err = v.Unmarshal(buf)
  478. if err != nil {
  479. // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
  480. tmos.Exit(fmt.Sprintf(`LoadValidators: Data has been corrupted or its spec has changed:
  481. %v\n`, err))
  482. }
  483. // TODO: ensure that buf is completely read.
  484. return v, nil
  485. }
  486. // saveValidatorsInfo persists the validator set.
  487. //
  488. // `height` is the effective height for which the validator is responsible for
  489. // signing. It should be called from s.Save(), right before the state itself is
  490. // persisted.
  491. func (store dbStore) saveValidatorsInfo(
  492. height, lastHeightChanged int64,
  493. valSet *types.ValidatorSet,
  494. batch dbm.Batch,
  495. ) error {
  496. if lastHeightChanged > height {
  497. return errors.New("lastHeightChanged cannot be greater than ValidatorsInfo height")
  498. }
  499. valInfo := &tmstate.ValidatorsInfo{
  500. LastHeightChanged: lastHeightChanged,
  501. }
  502. // Only persist validator set if it was updated or checkpoint height (see
  503. // valSetCheckpointInterval) is reached.
  504. if height == lastHeightChanged || height%valSetCheckpointInterval == 0 {
  505. pv, err := valSet.ToProto()
  506. if err != nil {
  507. return err
  508. }
  509. valInfo.ValidatorSet = pv
  510. }
  511. bz, err := valInfo.Marshal()
  512. if err != nil {
  513. return err
  514. }
  515. err = batch.Set(validatorsKey(height), bz)
  516. if err != nil {
  517. return err
  518. }
  519. return nil
  520. }
  521. //-----------------------------------------------------------------------------
  522. // ConsensusParamsInfo represents the latest consensus params, or the last height it changed
  523. // Allocate empty Consensus params at compile time to avoid multiple allocations during runtime
  524. var (
  525. empty = types.ConsensusParams{}
  526. emptypb = tmproto.ConsensusParams{}
  527. )
  528. // LoadConsensusParams loads the ConsensusParams for a given height.
  529. func (store dbStore) LoadConsensusParams(height int64) (types.ConsensusParams, error) {
  530. paramsInfo, err := store.loadConsensusParamsInfo(height)
  531. if err != nil {
  532. return empty, fmt.Errorf("could not find consensus params for height #%d: %w", height, err)
  533. }
  534. if paramsInfo.ConsensusParams.Equal(&emptypb) {
  535. paramsInfo2, err := store.loadConsensusParamsInfo(paramsInfo.LastHeightChanged)
  536. if err != nil {
  537. return empty, fmt.Errorf(
  538. "couldn't find consensus params at height %d (height %d was originally requested): %w",
  539. paramsInfo.LastHeightChanged,
  540. height,
  541. err,
  542. )
  543. }
  544. paramsInfo = paramsInfo2
  545. }
  546. return types.ConsensusParamsFromProto(paramsInfo.ConsensusParams), nil
  547. }
  548. func (store dbStore) loadConsensusParamsInfo(height int64) (*tmstate.ConsensusParamsInfo, error) {
  549. buf, err := store.db.Get(consensusParamsKey(height))
  550. if err != nil {
  551. return nil, err
  552. }
  553. if len(buf) == 0 {
  554. return nil, errors.New("value retrieved from db is empty")
  555. }
  556. paramsInfo := new(tmstate.ConsensusParamsInfo)
  557. if err = paramsInfo.Unmarshal(buf); err != nil {
  558. // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
  559. tmos.Exit(fmt.Sprintf(`LoadConsensusParams: Data has been corrupted or its spec has changed:
  560. %v\n`, err))
  561. }
  562. // TODO: ensure that buf is completely read.
  563. return paramsInfo, nil
  564. }
  565. // saveConsensusParamsInfo persists the consensus params for the next block to disk.
  566. // It should be called from s.Save(), right before the state itself is persisted.
  567. // If the consensus params did not change after processing the latest block,
  568. // only the last height for which they changed is persisted.
  569. func (store dbStore) saveConsensusParamsInfo(
  570. nextHeight, changeHeight int64,
  571. params types.ConsensusParams,
  572. batch dbm.Batch,
  573. ) error {
  574. paramsInfo := &tmstate.ConsensusParamsInfo{
  575. LastHeightChanged: changeHeight,
  576. }
  577. if changeHeight == nextHeight {
  578. paramsInfo.ConsensusParams = params.ToProto()
  579. }
  580. bz, err := paramsInfo.Marshal()
  581. if err != nil {
  582. return err
  583. }
  584. err = batch.Set(consensusParamsKey(nextHeight), bz)
  585. if err != nil {
  586. return err
  587. }
  588. return nil
  589. }