Skip to content

Commit

Permalink
Add network metrics endpoint (#2143)
Browse files Browse the repository at this point in the history
* Remove metrics from InfoResponse

* NetworkMetricsResponse

* get_network_metrics

* Add to bindings core

* python binding

* Nodejs binding

* lint

* python fix

* Comment tests

* lint

* tmp

* TODO
  • Loading branch information
thibault-martinez authored Mar 8, 2024
1 parent 85d30ef commit ffb6fc2
Show file tree
Hide file tree
Showing 19 changed files with 95 additions and 56 deletions.
9 changes: 4 additions & 5 deletions .github/actions/private-tangle/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ runs:
uses: actions/checkout@v3
with:
repository: iotaledger/iota-core
# TODO: remove ref when inx plugins are updated to the latest commit
ref: 25995c84ccc4f4cd9041881035f338acca513b0a
path: iota-core

- name: Prepare files for start and stop
Expand Down Expand Up @@ -40,6 +38,7 @@ runs:
- name: Wait for tangle to start
shell: bash
run: wget -qO- https://raw.githubusercontent.com/eficode/wait-for/v2.2.4/wait-for | sh -s -- -t 120 http://localhost:8050/health -- echo "Tangle is up"
- name: Wait for faucet to start
shell: bash
run: wget -qO- https://raw.githubusercontent.com/eficode/wait-for/v2.2.4/wait-for | sh -s -- -t 120 http://localhost:8088/health -- echo "Faucet is up"
# TODO https://github.com/iotaledger/iota-sdk/issues/2154
# - name: Wait for faucet to start
# shell: bash
# run: wget -qO- https://raw.githubusercontent.com/eficode/wait-for/v2.2.4/wait-for | sh -s -- -t 120 http://localhost:8088/health -- echo "Faucet is up"
2 changes: 2 additions & 0 deletions bindings/core/src/method/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ pub enum ClientMethod {
},
/// Returns general information about the node together with its URL.
GetNodeInfo,
/// Returns network metrics.
GetNetworkMetrics,
/// Check the readiness of the node to issue a new block, the reference mana cost based on the rate setter and
/// current network congestion, and the block issuance credits of the requested account.
#[serde(rename_all = "camelCase")]
Expand Down
1 change: 1 addition & 0 deletions bindings/core/src/method_handler/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ pub(crate) async fn call_client_method_internal(
ClientMethod::GetHealth { url } => Response::Bool(client.get_health(&url).await?),
ClientMethod::GetInfo { url, auth } => Response::Info(Client::get_info(&url, auth).await?),
ClientMethod::GetNodeInfo => Response::NodeInfo(client.get_node_info().await?),
ClientMethod::GetNetworkMetrics => Response::NetworkMetrics(client.get_network_metrics().await?),
ClientMethod::GetRoutes => Response::Routes(client.get_routes().await?),
ClientMethod::GetAccountCongestion { account_id, work_score } => {
Response::Congestion(client.get_account_congestion(&account_id, work_score).await?)
Expand Down
9 changes: 6 additions & 3 deletions bindings/core/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ use iota_sdk::{
api::{
core::{
BlockMetadataResponse, BlockWithMetadataResponse, CommitteeResponse, CongestionResponse, InfoResponse,
IssuanceBlockHeaderResponse, ManaRewardsResponse, OutputResponse, OutputWithMetadataResponse,
RoutesResponse, TransactionMetadataResponse, UtxoChangesFullResponse, UtxoChangesResponse,
ValidatorResponse, ValidatorsResponse,
IssuanceBlockHeaderResponse, ManaRewardsResponse, NetworkMetricsResponse, OutputResponse,
OutputWithMetadataResponse, RoutesResponse, TransactionMetadataResponse, UtxoChangesFullResponse,
UtxoChangesResponse, ValidatorResponse, ValidatorsResponse,
},
plugins::indexer::OutputIdsResponse,
},
Expand Down Expand Up @@ -102,6 +102,9 @@ pub enum Response {
/// - [`GetNodeInfo`](crate::method::ClientMethod::GetNodeInfo)
NodeInfo(NodeInfoResponse),
/// Response for:
/// - [`GetNetworkMetrics`](crate::method::ClientMethod::GetNetworkMetrics)
NetworkMetrics(NetworkMetricsResponse),
/// Response for:
/// - [`GetRoutes`](crate::method::ClientMethod::GetRoutes)
Routes(RoutesResponse),
/// Response for:
Expand Down
12 changes: 12 additions & 0 deletions bindings/nodejs/lib/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import {
IssuanceBlockHeaderResponse,
OutputMetadataResponse,
OutputWithMetadataResponse,
NetworkMetricsResponse,
} from '../types/models/api';
import { RoutesResponse } from '../types/models/api/routes-response';

Expand Down Expand Up @@ -156,6 +157,17 @@ export class Client {
return JSON.parse(response).payload;
}

/**
* Get the network metrics.
*/
async getNetworkMetrics(): Promise<NetworkMetricsResponse> {
const response = await this.methodHandler.callMethod({
name: 'getNetworkMetrics',
});

return JSON.parse(response).payload;
}

// Accounts routes.

/**
Expand Down
4 changes: 4 additions & 0 deletions bindings/nodejs/lib/types/client/bridge/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ export interface __GetInfoMethod__ {
};
}

export interface __GetNetworkMetricsMethod__ {
name: 'getNetworkMetrics';
}

// Accounts routes.

export interface __GetAccountCongestionMethod__ {
Expand Down
2 changes: 2 additions & 0 deletions bindings/nodejs/lib/types/client/bridge/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import type {
__GetProtocolParametersMethod__,
__GetHealthMethod__,
__GetNodeInfoMethod__,
__GetNetworkMetricsMethod__,
__GetBlockRawMethod__,
__GetIncludedBlockMethod__,
__GetIncludedBlockRawMethod__,
Expand Down Expand Up @@ -98,6 +99,7 @@ export type __ClientMethods__ =
| __GetProtocolParametersMethod__
| __GetHealthMethod__
| __GetNodeInfoMethod__
| __GetNetworkMetricsMethod__
| __GetBlockRawMethod__
| __GetIncludedBlockMethod__
| __GetIncludedBlockRawMethod__
Expand Down
1 change: 1 addition & 0 deletions bindings/nodejs/lib/types/models/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from './committee-response';
export * from './congestion-response';
export * from './issuance-response';
export * from './mana-rewards-response';
export * from './network-metrics';
export * from './output-id-proof';
export * from './output-metadata-response';
export * from './output-response';
Expand Down
1 change: 0 additions & 1 deletion bindings/nodejs/lib/types/models/api/info/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@

export * from './node-info';
export * from './node-info-base-token';
export * from './node-info-metrics';
export * from './node-info-protocol';
export * from './node-info-status';
5 changes: 0 additions & 5 deletions bindings/nodejs/lib/types/models/api/info/node-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0

import type { BaseTokenResponse } from './node-info-base-token';
import type { MetricsResponse } from './node-info-metrics';
import type { ProtocolParametersResponse } from './node-info-protocol';
import type { StatusResponse } from './node-info-status';
/**
Expand All @@ -21,10 +20,6 @@ export interface InfoResponse {
* The status of the node.
*/
status: StatusResponse;
/**
* The metrics for the node.
*/
metrics: MetricsResponse;
/**
* The protocol parameters.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Copyright 2023 IOTA Stiftung
// Copyright 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

/**
* Response from the /info endpoint.
* Metrics information about the network.
*/
export interface MetricsResponse {
export interface NetworkMetricsResponse {
/**
* The current rate of new blocks per second.
*/
Expand Down
21 changes: 17 additions & 4 deletions bindings/python/iota_sdk/client/_node_core_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from typing import List, Optional, Union
from abc import ABCMeta, abstractmethod
from iota_sdk.client.responses import InfoResponse, NodeInfoResponse, RoutesResponse, CongestionResponse, ManaRewardsResponse, CommitteeResponse, ValidatorResponse, ValidatorsResponse, IssuanceBlockHeaderResponse, BlockMetadataResponse, BlockWithMetadataResponse, OutputResponse, OutputWithMetadataResponse, TransactionMetadataResponse, UtxoChangesResponse, UtxoChangesFullResponse
from iota_sdk.client.responses import InfoResponse, NodeInfoResponse, NetworkMetricsResponse, RoutesResponse, CongestionResponse, ManaRewardsResponse, CommitteeResponse, ValidatorResponse, ValidatorsResponse, IssuanceBlockHeaderResponse, BlockMetadataResponse, BlockWithMetadataResponse, OutputResponse, OutputWithMetadataResponse, TransactionMetadataResponse, UtxoChangesResponse, UtxoChangesFullResponse
from iota_sdk.types.block.block import Block
from iota_sdk.types.block.id import BlockId
from iota_sdk.types.common import HexStr, EpochIndex, SlotIndex
Expand Down Expand Up @@ -82,9 +82,20 @@ def get_info(self, url: str, auth=None) -> InfoResponse:
'auth': auth
}))

def get_network_metrics(self) -> NetworkMetricsResponse:
"""Returns network metrics.
GET /api/core/v3/network/metrics
Returns:
Network metrics.
"""
return NetworkMetricsResponse.from_dict(
self._call_method('getNetworkMetrics'))

# Accounts routes.

def get_account_congestion(self, account_id: HexStr, work_score: Optional[int] = None) -> CongestionResponse:
def get_account_congestion(
self, account_id: HexStr, work_score: Optional[int] = None) -> CongestionResponse:
"""Checks if the account is ready to issue a block.
GET /api/core/v3/accounts/{bech32Address}/congestion
"""
Expand Down Expand Up @@ -112,7 +123,8 @@ def get_output_mana_rewards(

# Validators routes.

def get_validators(self, page_size: Optional[int] = None, cursor: Optional[str] = None) -> ValidatorsResponse:
def get_validators(
self, page_size: Optional[int] = None, cursor: Optional[str] = None) -> ValidatorsResponse:
"""Returns information of all stakers (registered validators) and if they are active, ordered by their holding stake.
GET /api/core/v3/validators
"""
Expand All @@ -131,7 +143,8 @@ def get_validator(self, account_id: HexStr) -> ValidatorResponse:

# Committee routes.

def get_committee(self, epoch_index: Optional[EpochIndex] = None) -> CommitteeResponse:
def get_committee(
self, epoch_index: Optional[EpochIndex] = None) -> CommitteeResponse:
"""Returns the information of committee members at the given epoch index. If epoch index is not provided, the
current committee members are returned.
GET /api/core/v3/committee/?epochIndex
Expand Down
3 changes: 2 additions & 1 deletion bindings/python/iota_sdk/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@ def get_node(self) -> Dict[str, Any]:
def get_protocol_parameters(self) -> ProtocolParameters:
"""Gets the protocol parameters.
"""
return ProtocolParameters.from_dict(self._call_method('getProtocolParameters'))
return ProtocolParameters.from_dict(
self._call_method('getProtocolParameters'))

def get_network_id(self) -> int:
"""Gets the network id of the node we're connecting to.
Expand Down
25 changes: 22 additions & 3 deletions bindings/python/iota_sdk/client/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from iota_sdk.types.block.block import Block
from iota_sdk.types.block.id import BlockId
from iota_sdk.types.common import HexStr, json, EpochIndex, SlotIndex
from iota_sdk.types.node_info import BaseTokenResponse, MetricsResponse, StatusResponse, ProtocolParameters
from iota_sdk.types.node_info import BaseTokenResponse, StatusResponse, ProtocolParameters
from iota_sdk.types.output import Output, deserialize_output
from iota_sdk.types.output_id import OutputId, OutputWithId
from iota_sdk.types.output_id_proof import OutputIdProof
Expand Down Expand Up @@ -56,14 +56,12 @@ class InfoResponse:
name: The name of the node (e.g. Hornet).
version: The semantic version of the node.
status: The status of the node.
metrics: Node metrics.
protocol_parameters: Supported protocol versions by the node.
base_token: Gives info about the base token the network uses.
"""
name: str
version: str
status: StatusResponse
metrics: MetricsResponse
protocol_parameters: List[ProtocolParametersResponse]
base_token: BaseTokenResponse

Expand All @@ -82,8 +80,29 @@ class NodeInfoResponse:
url: str


@json
@dataclass
class NetworkMetricsResponse:
"""Network metrics.
Attributes:
blocks_per_second: The current rate of new blocks per second.
confirmed_blocks_per_second: The current rate of confirmed blocks per second.
confirmation_rate: The ratio of confirmed blocks to new blocks of the last confirmed slot.
"""
blocks_per_second: float = field(metadata=config(
encoder=str
))
confirmed_blocks_per_second: float = field(metadata=config(
encoder=str
))
confirmation_rate: float = field(metadata=config(
encoder=str
))

# Accounts routes responses


@json
@dataclass
class CongestionResponse:
Expand Down
21 changes: 0 additions & 21 deletions bindings/python/iota_sdk/types/node_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,6 @@ class StatusResponse:
pruning_epoch: EpochIndex


@json
@dataclass
class MetricsResponse:
"""Node metrics.
Attributes:
blocks_per_second: The current rate of new blocks per second.
confirmed_blocks_per_second: The current rate of confirmed blocks per second.
confirmation_rate: The ratio of confirmed blocks to new blocks of the last confirmed slot.
"""
blocks_per_second: float = field(metadata=config(
encoder=str
))
confirmed_blocks_per_second: float = field(metadata=config(
encoder=str
))
confirmation_rate: float = field(metadata=config(
encoder=str
))


@json
@dataclass
class StorageScoreParameters:
Expand Down
5 changes: 3 additions & 2 deletions bindings/python/tests/test_api_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from typing import Generic, TypeVar
from json import load, loads, dumps
from iota_sdk import RoutesResponse, CongestionResponse, OutputWithMetadataResponse, ManaRewardsResponse, ValidatorsResponse, ValidatorResponse, InfoResponse, CommitteeResponse, IssuanceBlockHeaderResponse, Block, BlockMetadataResponse, BlockWithMetadataResponse, OutputMetadata, OutputResponse, TransactionMetadataResponse, SlotCommitment, UtxoChangesResponse, UtxoChangesFullResponse
from iota_sdk import RoutesResponse, CongestionResponse, OutputWithMetadataResponse, ManaRewardsResponse, ValidatorsResponse, ValidatorResponse, CommitteeResponse, IssuanceBlockHeaderResponse, Block, BlockMetadataResponse, BlockWithMetadataResponse, OutputMetadata, OutputResponse, TransactionMetadataResponse, SlotCommitment, UtxoChangesResponse, UtxoChangesFullResponse


base_path = '../../sdk/tests/types/api/fixtures/'
Expand All @@ -27,7 +27,8 @@ def test_api_response(cls_type: Generic[T], path: str):
# GET /api/routes
test_api_response(RoutesResponse, "get-routes-response-example.json")
# GET /api/core/v3/info
test_api_response(InfoResponse, "get-info-response-example.json")
# TODO reenable when Metrics are split out of Info
# test_api_response(InfoResponse, "get-info-response-example.json")
# GET /api/core/v3/accounts/{bech32Address}/congestion
test_api_response(CongestionResponse,
"get-congestion-estimate-response-example.json")
Expand Down
15 changes: 12 additions & 3 deletions sdk/src/client/node_api/core/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ use crate::{
types::{
api::core::{
BlockMetadataResponse, BlockWithMetadataResponse, CommitteeResponse, CongestionResponse, InfoResponse,
IssuanceBlockHeaderResponse, ManaRewardsResponse, OutputResponse, OutputWithMetadataResponse,
PermanodeInfoResponse, RoutesResponse, SubmitBlockResponse, TransactionMetadataResponse,
UtxoChangesFullResponse, UtxoChangesResponse, ValidatorResponse, ValidatorsResponse,
IssuanceBlockHeaderResponse, ManaRewardsResponse, NetworkMetricsResponse, OutputResponse,
OutputWithMetadataResponse, PermanodeInfoResponse, RoutesResponse, SubmitBlockResponse,
TransactionMetadataResponse, UtxoChangesFullResponse, UtxoChangesResponse, ValidatorResponse,
ValidatorsResponse,
},
block::{
address::ToBech32Ext,
Expand Down Expand Up @@ -87,6 +88,14 @@ impl ClientInner {
pub async fn get_node_info(&self) -> Result<NodeInfoResponse, ClientError> {
self.get_request(INFO_PATH, None, false).await
}

/// Returns network metrics.
/// GET /api/core/v3/network/metrics
pub async fn get_network_metrics(&self) -> Result<NetworkMetricsResponse, ClientError> {
const PATH: &str = "api/core/v3/network/metrics";

self.get_request(PATH, None, false).await
}
}

impl Client {
Expand Down
6 changes: 2 additions & 4 deletions sdk/src/types/api/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ pub struct InfoResponse {
pub name: String,
pub version: String,
pub status: StatusResponse,
pub metrics: MetricsResponse,
pub protocol_parameters: ProtocolParametersMap,
pub base_token: BaseTokenResponse,
}
Expand Down Expand Up @@ -105,11 +104,10 @@ pub struct StatusResponse {
pub pruning_epoch: EpochIndex,
}

/// Returned in [`InfoResponse`].
/// Metric information about the node.
/// Metrics information about the network.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MetricsResponse {
pub struct NetworkMetricsResponse {
#[serde(with = "string")]
pub blocks_per_second: f64,
#[serde(with = "string")]
Expand Down
3 changes: 2 additions & 1 deletion sdk/tests/types/api/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ fn responses() {
// GET /api/routes
json_response::<RoutesResponse>("get-routes-response-example.json").unwrap();
// GET /api/core/v3/info
json_response::<InfoResponse>("get-info-response-example.json").unwrap();
// TODO reenable when Metrics are split out of Info
// json_response::<InfoResponse>("get-info-response-example.json").unwrap();
// GET /api/core/v3/accounts/{bech32Address}/congestion
json_response::<CongestionResponse>("get-congestion-estimate-response-example.json").unwrap();
// GET /api/core/v3/rewards/{outputId}
Expand Down

0 comments on commit ffb6fc2

Please sign in to comment.