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.

333 lines
11 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. package state
  2. import (
  3. "fmt"
  4. abci "github.com/tendermint/tendermint/abci/types"
  5. cmn "github.com/tendermint/tendermint/libs/common"
  6. "github.com/tendermint/tendermint/types"
  7. dbm "github.com/tendermint/tm-db"
  8. )
  9. const (
  10. // persist validators every valSetCheckpointInterval blocks to avoid
  11. // LoadValidators taking too much time.
  12. // https://github.com/tendermint/tendermint/pull/3438
  13. // 100000 results in ~ 100ms to get 100 validators (see BenchmarkLoadValidators)
  14. valSetCheckpointInterval = 100000
  15. )
  16. //------------------------------------------------------------------------
  17. func calcValidatorsKey(height int64) []byte {
  18. return []byte(fmt.Sprintf("validatorsKey:%v", height))
  19. }
  20. func calcConsensusParamsKey(height int64) []byte {
  21. return []byte(fmt.Sprintf("consensusParamsKey:%v", height))
  22. }
  23. func calcABCIResponsesKey(height int64) []byte {
  24. return []byte(fmt.Sprintf("abciResponsesKey:%v", height))
  25. }
  26. // LoadStateFromDBOrGenesisFile loads the most recent state from the database,
  27. // or creates a new one from the given genesisFilePath and persists the result
  28. // to the database.
  29. func LoadStateFromDBOrGenesisFile(stateDB dbm.DB, genesisFilePath string) (State, error) {
  30. state := LoadState(stateDB)
  31. if state.IsEmpty() {
  32. var err error
  33. state, err = MakeGenesisStateFromFile(genesisFilePath)
  34. if err != nil {
  35. return state, err
  36. }
  37. SaveState(stateDB, state)
  38. }
  39. return state, nil
  40. }
  41. // LoadStateFromDBOrGenesisDoc loads the most recent state from the database,
  42. // or creates a new one from the given genesisDoc and persists the result
  43. // to the database.
  44. func LoadStateFromDBOrGenesisDoc(stateDB dbm.DB, genesisDoc *types.GenesisDoc) (State, error) {
  45. state := LoadState(stateDB)
  46. if state.IsEmpty() {
  47. var err error
  48. state, err = MakeGenesisState(genesisDoc)
  49. if err != nil {
  50. return state, err
  51. }
  52. SaveState(stateDB, state)
  53. }
  54. return state, nil
  55. }
  56. // LoadState loads the State from the database.
  57. func LoadState(db dbm.DB) State {
  58. return loadState(db, stateKey)
  59. }
  60. func loadState(db dbm.DB, key []byte) (state State) {
  61. buf := db.Get(key)
  62. if len(buf) == 0 {
  63. return state
  64. }
  65. err := cdc.UnmarshalBinaryBare(buf, &state)
  66. if err != nil {
  67. // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
  68. cmn.Exit(fmt.Sprintf(`LoadState: Data has been corrupted or its spec has changed:
  69. %v\n`, err))
  70. }
  71. // TODO: ensure that buf is completely read.
  72. return state
  73. }
  74. // SaveState persists the State, the ValidatorsInfo, and the ConsensusParamsInfo to the database.
  75. // This flushes the writes (e.g. calls SetSync).
  76. func SaveState(db dbm.DB, state State) {
  77. saveState(db, state, stateKey)
  78. }
  79. func saveState(db dbm.DB, state State, key []byte) {
  80. nextHeight := state.LastBlockHeight + 1
  81. // If first block, save validators for block 1.
  82. if nextHeight == 1 {
  83. // This extra logic due to Tendermint validator set changes being delayed 1 block.
  84. // It may get overwritten due to InitChain validator updates.
  85. lastHeightVoteChanged := int64(1)
  86. saveValidatorsInfo(db, nextHeight, lastHeightVoteChanged, state.Validators)
  87. }
  88. // Save next validators.
  89. saveValidatorsInfo(db, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
  90. // Save next consensus params.
  91. saveConsensusParamsInfo(db, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams)
  92. db.SetSync(key, state.Bytes())
  93. }
  94. //------------------------------------------------------------------------
  95. // ABCIResponses retains the responses
  96. // of the various ABCI calls during block processing.
  97. // It is persisted to disk for each height before calling Commit.
  98. type ABCIResponses struct {
  99. DeliverTx []*abci.ResponseDeliverTx `json:"deliver_tx"`
  100. EndBlock *abci.ResponseEndBlock `json:"end_block"`
  101. BeginBlock *abci.ResponseBeginBlock `json:"begin_block"`
  102. }
  103. // NewABCIResponses returns a new ABCIResponses
  104. func NewABCIResponses(block *types.Block) *ABCIResponses {
  105. resDeliverTxs := make([]*abci.ResponseDeliverTx, block.NumTxs)
  106. if block.NumTxs == 0 {
  107. // This makes Amino encoding/decoding consistent.
  108. resDeliverTxs = nil
  109. }
  110. return &ABCIResponses{
  111. DeliverTx: resDeliverTxs,
  112. }
  113. }
  114. // Bytes serializes the ABCIResponse using go-amino.
  115. func (arz *ABCIResponses) Bytes() []byte {
  116. return cdc.MustMarshalBinaryBare(arz)
  117. }
  118. func (arz *ABCIResponses) ResultsHash() []byte {
  119. results := types.NewResults(arz.DeliverTx)
  120. return results.Hash()
  121. }
  122. // LoadABCIResponses loads the ABCIResponses for the given height from the database.
  123. // This is useful for recovering from crashes where we called app.Commit and before we called
  124. // s.Save(). It can also be used to produce Merkle proofs of the result of txs.
  125. func LoadABCIResponses(db dbm.DB, height int64) (*ABCIResponses, error) {
  126. buf := db.Get(calcABCIResponsesKey(height))
  127. if len(buf) == 0 {
  128. return nil, ErrNoABCIResponsesForHeight{height}
  129. }
  130. abciResponses := new(ABCIResponses)
  131. err := cdc.UnmarshalBinaryBare(buf, abciResponses)
  132. if err != nil {
  133. // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
  134. cmn.Exit(fmt.Sprintf(`LoadABCIResponses: Data has been corrupted or its spec has
  135. changed: %v\n`, err))
  136. }
  137. // TODO: ensure that buf is completely read.
  138. return abciResponses, nil
  139. }
  140. // SaveABCIResponses persists the ABCIResponses to the database.
  141. // This is useful in case we crash after app.Commit and before s.Save().
  142. // Responses are indexed by height so they can also be loaded later to produce Merkle proofs.
  143. func saveABCIResponses(db dbm.DB, height int64, abciResponses *ABCIResponses) {
  144. db.SetSync(calcABCIResponsesKey(height), abciResponses.Bytes())
  145. }
  146. //-----------------------------------------------------------------------------
  147. // ValidatorsInfo represents the latest validator set, or the last height it changed
  148. type ValidatorsInfo struct {
  149. ValidatorSet *types.ValidatorSet
  150. LastHeightChanged int64
  151. }
  152. // Bytes serializes the ValidatorsInfo using go-amino.
  153. func (valInfo *ValidatorsInfo) Bytes() []byte {
  154. return cdc.MustMarshalBinaryBare(valInfo)
  155. }
  156. // LoadValidators loads the ValidatorSet for a given height.
  157. // Returns ErrNoValSetForHeight if the validator set can't be found for this height.
  158. func LoadValidators(db dbm.DB, height int64) (*types.ValidatorSet, error) {
  159. valInfo := loadValidatorsInfo(db, height)
  160. if valInfo == nil {
  161. return nil, ErrNoValSetForHeight{height}
  162. }
  163. if valInfo.ValidatorSet == nil {
  164. lastStoredHeight := lastStoredHeightFor(height, valInfo.LastHeightChanged)
  165. valInfo2 := loadValidatorsInfo(db, lastStoredHeight)
  166. if valInfo2 == nil || valInfo2.ValidatorSet == nil {
  167. // TODO (melekes): remove the below if condition in the 0.33 major
  168. // release and just panic. Old chains might panic otherwise if they
  169. // haven't saved validators at intermediate (%valSetCheckpointInterval)
  170. // height yet.
  171. // https://github.com/tendermint/tendermint/issues/3543
  172. valInfo2 = loadValidatorsInfo(db, valInfo.LastHeightChanged)
  173. lastStoredHeight = valInfo.LastHeightChanged
  174. if valInfo2 == nil || valInfo2.ValidatorSet == nil {
  175. panic(
  176. fmt.Sprintf("Couldn't find validators at height %d (height %d was originally requested)",
  177. lastStoredHeight,
  178. height,
  179. ),
  180. )
  181. }
  182. }
  183. valInfo2.ValidatorSet.IncrementProposerPriority(int(height - lastStoredHeight)) // mutate
  184. valInfo = valInfo2
  185. }
  186. return valInfo.ValidatorSet, nil
  187. }
  188. func lastStoredHeightFor(height, lastHeightChanged int64) int64 {
  189. checkpointHeight := height - height%valSetCheckpointInterval
  190. return cmn.MaxInt64(checkpointHeight, lastHeightChanged)
  191. }
  192. // CONTRACT: Returned ValidatorsInfo can be mutated.
  193. func loadValidatorsInfo(db dbm.DB, height int64) *ValidatorsInfo {
  194. buf := db.Get(calcValidatorsKey(height))
  195. if len(buf) == 0 {
  196. return nil
  197. }
  198. v := new(ValidatorsInfo)
  199. err := cdc.UnmarshalBinaryBare(buf, v)
  200. if err != nil {
  201. // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
  202. cmn.Exit(fmt.Sprintf(`LoadValidators: Data has been corrupted or its spec has changed:
  203. %v\n`, err))
  204. }
  205. // TODO: ensure that buf is completely read.
  206. return v
  207. }
  208. // saveValidatorsInfo persists the validator set.
  209. //
  210. // `height` is the effective height for which the validator is responsible for
  211. // signing. It should be called from s.Save(), right before the state itself is
  212. // persisted.
  213. func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) {
  214. if lastHeightChanged > height {
  215. panic("LastHeightChanged cannot be greater than ValidatorsInfo height")
  216. }
  217. valInfo := &ValidatorsInfo{
  218. LastHeightChanged: lastHeightChanged,
  219. }
  220. // Only persist validator set if it was updated or checkpoint height (see
  221. // valSetCheckpointInterval) is reached.
  222. if height == lastHeightChanged || height%valSetCheckpointInterval == 0 {
  223. valInfo.ValidatorSet = valSet
  224. }
  225. db.Set(calcValidatorsKey(height), valInfo.Bytes())
  226. }
  227. //-----------------------------------------------------------------------------
  228. // ConsensusParamsInfo represents the latest consensus params, or the last height it changed
  229. type ConsensusParamsInfo struct {
  230. ConsensusParams types.ConsensusParams
  231. LastHeightChanged int64
  232. }
  233. // Bytes serializes the ConsensusParamsInfo using go-amino.
  234. func (params ConsensusParamsInfo) Bytes() []byte {
  235. return cdc.MustMarshalBinaryBare(params)
  236. }
  237. // LoadConsensusParams loads the ConsensusParams for a given height.
  238. func LoadConsensusParams(db dbm.DB, height int64) (types.ConsensusParams, error) {
  239. empty := types.ConsensusParams{}
  240. paramsInfo := loadConsensusParamsInfo(db, height)
  241. if paramsInfo == nil {
  242. return empty, ErrNoConsensusParamsForHeight{height}
  243. }
  244. if paramsInfo.ConsensusParams.Equals(&empty) {
  245. paramsInfo2 := loadConsensusParamsInfo(db, paramsInfo.LastHeightChanged)
  246. if paramsInfo2 == nil {
  247. panic(
  248. fmt.Sprintf(
  249. "Couldn't find consensus params at height %d as last changed from height %d",
  250. paramsInfo.LastHeightChanged,
  251. height,
  252. ),
  253. )
  254. }
  255. paramsInfo = paramsInfo2
  256. }
  257. return paramsInfo.ConsensusParams, nil
  258. }
  259. func loadConsensusParamsInfo(db dbm.DB, height int64) *ConsensusParamsInfo {
  260. buf := db.Get(calcConsensusParamsKey(height))
  261. if len(buf) == 0 {
  262. return nil
  263. }
  264. paramsInfo := new(ConsensusParamsInfo)
  265. err := cdc.UnmarshalBinaryBare(buf, paramsInfo)
  266. if err != nil {
  267. // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
  268. cmn.Exit(fmt.Sprintf(`LoadConsensusParams: Data has been corrupted or its spec has changed:
  269. %v\n`, err))
  270. }
  271. // TODO: ensure that buf is completely read.
  272. return paramsInfo
  273. }
  274. // saveConsensusParamsInfo persists the consensus params for the next block to disk.
  275. // It should be called from s.Save(), right before the state itself is persisted.
  276. // If the consensus params did not change after processing the latest block,
  277. // only the last height for which they changed is persisted.
  278. func saveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params types.ConsensusParams) {
  279. paramsInfo := &ConsensusParamsInfo{
  280. LastHeightChanged: changeHeight,
  281. }
  282. if changeHeight == nextHeight {
  283. paramsInfo.ConsensusParams = params
  284. }
  285. db.Set(calcConsensusParamsKey(nextHeight), paramsInfo.Bytes())
  286. }