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.

398 lines
13 KiB

node: refactor node.NewNode (#3456) The node.NewNode method is pretty complex at the moment, an in order to address issues like #3156, we need to simplify the interface for partial node instantiation. In some places, we don't need to build up a full node (like in the node.TestCreateProposalBlock test), but the complexity of such partial instantiation needs to be reduced. This PR aims to eventually make this easier/simpler. See also this gist https://gist.github.com/thanethomson/56e1640d057a26186e38ad678a1d114c for some background work done when starting to refactor here. ## Commits: * [WIP] Refactor node.NewNode to simplify The `node.NewNode` method is pretty complex at the moment, an in order to address issues like #3156, we need to simplify the interface for partial node instantiation. In some places, we don't need to build up a full node (like in the `node.TestCreateProposalBlock` test), but the complexity of such partial instantiation needs to be reduced. This PR aims to eventually make this easier/simpler. * Refactor state loading and genesis doc provider into state package * Refactor for clarity of return parameters * Fix incorrect capitalization of error messages * Simplify extracted functions' names * Document optionally-prefixed functions * Refactor optionallyFastSync for clarity of separation of concerns * Restructure function for early return * Restructure function for early return * Remove dependence on deprecated panic functions * refactor code a bit more plus, expose PEXReactor on node * align logger names * add a changelog entry * align logger names 2 * add a note about PEXReactor returning nil
6 years ago
node: refactor node.NewNode (#3456) The node.NewNode method is pretty complex at the moment, an in order to address issues like #3156, we need to simplify the interface for partial node instantiation. In some places, we don't need to build up a full node (like in the node.TestCreateProposalBlock test), but the complexity of such partial instantiation needs to be reduced. This PR aims to eventually make this easier/simpler. See also this gist https://gist.github.com/thanethomson/56e1640d057a26186e38ad678a1d114c for some background work done when starting to refactor here. ## Commits: * [WIP] Refactor node.NewNode to simplify The `node.NewNode` method is pretty complex at the moment, an in order to address issues like #3156, we need to simplify the interface for partial node instantiation. In some places, we don't need to build up a full node (like in the `node.TestCreateProposalBlock` test), but the complexity of such partial instantiation needs to be reduced. This PR aims to eventually make this easier/simpler. * Refactor state loading and genesis doc provider into state package * Refactor for clarity of return parameters * Fix incorrect capitalization of error messages * Simplify extracted functions' names * Document optionally-prefixed functions * Refactor optionallyFastSync for clarity of separation of concerns * Restructure function for early return * Restructure function for early return * Remove dependence on deprecated panic functions * refactor code a bit more plus, expose PEXReactor on node * align logger names * add a changelog entry * align logger names 2 * add a note about PEXReactor returning nil
6 years ago
node: refactor node.NewNode (#3456) The node.NewNode method is pretty complex at the moment, an in order to address issues like #3156, we need to simplify the interface for partial node instantiation. In some places, we don't need to build up a full node (like in the node.TestCreateProposalBlock test), but the complexity of such partial instantiation needs to be reduced. This PR aims to eventually make this easier/simpler. See also this gist https://gist.github.com/thanethomson/56e1640d057a26186e38ad678a1d114c for some background work done when starting to refactor here. ## Commits: * [WIP] Refactor node.NewNode to simplify The `node.NewNode` method is pretty complex at the moment, an in order to address issues like #3156, we need to simplify the interface for partial node instantiation. In some places, we don't need to build up a full node (like in the `node.TestCreateProposalBlock` test), but the complexity of such partial instantiation needs to be reduced. This PR aims to eventually make this easier/simpler. * Refactor state loading and genesis doc provider into state package * Refactor for clarity of return parameters * Fix incorrect capitalization of error messages * Simplify extracted functions' names * Document optionally-prefixed functions * Refactor optionallyFastSync for clarity of separation of concerns * Restructure function for early return * Restructure function for early return * Remove dependence on deprecated panic functions * refactor code a bit more plus, expose PEXReactor on node * align logger names * add a changelog entry * align logger names 2 * add a note about PEXReactor returning nil
6 years ago
node: refactor node.NewNode (#3456) The node.NewNode method is pretty complex at the moment, an in order to address issues like #3156, we need to simplify the interface for partial node instantiation. In some places, we don't need to build up a full node (like in the node.TestCreateProposalBlock test), but the complexity of such partial instantiation needs to be reduced. This PR aims to eventually make this easier/simpler. See also this gist https://gist.github.com/thanethomson/56e1640d057a26186e38ad678a1d114c for some background work done when starting to refactor here. ## Commits: * [WIP] Refactor node.NewNode to simplify The `node.NewNode` method is pretty complex at the moment, an in order to address issues like #3156, we need to simplify the interface for partial node instantiation. In some places, we don't need to build up a full node (like in the `node.TestCreateProposalBlock` test), but the complexity of such partial instantiation needs to be reduced. This PR aims to eventually make this easier/simpler. * Refactor state loading and genesis doc provider into state package * Refactor for clarity of return parameters * Fix incorrect capitalization of error messages * Simplify extracted functions' names * Document optionally-prefixed functions * Refactor optionallyFastSync for clarity of separation of concerns * Restructure function for early return * Restructure function for early return * Remove dependence on deprecated panic functions * refactor code a bit more plus, expose PEXReactor on node * align logger names * add a changelog entry * align logger names 2 * add a note about PEXReactor returning nil
6 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
7 years ago
  1. package state
  2. import (
  3. "errors"
  4. "fmt"
  5. abci "github.com/tendermint/tendermint/abci/types"
  6. cfg "github.com/tendermint/tendermint/config"
  7. cmn "github.com/tendermint/tendermint/libs/common"
  8. dbm "github.com/tendermint/tendermint/libs/db"
  9. "github.com/tendermint/tendermint/types"
  10. )
  11. const (
  12. // persist validators every valSetCheckpointInterval blocks to avoid
  13. // LoadValidators taking too much time.
  14. // https://github.com/tendermint/tendermint/pull/3438
  15. // 100000 results in ~ 100ms to get 100 validators (see BenchmarkLoadValidators)
  16. valSetCheckpointInterval = 100000
  17. )
  18. var (
  19. genesisDocKey = []byte("genesisDoc")
  20. )
  21. // GenesisDocProvider returns a GenesisDoc.
  22. // It allows the GenesisDoc to be pulled from sources other than the
  23. // filesystem, for instance from a distributed key-value store cluster.
  24. type GenesisDocProvider func() (*types.GenesisDoc, error)
  25. // DefaultGenesisDocProviderFunc returns a GenesisDocProvider that loads
  26. // the GenesisDoc from the config.GenesisFile() on the filesystem.
  27. func DefaultGenesisDocProviderFunc(config *cfg.Config) GenesisDocProvider {
  28. return func() (*types.GenesisDoc, error) {
  29. return types.GenesisDocFromFile(config.GenesisFile())
  30. }
  31. }
  32. //------------------------------------------------------------------------
  33. func calcValidatorsKey(height int64) []byte {
  34. return []byte(fmt.Sprintf("validatorsKey:%v", height))
  35. }
  36. func calcConsensusParamsKey(height int64) []byte {
  37. return []byte(fmt.Sprintf("consensusParamsKey:%v", height))
  38. }
  39. func calcABCIResponsesKey(height int64) []byte {
  40. return []byte(fmt.Sprintf("abciResponsesKey:%v", height))
  41. }
  42. // LoadStateFromDBOrGenesisFile loads the most recent state from the database,
  43. // or creates a new one from the given genesisFilePath and persists the result
  44. // to the database.
  45. func LoadStateFromDBOrGenesisFile(stateDB dbm.DB, genesisFilePath string) (State, error) {
  46. state := LoadState(stateDB)
  47. if state.IsEmpty() {
  48. var err error
  49. state, err = MakeGenesisStateFromFile(genesisFilePath)
  50. if err != nil {
  51. return state, err
  52. }
  53. SaveState(stateDB, state)
  54. }
  55. return state, nil
  56. }
  57. // LoadStateFromDBOrGenesisDoc loads the most recent state from the database,
  58. // or creates a new one from the given genesisDoc and persists the result
  59. // to the database.
  60. func LoadStateFromDBOrGenesisDoc(stateDB dbm.DB, genesisDoc *types.GenesisDoc) (State, error) {
  61. state := LoadState(stateDB)
  62. if state.IsEmpty() {
  63. var err error
  64. state, err = MakeGenesisState(genesisDoc)
  65. if err != nil {
  66. return state, err
  67. }
  68. SaveState(stateDB, state)
  69. }
  70. return state, nil
  71. }
  72. // LoadStateFromDBOrGenesisDocProvider attempts to load the state from the
  73. // database, or creates one using the given genesisDocProvider and persists the
  74. // result to the database. On success this also returns the genesis doc loaded
  75. // through the given provider.
  76. func LoadStateFromDBOrGenesisDocProvider(stateDB dbm.DB, genesisDocProvider GenesisDocProvider) (State, *types.GenesisDoc, error) {
  77. // Get genesis doc
  78. genDoc, err := loadGenesisDoc(stateDB)
  79. if err != nil {
  80. genDoc, err = genesisDocProvider()
  81. if err != nil {
  82. return State{}, nil, err
  83. }
  84. // save genesis doc to prevent a certain class of user errors (e.g. when it
  85. // was changed, accidentally or not). Also good for audit trail.
  86. saveGenesisDoc(stateDB, genDoc)
  87. }
  88. state, err := LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
  89. if err != nil {
  90. return State{}, nil, err
  91. }
  92. return state, genDoc, nil
  93. }
  94. // panics if failed to unmarshal bytes
  95. func loadGenesisDoc(db dbm.DB) (*types.GenesisDoc, error) {
  96. bytes := db.Get(genesisDocKey)
  97. if len(bytes) == 0 {
  98. return nil, errors.New("Genesis doc not found")
  99. }
  100. var genDoc *types.GenesisDoc
  101. err := cdc.UnmarshalJSON(bytes, &genDoc)
  102. if err != nil {
  103. panic(fmt.Sprintf("Failed to load genesis doc due to unmarshaling error: %v (bytes: %X)", err, bytes))
  104. }
  105. return genDoc, nil
  106. }
  107. // panics if failed to marshal the given genesis document
  108. func saveGenesisDoc(db dbm.DB, genDoc *types.GenesisDoc) {
  109. bytes, err := cdc.MarshalJSON(genDoc)
  110. if err != nil {
  111. panic(fmt.Sprintf("Failed to save genesis doc due to marshaling error: %v", err))
  112. }
  113. db.SetSync(genesisDocKey, bytes)
  114. }
  115. // LoadState loads the State from the database.
  116. func LoadState(db dbm.DB) State {
  117. return loadState(db, stateKey)
  118. }
  119. func loadState(db dbm.DB, key []byte) (state State) {
  120. buf := db.Get(key)
  121. if len(buf) == 0 {
  122. return state
  123. }
  124. err := cdc.UnmarshalBinaryBare(buf, &state)
  125. if err != nil {
  126. // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
  127. cmn.Exit(fmt.Sprintf(`LoadState: Data has been corrupted or its spec has changed:
  128. %v\n`, err))
  129. }
  130. // TODO: ensure that buf is completely read.
  131. return state
  132. }
  133. // SaveState persists the State, the ValidatorsInfo, and the ConsensusParamsInfo to the database.
  134. // This flushes the writes (e.g. calls SetSync).
  135. func SaveState(db dbm.DB, state State) {
  136. saveState(db, state, stateKey)
  137. }
  138. func saveState(db dbm.DB, state State, key []byte) {
  139. nextHeight := state.LastBlockHeight + 1
  140. // If first block, save validators for block 1.
  141. if nextHeight == 1 {
  142. // This extra logic due to Tendermint validator set changes being delayed 1 block.
  143. // It may get overwritten due to InitChain validator updates.
  144. lastHeightVoteChanged := int64(1)
  145. saveValidatorsInfo(db, nextHeight, lastHeightVoteChanged, state.Validators)
  146. }
  147. // Save next validators.
  148. saveValidatorsInfo(db, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
  149. // Save next consensus params.
  150. saveConsensusParamsInfo(db, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams)
  151. db.SetSync(key, state.Bytes())
  152. }
  153. //------------------------------------------------------------------------
  154. // ABCIResponses retains the responses
  155. // of the various ABCI calls during block processing.
  156. // It is persisted to disk for each height before calling Commit.
  157. type ABCIResponses struct {
  158. DeliverTx []*abci.ResponseDeliverTx
  159. EndBlock *abci.ResponseEndBlock
  160. BeginBlock *abci.ResponseBeginBlock
  161. }
  162. // NewABCIResponses returns a new ABCIResponses
  163. func NewABCIResponses(block *types.Block) *ABCIResponses {
  164. resDeliverTxs := make([]*abci.ResponseDeliverTx, block.NumTxs)
  165. if block.NumTxs == 0 {
  166. // This makes Amino encoding/decoding consistent.
  167. resDeliverTxs = nil
  168. }
  169. return &ABCIResponses{
  170. DeliverTx: resDeliverTxs,
  171. }
  172. }
  173. // Bytes serializes the ABCIResponse using go-amino.
  174. func (arz *ABCIResponses) Bytes() []byte {
  175. return cdc.MustMarshalBinaryBare(arz)
  176. }
  177. func (arz *ABCIResponses) ResultsHash() []byte {
  178. results := types.NewResults(arz.DeliverTx)
  179. return results.Hash()
  180. }
  181. // LoadABCIResponses loads the ABCIResponses for the given height from the database.
  182. // This is useful for recovering from crashes where we called app.Commit and before we called
  183. // s.Save(). It can also be used to produce Merkle proofs of the result of txs.
  184. func LoadABCIResponses(db dbm.DB, height int64) (*ABCIResponses, error) {
  185. buf := db.Get(calcABCIResponsesKey(height))
  186. if len(buf) == 0 {
  187. return nil, ErrNoABCIResponsesForHeight{height}
  188. }
  189. abciResponses := new(ABCIResponses)
  190. err := cdc.UnmarshalBinaryBare(buf, abciResponses)
  191. if err != nil {
  192. // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
  193. cmn.Exit(fmt.Sprintf(`LoadABCIResponses: Data has been corrupted or its spec has
  194. changed: %v\n`, err))
  195. }
  196. // TODO: ensure that buf is completely read.
  197. return abciResponses, nil
  198. }
  199. // SaveABCIResponses persists the ABCIResponses to the database.
  200. // This is useful in case we crash after app.Commit and before s.Save().
  201. // Responses are indexed by height so they can also be loaded later to produce Merkle proofs.
  202. func saveABCIResponses(db dbm.DB, height int64, abciResponses *ABCIResponses) {
  203. db.SetSync(calcABCIResponsesKey(height), abciResponses.Bytes())
  204. }
  205. //-----------------------------------------------------------------------------
  206. // ValidatorsInfo represents the latest validator set, or the last height it changed
  207. type ValidatorsInfo struct {
  208. ValidatorSet *types.ValidatorSet
  209. LastHeightChanged int64
  210. }
  211. // Bytes serializes the ValidatorsInfo using go-amino.
  212. func (valInfo *ValidatorsInfo) Bytes() []byte {
  213. return cdc.MustMarshalBinaryBare(valInfo)
  214. }
  215. // LoadValidators loads the ValidatorSet for a given height.
  216. // Returns ErrNoValSetForHeight if the validator set can't be found for this height.
  217. func LoadValidators(db dbm.DB, height int64) (*types.ValidatorSet, error) {
  218. valInfo := loadValidatorsInfo(db, height)
  219. if valInfo == nil {
  220. return nil, ErrNoValSetForHeight{height}
  221. }
  222. if valInfo.ValidatorSet == nil {
  223. lastStoredHeight := lastStoredHeightFor(height, valInfo.LastHeightChanged)
  224. valInfo2 := loadValidatorsInfo(db, lastStoredHeight)
  225. if valInfo2 == nil || valInfo2.ValidatorSet == nil {
  226. // TODO (melekes): remove the below if condition in the 0.33 major
  227. // release and just panic. Old chains might panic otherwise if they
  228. // haven't saved validators at intermediate (%valSetCheckpointInterval)
  229. // height yet.
  230. // https://github.com/tendermint/tendermint/issues/3543
  231. valInfo2 = loadValidatorsInfo(db, valInfo.LastHeightChanged)
  232. lastStoredHeight = valInfo.LastHeightChanged
  233. if valInfo2 == nil || valInfo2.ValidatorSet == nil {
  234. panic(
  235. fmt.Sprintf("Couldn't find validators at height %d (height %d was originally requested)",
  236. lastStoredHeight,
  237. height,
  238. ),
  239. )
  240. }
  241. }
  242. valInfo2.ValidatorSet.IncrementProposerPriority(int(height - lastStoredHeight)) // mutate
  243. valInfo = valInfo2
  244. }
  245. return valInfo.ValidatorSet, nil
  246. }
  247. func lastStoredHeightFor(height, lastHeightChanged int64) int64 {
  248. checkpointHeight := height - height%valSetCheckpointInterval
  249. return cmn.MaxInt64(checkpointHeight, lastHeightChanged)
  250. }
  251. // CONTRACT: Returned ValidatorsInfo can be mutated.
  252. func loadValidatorsInfo(db dbm.DB, height int64) *ValidatorsInfo {
  253. buf := db.Get(calcValidatorsKey(height))
  254. if len(buf) == 0 {
  255. return nil
  256. }
  257. v := new(ValidatorsInfo)
  258. err := cdc.UnmarshalBinaryBare(buf, v)
  259. if err != nil {
  260. // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
  261. cmn.Exit(fmt.Sprintf(`LoadValidators: Data has been corrupted or its spec has changed:
  262. %v\n`, err))
  263. }
  264. // TODO: ensure that buf is completely read.
  265. return v
  266. }
  267. // saveValidatorsInfo persists the validator set.
  268. //
  269. // `height` is the effective height for which the validator is responsible for
  270. // signing. It should be called from s.Save(), right before the state itself is
  271. // persisted.
  272. func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) {
  273. if lastHeightChanged > height {
  274. panic("LastHeightChanged cannot be greater than ValidatorsInfo height")
  275. }
  276. valInfo := &ValidatorsInfo{
  277. LastHeightChanged: lastHeightChanged,
  278. }
  279. // Only persist validator set if it was updated or checkpoint height (see
  280. // valSetCheckpointInterval) is reached.
  281. if height == lastHeightChanged || height%valSetCheckpointInterval == 0 {
  282. valInfo.ValidatorSet = valSet
  283. }
  284. db.Set(calcValidatorsKey(height), valInfo.Bytes())
  285. }
  286. //-----------------------------------------------------------------------------
  287. // ConsensusParamsInfo represents the latest consensus params, or the last height it changed
  288. type ConsensusParamsInfo struct {
  289. ConsensusParams types.ConsensusParams
  290. LastHeightChanged int64
  291. }
  292. // Bytes serializes the ConsensusParamsInfo using go-amino.
  293. func (params ConsensusParamsInfo) Bytes() []byte {
  294. return cdc.MustMarshalBinaryBare(params)
  295. }
  296. // LoadConsensusParams loads the ConsensusParams for a given height.
  297. func LoadConsensusParams(db dbm.DB, height int64) (types.ConsensusParams, error) {
  298. empty := types.ConsensusParams{}
  299. paramsInfo := loadConsensusParamsInfo(db, height)
  300. if paramsInfo == nil {
  301. return empty, ErrNoConsensusParamsForHeight{height}
  302. }
  303. if paramsInfo.ConsensusParams.Equals(&empty) {
  304. paramsInfo2 := loadConsensusParamsInfo(db, paramsInfo.LastHeightChanged)
  305. if paramsInfo2 == nil {
  306. panic(
  307. fmt.Sprintf(
  308. "Couldn't find consensus params at height %d as last changed from height %d",
  309. paramsInfo.LastHeightChanged,
  310. height,
  311. ),
  312. )
  313. }
  314. paramsInfo = paramsInfo2
  315. }
  316. return paramsInfo.ConsensusParams, nil
  317. }
  318. func loadConsensusParamsInfo(db dbm.DB, height int64) *ConsensusParamsInfo {
  319. buf := db.Get(calcConsensusParamsKey(height))
  320. if len(buf) == 0 {
  321. return nil
  322. }
  323. paramsInfo := new(ConsensusParamsInfo)
  324. err := cdc.UnmarshalBinaryBare(buf, paramsInfo)
  325. if err != nil {
  326. // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
  327. cmn.Exit(fmt.Sprintf(`LoadConsensusParams: Data has been corrupted or its spec has changed:
  328. %v\n`, err))
  329. }
  330. // TODO: ensure that buf is completely read.
  331. return paramsInfo
  332. }
  333. // saveConsensusParamsInfo persists the consensus params for the next block to disk.
  334. // It should be called from s.Save(), right before the state itself is persisted.
  335. // If the consensus params did not change after processing the latest block,
  336. // only the last height for which they changed is persisted.
  337. func saveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params types.ConsensusParams) {
  338. paramsInfo := &ConsensusParamsInfo{
  339. LastHeightChanged: changeHeight,
  340. }
  341. if changeHeight == nextHeight {
  342. paramsInfo.ConsensusParams = params
  343. }
  344. db.Set(calcConsensusParamsKey(nextHeight), paramsInfo.Bytes())
  345. }