From 35371ae33e070bd77c2980a2b49c9b283f96f365 Mon Sep 17 00:00:00 2001 From: violet Date: Tue, 11 Feb 2025 13:53:08 -0500 Subject: [PATCH] Feat: dimension block_height by proposer in TIA and Cosmos. This allows validators to monitor for conditions where their node stops proposing blocks --- .../consensus/uptime/types/types_common.go | 1 + internal/packages/health/block/api/api.go | 15 ++-- .../health/block/collector/collector.go | 10 ++- .../packages/health/block/parser/parser.go | 71 ++++++++----------- .../packages/health/block/router/router.go | 2 +- .../packages/health/block/types/common.go | 3 + .../packages/health/block/types/cosmos.go | 46 ++++-------- 7 files changed, 60 insertions(+), 88 deletions(-) diff --git a/internal/packages/consensus/uptime/types/types_common.go b/internal/packages/consensus/uptime/types/types_common.go index d2577e8..a3ff868 100644 --- a/internal/packages/consensus/uptime/types/types_common.go +++ b/internal/packages/consensus/uptime/types/types_common.go @@ -20,6 +20,7 @@ type ValidatorUptimeStatus struct { ValidatorConsensusAddress string `json:"validator_consensus_addreess"` MissedBlockCounter float64 `json:"missed_block_counter"` IsTomstoned float64 + ProposerPriority float64 `json:"proposer_priority"` // Only Consumer Chain ConsumerConsensusAddress string `json:"consumer_consensus_address"` } diff --git a/internal/packages/health/block/api/api.go b/internal/packages/health/block/api/api.go index 44382bb..a13bbd9 100644 --- a/internal/packages/health/block/api/api.go +++ b/internal/packages/health/block/api/api.go @@ -13,8 +13,10 @@ import ( func GetBlockStatus( c *common.Exporter, CommonBlockCallClient common.ClientType, - CommonBlockCallMethod common.Method, CommonBlockQueryPath string, CommonBlockPayload string, - CommonBlockParser func([]byte) (float64, float64, error), + CommonBlockCallMethod common.Method, + CommonBlockQueryPath string, + CommonBlockPayload string, + CommonBlockParser types.BlockParser, ) (types.CommonBlock, error) { // init context ctx := context.Background() @@ -56,15 +58,12 @@ func GetBlockStatus( return types.CommonBlock{}, common.ErrGotStrangeStatusCode } - blockHeight, blockTimeStamp, err := CommonBlockParser(resp.Body()) + block, err := CommonBlockParser(resp.Body()) if err != nil { c.Errorf("parser error: %s", err) return types.CommonBlock{}, common.ErrFailedJsonUnmarshal } - c.Debugf("got block timestamp: %d", int(blockTimeStamp)) - return types.CommonBlock{ - LastBlockHeight: blockHeight, - LastBlockTimeStamp: blockTimeStamp, - }, nil + c.Debugf("got block timestamp: %d", int(block.LastBlockTimeStamp)) + return block, nil } diff --git a/internal/packages/health/block/collector/collector.go b/internal/packages/health/block/collector/collector.go index c21092e..bf46b44 100644 --- a/internal/packages/health/block/collector/collector.go +++ b/internal/packages/health/block/collector/collector.go @@ -47,12 +47,14 @@ func loop(c *common.Exporter, m common.Packager) { ConstLabels: packageLabels, Name: TimestampMetricName, }) - blockHeightMetric := m.Factory.NewGauge(prometheus.GaugeOpts{ + + // Change block height metric to use a vector + blockHeightMetric := m.Factory.NewGaugeVec(prometheus.GaugeOpts{ Namespace: common.Namespace, Subsystem: Subsystem, ConstLabels: packageLabels, Name: BlockHeightMetricName, - }) + }, []string{common.ProposerAddressLabel}) for { // NOTE: block is a default package, so skip the select node logic to GetStatus method @@ -71,7 +73,9 @@ func loop(c *common.Exporter, m common.Packager) { // NOTE: block package is a default package, so the metrics will be updated regardless of app mode timestampMetric.Set(status.LastBlockTimeStamp) - blockHeightMetric.Set(status.LastBlockHeight) + blockHeightMetric.With(prometheus.Labels{ + common.ProposerAddressLabel: status.ProposerAddress, + }).Set(status.LastBlockHeight) c.Infof("updated metrics successfully and going to sleep %s ...", subsystemSleep.String()) diff --git a/internal/packages/health/block/parser/parser.go b/internal/packages/health/block/parser/parser.go index 51c5e35..44b8066 100644 --- a/internal/packages/health/block/parser/parser.go +++ b/internal/packages/health/block/parser/parser.go @@ -2,7 +2,6 @@ package parser import ( "encoding/json" - "fmt" "strconv" "github.com/cosmostation/cvms/internal/helper" @@ -11,74 +10,62 @@ import ( ) // cosmos -func CosmosBlockParser(resp []byte) (float64, float64, error) { - var preResult map[string]interface{} - if err := json.Unmarshal(resp, &preResult); err != nil { - return 0, 0, errors.Wrap(err, "failed to unmarshal json in parser") +func CosmosBlockParser(resp []byte) (types.CommonBlock, error) { + var result types.CosmosBlockResponse + if err := json.Unmarshal(resp, &result); err != nil { + return types.CommonBlock{}, errors.Wrap(err, "failed to unmarshal json in parser") } - _, ok := preResult["jsonrpc"].(string) - if ok { // tendermint v0.34.x - var resultV34 types.CosmosV34BlockResponse - if err := json.Unmarshal(resp, &resultV34); err != nil { - return 0, 0, errors.Wrap(err, "failed to unmarshal json in parser") - } - - timestamp := resultV34.Result.SyncInfo.LatestBlockTime.Unix() - blockHeight, err := strconv.ParseFloat(resultV34.Result.SyncInfo.LatestBlockHeight, 64) - if err != nil { - return 0, 0, errors.Wrap(err, "failed to convert from stirng to float in parser") - } - - return blockHeight, float64(timestamp), nil - - } else { // tendermint v0.37.x - var resultV37 types.CosmosV37BlockResponse - if err := json.Unmarshal(resp, &resultV37); err != nil { - return 0, 0, fmt.Errorf("parsing error: %s", err.Error()) - } - - timestamp := resultV37.SyncInfo.LatestBlockTime.Unix() - blockHeight, err := strconv.ParseFloat(resultV37.SyncInfo.LatestBlockHeight, 64) - if err != nil { - return 0, 0, errors.Wrap(err, "failed to convert from stirng to float in parser") - } - - return blockHeight, float64(timestamp), nil + blockHeight, err := strconv.ParseFloat(result.Result.Block.Header.Height, 64) + if err != nil { + return types.CommonBlock{}, errors.Wrap(err, "failed to convert height to float") } + + return types.CommonBlock{ + LastBlockHeight: blockHeight, + LastBlockTimeStamp: float64(result.Result.Block.Header.Time.Unix()), + ProposerAddress: result.Result.Block.Header.ProposerAddress, + }, nil } // ethereum -func EthereumBlockParser(resp []byte) (float64, float64, error) { +func EthereumBlockParser(resp []byte) (types.CommonBlock, error) { var result types.EthereumBlockResponse if err := json.Unmarshal(resp, &result); err != nil { - return 0, 0, errors.Wrap(err, "failed to unmarshal json in parser") + return types.CommonBlock{}, errors.Wrap(err, "failed to unmarshal json in parser") } timestamp, err := helper.ParsingfromHexaNumberBaseHexaDecimal(helper.HexaNumberToInteger(result.Result.TimeStamp)) if err != nil { - return 0, 0, errors.Wrap(err, "failed to convert from stirng to float in parser") + return types.CommonBlock{}, errors.Wrap(err, "failed to convert from string to float in parser") } blockHeight, err := helper.ParsingfromHexaNumberBaseHexaDecimal(helper.HexaNumberToInteger(result.Result.Number)) if err != nil { - return 0, 0, errors.Wrap(err, "failed to convert from stirng to float in parser") + return types.CommonBlock{}, errors.Wrap(err, "failed to convert from string to float in parser") } - return float64(blockHeight), float64(timestamp), nil + return types.CommonBlock{ + LastBlockHeight: float64(blockHeight), + LastBlockTimeStamp: float64(timestamp), + }, nil } // celestia -func CelestiaBlockParser(resp []byte) (float64, float64, error) { +func CelestiaBlockParser(resp []byte) (types.CommonBlock, error) { var result types.CelestiaBlockResponse if err := json.Unmarshal(resp, &result); err != nil { - return 0, 0, errors.Wrap(err, "failed to unmarshal json in parser") + return types.CommonBlock{}, errors.Wrap(err, "failed to unmarshal json in parser") } blockHeight, err := strconv.ParseFloat(result.Result.Header.Height, 64) if err != nil { - return 0, 0, errors.Wrap(err, "failed to convert from stirng to float in parser") + return types.CommonBlock{}, errors.Wrap(err, "failed to convert from string to float in parser") } - return blockHeight, float64(result.Result.Header.Time.Unix()), nil + return types.CommonBlock{ + LastBlockHeight: blockHeight, + LastBlockTimeStamp: float64(result.Result.Header.Time.Unix()), + ProposerAddress: result.Result.Header.ProposerAddress, + }, nil } diff --git a/internal/packages/health/block/router/router.go b/internal/packages/health/block/router/router.go index 338b64b..b76489a 100644 --- a/internal/packages/health/block/router/router.go +++ b/internal/packages/health/block/router/router.go @@ -13,7 +13,7 @@ func GetStatus(client *common.Exporter, protocolType string) (types.CommonBlock, CommonBlockCallMethod common.Method CommonBlockQueryPath string CommonBlockPayload string - CommonBlockParser func(resp []byte) (blockHeight, timeStamp float64, err error) + CommonBlockParser types.BlockParser ) switch protocolType { diff --git a/internal/packages/health/block/types/common.go b/internal/packages/health/block/types/common.go index c892119..38356cf 100644 --- a/internal/packages/health/block/types/common.go +++ b/internal/packages/health/block/types/common.go @@ -8,4 +8,7 @@ var ( type CommonBlock struct { LastBlockHeight float64 LastBlockTimeStamp float64 + ProposerAddress string } + +type BlockParser func(resp []byte) (CommonBlock, error) diff --git a/internal/packages/health/block/types/cosmos.go b/internal/packages/health/block/types/cosmos.go index be889b3..e213609 100644 --- a/internal/packages/health/block/types/cosmos.go +++ b/internal/packages/health/block/types/cosmos.go @@ -3,42 +3,20 @@ package types import "time" const ( - CosmosBlockQueryPath = "/status" + CosmosBlockQueryPath = "/block" CosmosBlockQueryPayload = "" ) -type CosmosV34BlockResponse struct { - JsonRPC string `json:"jsonrpc" validate:"required"` - ID int `json:"id" validate:"required"` +type CosmosBlockResponse struct { + JsonRPC string `json:"jsonrpc"` + ID int `json:"id"` Result struct { - NodeInfo map[string]any `json:"node_info"` - SyncInfo struct { - LatestBlockHash string `json:"latest_block_hash"` - LatestAppHash string `json:"latest_app_hash"` - LatestBlockHeight string `json:"latest_block_height"` - LatestBlockTime time.Time `json:"latest_block_time"` - EarliestBlockHash string `json:"earliest_block_hash"` - EarliestAppHash string `json:"earliest_app_hash"` - EarliestBlcokHeight string `json:"earliest_block_height"` - EarliestBlockTime time.Time `json:"earliest_block_time"` - CatchingUp bool `json:"catching_up"` - } `json:"sync_info"` - ValidatorInfo map[string]any `json:"validator_info"` - } `json:"result" validate:"required"` -} - -type CosmosV37BlockResponse struct { - NodeInfo map[string]any `json:"node_info"` - SyncInfo struct { - LatestBlockHash string `json:"latest_block_hash"` - LatestAppHash string `json:"latest_app_hash"` - LatestBlockHeight string `json:"latest_block_height"` - LatestBlockTime time.Time `json:"latest_block_time"` - EarliestBlockHash string `json:"earliest_block_hash"` - EarliestAppHash string `json:"earliest_app_hash"` - EarliestBlcokHeight string `json:"earliest_block_height"` - EarliestBlockTime time.Time `json:"earliest_block_time"` - CatchingUp bool `json:"catching_up"` - } `json:"sync_info" validate:"required"` - ValidatorInfo map[string]any `json:"validator_info"` + Block struct { + Header struct { + Height string `json:"height"` + Time time.Time `json:"time"` + ProposerAddress string `json:"proposer_address"` + } `json:"header"` + } `json:"block"` + } `json:"result"` }