diff --git a/.changelog/5265.feature.md b/.changelog/5265.feature.md new file mode 100644 index 00000000000..1454868c78e --- /dev/null +++ b/.changelog/5265.feature.md @@ -0,0 +1,6 @@ +go/oasis-node: Display consensus checkpoint heights + +A new field `heights` has been added to the `oasis-node control status` output +under the `consensus.checkpoint` status section. If checkpointer is not +disabled and there is at least one checkpoint, this field displays a list +of local checkpoint heights. diff --git a/go/consensus/api/api.go b/go/consensus/api/api.go index fb076745753..cd7eed8a3de 100644 --- a/go/consensus/api/api.go +++ b/go/consensus/api/api.go @@ -375,6 +375,9 @@ type Status struct { // P2P is the P2P status of the node. P2P *P2PStatus `json:"p2p,omitempty"` + + // Checkpoint is the checkpoints status of the node. + Checkpoint *CheckpointStatus `json:"checkpoint,omitempty"` } // P2PStatus is the P2P status of a node. @@ -392,6 +395,14 @@ type P2PStatus struct { Peers []string `json:"peers"` } +// CheckpointStatus is the checkpoints status of the node. +type CheckpointStatus struct { + // Heights is a list of consensus heights for which the node has checkpoints locally. + // + // Heights are sorted in descending order. + Heights []uint64 `json:"heights"` +} + // Service is an interface that a consensus backend service must provide. type Service interface { service.BackgroundService diff --git a/go/consensus/cometbft/full/common.go b/go/consensus/cometbft/full/common.go index c8d61898c64..58fb1dff135 100644 --- a/go/consensus/cometbft/full/common.go +++ b/go/consensus/cometbft/full/common.go @@ -3,6 +3,7 @@ package full import ( "context" "fmt" + "slices" "sync" "sync/atomic" @@ -831,19 +832,47 @@ func (n *commonNode) GetStatus(ctx context.Context) (*consensusAPI.Status, error valSetHeight = status.GenesisHeight } vals, err := n.stateStore.LoadValidators(valSetHeight) - if err != nil { - // Failed to load validator set. - status.IsValidator = false - } else { + switch err { + case nil: consensusPk := n.identity.ConsensusSigner.Public() consensusAddr := []byte(crypto.PublicKeyToCometBFT(&consensusPk).Address()) status.IsValidator = vals.HasAddress(consensusAddr) + default: + // Failed to load validator set. + status.IsValidator = false + } + + if n.Checkpointer() != nil { + status.Checkpoint = n.fetchCheckpointStatus(ctx) } } return status, nil } +// fetchCheckpointStatus fetches checkpoint status. +// +// In case of zero checkpoints or error a nil status is returned. +func (n *commonNode) fetchCheckpointStatus(ctx context.Context) *consensusAPI.CheckpointStatus { + cps, err := n.mux.State().Storage().GetCheckpoints(ctx, &checkpoint.GetCheckpointsRequest{ + Version: 1, + }) + if err != nil { + n.Logger.Error("failed to fetch checkpoints status", "err", err) + return nil + } + var heights []uint64 + for _, cp := range cps { + heights = append(heights, cp.Root.Version) + } + if len(heights) <= 0 { + return nil + } + slices.Sort(heights) + slices.Reverse(heights) + return &consensusAPI.CheckpointStatus{Heights: heights} +} + // Unimplemented methods. // Implements consensusAPI.Backend.