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.

327 lines
10 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
  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. DeliverTxs []*abci.ResponseDeliverTx `json:"deliver_txs"`
  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, len(block.Data.Txs))
  106. if len(block.Data.Txs) == 0 {
  107. // This makes Amino encoding/decoding consistent.
  108. resDeliverTxs = nil
  109. }
  110. return &ABCIResponses{
  111. DeliverTxs: 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.DeliverTxs)
  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
  143. // Merkle proofs.
  144. //
  145. // Exposed for testing.
  146. func SaveABCIResponses(db dbm.DB, height int64, abciResponses *ABCIResponses) {
  147. db.SetSync(calcABCIResponsesKey(height), abciResponses.Bytes())
  148. }
  149. //-----------------------------------------------------------------------------
  150. // ValidatorsInfo represents the latest validator set, or the last height it changed
  151. type ValidatorsInfo struct {
  152. ValidatorSet *types.ValidatorSet
  153. LastHeightChanged int64
  154. }
  155. // Bytes serializes the ValidatorsInfo using go-amino.
  156. func (valInfo *ValidatorsInfo) Bytes() []byte {
  157. return cdc.MustMarshalBinaryBare(valInfo)
  158. }
  159. // LoadValidators loads the ValidatorSet for a given height.
  160. // Returns ErrNoValSetForHeight if the validator set can't be found for this height.
  161. func LoadValidators(db dbm.DB, height int64) (*types.ValidatorSet, error) {
  162. valInfo := loadValidatorsInfo(db, height)
  163. if valInfo == nil {
  164. return nil, ErrNoValSetForHeight{height}
  165. }
  166. if valInfo.ValidatorSet == nil {
  167. lastStoredHeight := lastStoredHeightFor(height, valInfo.LastHeightChanged)
  168. valInfo2 := loadValidatorsInfo(db, lastStoredHeight)
  169. if valInfo2 == nil || valInfo2.ValidatorSet == nil {
  170. panic(
  171. fmt.Sprintf("Couldn't find validators at height %d (height %d was originally requested)",
  172. lastStoredHeight,
  173. height,
  174. ),
  175. )
  176. }
  177. valInfo2.ValidatorSet.IncrementProposerPriority(int(height - lastStoredHeight)) // mutate
  178. valInfo = valInfo2
  179. }
  180. return valInfo.ValidatorSet, nil
  181. }
  182. func lastStoredHeightFor(height, lastHeightChanged int64) int64 {
  183. checkpointHeight := height - height%valSetCheckpointInterval
  184. return cmn.MaxInt64(checkpointHeight, lastHeightChanged)
  185. }
  186. // CONTRACT: Returned ValidatorsInfo can be mutated.
  187. func loadValidatorsInfo(db dbm.DB, height int64) *ValidatorsInfo {
  188. buf := db.Get(calcValidatorsKey(height))
  189. if len(buf) == 0 {
  190. return nil
  191. }
  192. v := new(ValidatorsInfo)
  193. err := cdc.UnmarshalBinaryBare(buf, v)
  194. if err != nil {
  195. // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
  196. cmn.Exit(fmt.Sprintf(`LoadValidators: Data has been corrupted or its spec has changed:
  197. %v\n`, err))
  198. }
  199. // TODO: ensure that buf is completely read.
  200. return v
  201. }
  202. // saveValidatorsInfo persists the validator set.
  203. //
  204. // `height` is the effective height for which the validator is responsible for
  205. // signing. It should be called from s.Save(), right before the state itself is
  206. // persisted.
  207. func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) {
  208. if lastHeightChanged > height {
  209. panic("LastHeightChanged cannot be greater than ValidatorsInfo height")
  210. }
  211. valInfo := &ValidatorsInfo{
  212. LastHeightChanged: lastHeightChanged,
  213. }
  214. // Only persist validator set if it was updated or checkpoint height (see
  215. // valSetCheckpointInterval) is reached.
  216. if height == lastHeightChanged || height%valSetCheckpointInterval == 0 {
  217. valInfo.ValidatorSet = valSet
  218. }
  219. db.Set(calcValidatorsKey(height), valInfo.Bytes())
  220. }
  221. //-----------------------------------------------------------------------------
  222. // ConsensusParamsInfo represents the latest consensus params, or the last height it changed
  223. type ConsensusParamsInfo struct {
  224. ConsensusParams types.ConsensusParams
  225. LastHeightChanged int64
  226. }
  227. // Bytes serializes the ConsensusParamsInfo using go-amino.
  228. func (params ConsensusParamsInfo) Bytes() []byte {
  229. return cdc.MustMarshalBinaryBare(params)
  230. }
  231. // LoadConsensusParams loads the ConsensusParams for a given height.
  232. func LoadConsensusParams(db dbm.DB, height int64) (types.ConsensusParams, error) {
  233. empty := types.ConsensusParams{}
  234. paramsInfo := loadConsensusParamsInfo(db, height)
  235. if paramsInfo == nil {
  236. return empty, ErrNoConsensusParamsForHeight{height}
  237. }
  238. if paramsInfo.ConsensusParams.Equals(&empty) {
  239. paramsInfo2 := loadConsensusParamsInfo(db, paramsInfo.LastHeightChanged)
  240. if paramsInfo2 == nil {
  241. panic(
  242. fmt.Sprintf(
  243. "Couldn't find consensus params at height %d as last changed from height %d",
  244. paramsInfo.LastHeightChanged,
  245. height,
  246. ),
  247. )
  248. }
  249. paramsInfo = paramsInfo2
  250. }
  251. return paramsInfo.ConsensusParams, nil
  252. }
  253. func loadConsensusParamsInfo(db dbm.DB, height int64) *ConsensusParamsInfo {
  254. buf := db.Get(calcConsensusParamsKey(height))
  255. if len(buf) == 0 {
  256. return nil
  257. }
  258. paramsInfo := new(ConsensusParamsInfo)
  259. err := cdc.UnmarshalBinaryBare(buf, paramsInfo)
  260. if err != nil {
  261. // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
  262. cmn.Exit(fmt.Sprintf(`LoadConsensusParams: Data has been corrupted or its spec has changed:
  263. %v\n`, err))
  264. }
  265. // TODO: ensure that buf is completely read.
  266. return paramsInfo
  267. }
  268. // saveConsensusParamsInfo persists the consensus params for the next block to disk.
  269. // It should be called from s.Save(), right before the state itself is persisted.
  270. // If the consensus params did not change after processing the latest block,
  271. // only the last height for which they changed is persisted.
  272. func saveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params types.ConsensusParams) {
  273. paramsInfo := &ConsensusParamsInfo{
  274. LastHeightChanged: changeHeight,
  275. }
  276. if changeHeight == nextHeight {
  277. paramsInfo.ConsensusParams = params
  278. }
  279. db.Set(calcConsensusParamsKey(nextHeight), paramsInfo.Bytes())
  280. }