diff --git a/.changelog/5998.feature.md b/.changelog/5998.feature.md new file mode 100644 index 00000000000..bf930eda9f5 --- /dev/null +++ b/.changelog/5998.feature.md @@ -0,0 +1,8 @@ +go/oasis_node: Add runtime block history status message + +A new field `history` has been added to the `oasis-node control status` +output under the runtime status section. This field displays the latest round +and consensus height of the latest runtime block seen by the block history. + +This is useful for the node operators, so that they can estimate the history +reindex speed. diff --git a/go/control/api/api.go b/go/control/api/api.go index a86b23b4bb1..eb99c4ad28c 100644 --- a/go/control/api/api.go +++ b/go/control/api/api.go @@ -192,6 +192,9 @@ type RuntimeStatus struct { // Storage contains the storage worker status in case this node is a storage node. Storage *storageWorker.Status `json:"storage,omitempty"` + // History is status of block history. + History *HistoryStatus `json:"history,omitempty"` + // Provisioner is the name of the runtime provisioner. Provisioner string `json:"provisioner,omitempty"` @@ -230,6 +233,14 @@ type SeedStatus struct { NodePeers []string `json:"node_peers"` } +// HistoryStatus is the status as observed by the block history. +type HistoryStatus struct { + // LastRound is the round of the latest block. + LastRound uint64 `json:"last_round"` + // LastHeight is the consensus height of the latest block. + LastHeight int64 `json:"last_height"` +} + // DebugModuleName is the module name for the debug controller service. const DebugModuleName = "control/debug" diff --git a/go/oasis-node/cmd/node/node_control.go b/go/oasis-node/cmd/node/node_control.go index 00bb8280515..9e82b2f9b50 100644 --- a/go/oasis-node/cmd/node/node_control.go +++ b/go/oasis-node/cmd/node/node_control.go @@ -341,6 +341,30 @@ func (n *Node) getRuntimeStatus(ctx context.Context) (map[common.Namespace]contr } } + // Fetch block history status. + var historyStatus control.HistoryStatus + cBlk, err := rt.History().GetCommittedBlock(ctx, roothash.RoundLatest) + switch err { + case nil: + historyStatus.LastRound = cBlk.Header.Round + default: + n.logger.Error("failed to fetch block history latest block", + "err", err, + "runtime_id", rt.ID(), + ) + } + height, err := rt.History().LastConsensusHeight() + switch err { + case nil: + historyStatus.LastHeight = height + default: + n.logger.Error("failed to fetch block history latest height", + "err", err, + "runtime_id", rt.ID(), + ) + } + status.History = &historyStatus + // Fetch provisioner type. status.Provisioner = "none" if provisioner := rt.HostProvisioner(); provisioner != nil { diff --git a/go/roothash/api/history.go b/go/roothash/api/history.go index 8334e2c3a0e..db195777898 100644 --- a/go/roothash/api/history.go +++ b/go/roothash/api/history.go @@ -48,6 +48,8 @@ type BlockHistory interface { // LastConsensusHeight returns the last consensus height which was seen // by block history. + // + // This method can return height for block not yet synced to storage. LastConsensusHeight() (int64, error) // GetCommittedBlock returns the committed block at a specific round. @@ -57,6 +59,7 @@ type BlockHistory interface { GetCommittedBlock(ctx context.Context, round uint64) (*block.Block, error) // GetBlock returns the block at a specific round. + // // Passing the special value `RoundLatest` will return the latest block. // // This method returns blocks that are both committed and synced to storage. @@ -65,13 +68,19 @@ type BlockHistory interface { // GetAnnotatedBlock returns the annotated block at a specific round. // // Passing the special value `RoundLatest` will return the latest annotated block. + // + // This method returns blocks that are both committed and synced to storage. GetAnnotatedBlock(ctx context.Context, round uint64) (*AnnotatedBlock, error) // GetEarliestBlock returns the earliest known block. + // + // This method can return blocks not yet synced to storage. GetEarliestBlock(ctx context.Context) (*block.Block, error) // GetRoundResults returns the round results for the given round. // // Passing the special value `RoundLatest` will return results for the latest round. + // + // This method can return round results for block not yet synced to storage. GetRoundResults(ctx context.Context, round uint64) (*RoundResults, error) }