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.

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