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.

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