Skip to content

Commit

Permalink
Validator route pagination (#2101)
Browse files Browse the repository at this point in the history
* little improvements

* add get_validators example with optional pagination

* the example

* fix example doc

* update

* python: add example

* nodejs: add example

* update year

Co-authored-by: Thoralf-M <[email protected]>

* rust: update example

* python: update example

* nodejs: update example

* python: nit

* docs

* fix

* single letter closures

---------

Co-authored-by: Thoralf-M <[email protected]>
Co-authored-by: Thibault Martinez <[email protected]>
  • Loading branch information
3 people authored Mar 5, 2024
1 parent 9e70832 commit 12e5ec8
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 16 deletions.
42 changes: 42 additions & 0 deletions bindings/nodejs/examples/client/get-validators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { Client, initLogger } from '@iota/sdk';
require('dotenv').config({ path: '.env' });

// Run with command:
// yarn run-example ./client/get-validators.ts [PAGE_SIZE] [CURSOR]

// This example returns the validators known by the node by querying the corresponding endpoint.
// You can provide a custom PAGE_SIZE and additionally a CURSOR from a previous request.
async function run() {
initLogger();
for (const envVar of ['NODE_URL']) {
if (!(envVar in process.env)) {
throw new Error(`.env ${envVar} is undefined, see .env.example`);
}
}

const client = await Client.create({
// Insert your node URL in the .env.
nodes: [process.env.NODE_URL as string],
});

let pageSize = 1;
let cursor = '';
if (process.argv.length > 1) {
pageSize = parseInt(process.argv[2]);
if (process.argv.length > 2) {
cursor = process.argv[3];
}
}

try {
const validators = await client.getValidators(pageSize, cursor);
console.log(validators);
} catch (error) {
console.error('Error: ', error);
}
}

void run().then(() => process.exit());
24 changes: 24 additions & 0 deletions bindings/python/examples/client/get_validators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import dataclasses
import json
import os
import sys

from dotenv import load_dotenv
from iota_sdk import Client

load_dotenv()

node_url = os.environ.get('NODE_URL', 'https://api.testnet.shimmer.network')
page_size = 1
cursor = ""

if len(sys.argv) > 1:
page_size = int(sys.argv[1])
if len(sys.argv) > 2:
cursor = sys.argv[2]

# Create a Client instance
client = Client(nodes=[node_url])

validators = client.get_validators(page_size, cursor)
print(f'{json.dumps(dataclasses.asdict(validators), indent=4)}')
5 changes: 5 additions & 0 deletions sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,11 @@ name = "node_api_core_get_included_block_raw"
path = "examples/client/node_api_core/16_get_included_block_raw.rs"
required-features = ["client"]

[[example]]
name = "node_api_core_get_validators"
path = "examples/client/node_api_core/get_validators.rs"
required-features = ["client"]

# Node API indexer examples

[[example]]
Expand Down
40 changes: 40 additions & 0 deletions sdk/examples/client/node_api_core/get_validators.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

//! This example returns the validators known by the node by querying the corresponding endpoint.
//! You can provide a custom PAGE_SIZE and additionally a CURSOR from a previous request.
//!
//! Rename `.env.example` to `.env` first, then run the command:
//! ```sh
//! cargo run --release --all-features --example node_api_core_get_validators [PAGE_SIZE] [CURSOR] [NODE_URL]
//! ```

use iota_sdk::client::{Client, ClientError};

#[tokio::main]
async fn main() -> Result<(), ClientError> {
// If not provided we use the default node from the `.env` file.
dotenvy::dotenv().ok();

let page_size = std::env::args().nth(1).map(|s| s.parse::<u32>().unwrap());
let cursor = std::env::args().nth(2);

// Take the node URL from command line argument or use one from env as default.
let node_url = std::env::args()
.nth(3)
.unwrap_or_else(|| std::env::var("NODE_URL").expect("NODE_URL not set"));

// Create a node client.
let client = Client::builder()
.with_node(&node_url)?
.with_ignore_node_health()
.finish()
.await?;

// Get validators.
let validators = client.get_validators(page_size, cursor).await?;

println!("{validators:#?}");

Ok(())
}
6 changes: 3 additions & 3 deletions sdk/src/client/node_api/core/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ impl Client {
) -> Result<CongestionResponse, ClientError> {
let bech32_address = account_id.to_bech32(self.get_bech32_hrp().await?);
let path = &format!("api/core/v3/accounts/{bech32_address}/congestion");
let query = query_tuples_to_query_string([work_score.into().map(|i| ("workScore", i.to_string()))]);
let query = query_tuples_to_query_string([work_score.into().map(|s| ("workScore", s.to_string()))]);

self.get_request(path, query.as_deref(), false).await
}
Expand Down Expand Up @@ -138,8 +138,8 @@ impl Client {
) -> Result<ValidatorsResponse, ClientError> {
const PATH: &str = "api/core/v3/validators";
let query = query_tuples_to_query_string([
page_size.into().map(|i| ("pageSize", i.to_string())),
cursor.into().map(|i| ("cursor", i)),
page_size.into().map(|n| ("pageSize", n.to_string())),
cursor.into().map(|c| ("cursor", c)),
]);

self.get_request(PATH, query.as_deref(), false).await
Expand Down
4 changes: 2 additions & 2 deletions sdk/src/client/node_api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ pub(crate) fn query_tuples_to_query_string(
) -> Option<String> {
let query = tuples
.into_iter()
.filter_map(|tuple| tuple.map(|(key, value)| format!("{}={}", key, value)))
.filter_map(|tuple| tuple.map(|(key, value)| format!("{key}={value}")))
.collect::<Vec<_>>();

if query.is_empty() { None } else { Some(query.join("&")) }
(!query.is_empty()).then_some(query.join("&"))
}
22 changes: 11 additions & 11 deletions sdk/src/types/api/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,24 +205,24 @@ pub struct BaseTokenResponse {
#[serde(rename_all = "camelCase")]
pub struct ValidatorResponse {
/// Account address of the validator.
address: Bech32Address,
pub address: Bech32Address,
/// The epoch index until which the validator registered to stake.
staking_end_epoch: EpochIndex,
pub staking_end_epoch: EpochIndex,
/// The total stake of the pool, including delegators.
#[serde(with = "string")]
pool_stake: u64,
pub pool_stake: u64,
/// The stake of a validator.
#[serde(with = "string")]
validator_stake: u64,
pub validator_stake: u64,
/// The fixed cost of the validator, which it receives as part of its Mana rewards.
#[serde(with = "string")]
fixed_cost: u64,
pub fixed_cost: u64,
/// Shows whether the validator was active recently.
active: bool,
pub active: bool,
/// The latest protocol version the validator supported.
latest_supported_protocol_version: u8,
pub latest_supported_protocol_version: u8,
/// The protocol hash of the latest supported protocol of the validator.
latest_supported_protocol_hash: ProtocolParametersHash,
pub latest_supported_protocol_hash: ProtocolParametersHash,
}

/// Response of GET /api/core/v3/blocks/validators.
Expand All @@ -232,13 +232,13 @@ pub struct ValidatorResponse {
#[serde(rename_all = "camelCase")]
pub struct ValidatorsResponse {
/// List of registered validators ready for the next epoch.
validators: Vec<ValidatorResponse>,
pub validators: Vec<ValidatorResponse>,
/// The number of validators returned per one API request with pagination.
page_size: u32,
pub page_size: u32,
/// The cursor that needs to be provided as cursor query parameter to request the next page. If empty, this was the
/// last page.
#[serde(default, skip_serializing_if = "Option::is_none")]
cursor: Option<String>,
pub cursor: Option<String>,
}

/// Response of GET /api/core/v3/rewards/{outputId}.
Expand Down

0 comments on commit 12e5ec8

Please sign in to comment.