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.

174 lines
4.3 KiB

  1. package core
  2. import (
  3. "bytes"
  4. "time"
  5. cmn "github.com/tendermint/tendermint/libs/common"
  6. ctypes "github.com/tendermint/tendermint/rpc/core/types"
  7. sm "github.com/tendermint/tendermint/state"
  8. "github.com/tendermint/tendermint/types"
  9. )
  10. // Get Tendermint status including node info, pubkey, latest block
  11. // hash, app hash, block height and time.
  12. //
  13. // ```shell
  14. // curl 'localhost:26657/status'
  15. // ```
  16. //
  17. // ```go
  18. // client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
  19. // result, err := client.Status()
  20. // ```
  21. //
  22. // > The above command returns JSON structured like this:
  23. //
  24. // ```json
  25. //{
  26. // "jsonrpc": "2.0",
  27. // "id": "",
  28. // "result": {
  29. // "node_info": {
  30. // "id": "562dd7f579f0ecee8c94a11a3c1e378c1876f433",
  31. // "listen_addr": "192.168.1.2:26656",
  32. // "network": "test-chain-I6zScH",
  33. // "version": "0.19.0",
  34. // "channels": "4020212223303800",
  35. // "moniker": "Ethans-MacBook-Pro.local",
  36. // "other": [
  37. // "amino_version=0.9.8",
  38. // "p2p_version=0.5.0",
  39. // "consensus_version=v1/0.2.2",
  40. // "rpc_version=0.7.0/3",
  41. // "tx_index=on",
  42. // "rpc_addr=tcp://0.0.0.0:26657"
  43. // ]
  44. // },
  45. // "sync_info": {
  46. // "latest_block_hash": "2D4D7055BE685E3CB2410603C92AD37AE557AC59",
  47. // "latest_app_hash": "0000000000000000",
  48. // "latest_block_height": 231,
  49. // "latest_block_time": "2018-04-27T23:18:08.459766485-04:00",
  50. // "catching_up": false
  51. // },
  52. // "validator_info": {
  53. // "address": "5875562FF0FFDECC895C20E32FC14988952E99E7",
  54. // "pub_key": {
  55. // "type": "tendermint/PubKeyEd25519",
  56. // "value": "PpDJRUrLG2RgFqYYjawfn/AcAgacSXpLFrmfYYQnuzE="
  57. // },
  58. // "voting_power": 10
  59. // }
  60. // }
  61. //}
  62. // ```
  63. func Status() (*ctypes.ResultStatus, error) {
  64. latestHeight := blockStore.Height()
  65. var (
  66. latestBlockMeta *types.BlockMeta
  67. latestBlockHash cmn.HexBytes
  68. latestAppHash cmn.HexBytes
  69. latestBlockTimeNano int64
  70. )
  71. if latestHeight != 0 {
  72. latestBlockMeta = blockStore.LoadBlockMeta(latestHeight)
  73. latestBlockHash = latestBlockMeta.BlockID.Hash
  74. latestAppHash = latestBlockMeta.Header.AppHash
  75. latestBlockTimeNano = latestBlockMeta.Header.Time.UnixNano()
  76. }
  77. latestBlockTime := time.Unix(0, latestBlockTimeNano)
  78. var votingPower int64
  79. if val := validatorAtHeight(latestHeight); val != nil {
  80. votingPower = val.VotingPower
  81. }
  82. result := &ctypes.ResultStatus{
  83. NodeInfo: p2pSwitch.NodeInfo(),
  84. SyncInfo: ctypes.SyncInfo{
  85. LatestBlockHash: latestBlockHash,
  86. LatestAppHash: latestAppHash,
  87. LatestBlockHeight: latestHeight,
  88. LatestBlockTime: latestBlockTime,
  89. CatchingUp: consensusReactor.FastSync(),
  90. },
  91. ValidatorInfo: ctypes.ValidatorInfo{
  92. Address: pubKey.Address(),
  93. PubKey: pubKey,
  94. VotingPower: votingPower,
  95. },
  96. }
  97. return result, nil
  98. }
  99. const consensusTimeout = time.Second
  100. func validatorAtHeight(h int64) *types.Validator {
  101. lastBlockHeight, vals := getValidatorsWithTimeout(
  102. consensusState,
  103. consensusTimeout,
  104. )
  105. if lastBlockHeight == -1 {
  106. return nil
  107. }
  108. privValAddress := pubKey.Address()
  109. // if we're still at height h, search in the current validator set
  110. if lastBlockHeight == h {
  111. for _, val := range vals {
  112. if bytes.Equal(val.Address, privValAddress) {
  113. return val
  114. }
  115. }
  116. }
  117. // if we've moved to the next height, retrieve the validator set from DB
  118. if lastBlockHeight > h {
  119. vals, err := sm.LoadValidators(stateDB, h)
  120. if err != nil {
  121. // should not happen
  122. return nil
  123. }
  124. _, val := vals.GetByAddress(privValAddress)
  125. return val
  126. }
  127. return nil
  128. }
  129. type validatorRetriever interface {
  130. GetValidators() (int64, []*types.Validator)
  131. }
  132. // NOTE: Consensus might halt, but we still need to process RPC requests (at
  133. // least for endpoints whole output does not depend on consensus state).
  134. func getValidatorsWithTimeout(
  135. vr validatorRetriever,
  136. t time.Duration,
  137. ) (int64, []*types.Validator) {
  138. resultCh := make(chan struct {
  139. lastBlockHeight int64
  140. vals []*types.Validator
  141. })
  142. go func() {
  143. h, v := vr.GetValidators()
  144. resultCh <- struct {
  145. lastBlockHeight int64
  146. vals []*types.Validator
  147. }{h, v}
  148. }()
  149. select {
  150. case res := <-resultCh:
  151. return res.lastBlockHeight, res.vals
  152. case <-time.After(t):
  153. if logger != nil {
  154. logger.Error("Timed out querying validators from consensus", "timeout", t)
  155. }
  156. return -1, []*types.Validator{}
  157. }
  158. }