Skip to content

Commit

Permalink
feat: allow to get pending validators
Browse files Browse the repository at this point in the history
  • Loading branch information
Nuttymoon committed Jun 4, 2023
1 parent 377ad14 commit fc6351c
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 25 deletions.
20 changes: 10 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions crates/ash_cli/src/avalanche.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@ fn update_subnet_validators(
Ok(())
}

// Update a Subnet's pending validators
fn update_subnet_pending_validators(
network: &mut AvalancheNetwork,
subnet_id: &str,
) -> Result<(), CliError> {
network
.update_subnet_pending_validators(parse_id(subnet_id)?)
.map_err(|e| CliError::dataerr(format!("Error updating pending validators: {e}")))?;
Ok(())
}

// Parse avalanche subcommand
pub(crate) fn parse(
avalanche: AvalancheCommand,
Expand Down
58 changes: 45 additions & 13 deletions crates/ash_cli/src/avalanche/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ enum ValidatorSubcommands {
},
/// List the Subnet's validators
#[command()]
List,
List {
/// List pending validators
#[arg(long, short = 'p')]
pending: bool,
},
/// Show validator information
#[command()]
Info {
Expand All @@ -84,28 +88,50 @@ enum ValidatorSubcommands {
fn list(
network_name: &str,
subnet_id: &str,
pending: bool,
config: Option<&str>,
json: bool,
) -> Result<(), CliError> {
let mut network = load_network(network_name, config)?;
update_network_subnets(&mut network)?;
update_subnet_validators(&mut network, subnet_id)?;
let subnet;
let validators;
let first_line;

let subnet = network
.get_subnet(parse_id(subnet_id)?)
.map_err(|e| CliError::dataerr(format!("Error listing validators: {e}")))?;
match pending {
true => {
update_subnet_pending_validators(&mut network, subnet_id)?;
subnet = network
.get_subnet(parse_id(subnet_id)?)
.map_err(|e| CliError::dataerr(format!("Error listing validators: {e}")))?;
validators = subnet.pending_validators.clone();
first_line = format!(
"Found {} pending validator(s) on Subnet '{}':",
type_colorize(&subnet.validators.len()),
type_colorize(&subnet_id)
)
}
false => {
update_subnet_validators(&mut network, subnet_id)?;
subnet = network
.get_subnet(parse_id(subnet_id)?)
.map_err(|e| CliError::dataerr(format!("Error listing validators: {e}")))?;
validators = subnet.validators.clone();
first_line = format!(
"Found {} validator(s) on Subnet '{}':",
type_colorize(&subnet.validators.len()),
type_colorize(&subnet_id)
)
}
}

if json {
println!("{}", serde_json::to_string(&subnet.validators).unwrap());
println!("{}", serde_json::to_string(&validators).unwrap());
return Ok(());
}

println!(
"Found {} validator(s) on Subnet '{}':",
type_colorize(&subnet.validators.len()),
type_colorize(&subnet_id)
);
for validator in subnet.validators.iter() {
println!("{}", first_line);
for validator in validators.iter() {
println!(
"{}",
template_validator_info(validator, subnet, true, true, 2)
Expand Down Expand Up @@ -254,6 +280,12 @@ pub(crate) fn parse(
ValidatorSubcommands::Info { id } => {
info(&validator.network, &validator.subnet_id, &id, config, json)
}
ValidatorSubcommands::List => list(&validator.network, &validator.subnet_id, config, json),
ValidatorSubcommands::List { pending } => list(
&validator.network,
&validator.subnet_id,
pending,
config,
json,
),
}
}
28 changes: 28 additions & 0 deletions crates/ash_sdk/src/avalanche.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,34 @@ impl AvalancheNetwork {
Ok(())
}

/// Update the pending validators of a Subnet by querying an API endpoint
pub fn update_subnet_pending_validators(&mut self, subnet_id: Id) -> Result<(), AshError> {
let rpc_url = &self.get_pchain()?.rpc_url;

let validators = platformvm::get_pending_validators(rpc_url, subnet_id)?;

// Replace the pending validators of the Subnet
let mut subnet = self.get_subnet(subnet_id)?.clone();

subnet.pending_validators = validators;

// Get the index of the Subnet
let subnet_index = self
.subnets
.iter()
.position(|subnet| subnet.id == subnet_id)
.ok_or(AvalancheNetworkError::NotFound {
network: self.name.clone(),
target_type: "Subnet".to_string(),
target_value: subnet_id.to_string(),
})?;

// Replace the Subnet
self.subnets[subnet_index] = subnet;

Ok(())
}

/// Check if the operation is allowed on the network
/// If not, return an error
fn check_operation_allowed(
Expand Down
44 changes: 43 additions & 1 deletion crates/ash_sdk/src/avalanche/jsonrpc/platformvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use crate::avalanche::{
blockchains::AvalancheBlockchain,
jsonrpc::{get_json_rpc_req_result, JsonRpcResponse},
subnets::{AvalancheSubnet, AvalancheSubnetValidator},
subnets::{AvalancheSubnet, AvalancheSubnetDelegator, AvalancheSubnetValidator},
};
use crate::{errors::*, impl_json_rpc_response};
use avalanche_types::{
Expand Down Expand Up @@ -51,6 +51,7 @@ impl_json_rpc_response!(
);
impl_json_rpc_response!(GetBlockchainsResponse, GetBlockchainsResult);
impl_json_rpc_response!(GetCurrentValidatorsResponse, GetCurrentValidatorsResult);
impl_json_rpc_response!(GetPendingValidatorsResponse, GetPendingValidatorsResult);

/// Get the Subnets of the network by querying the P-Chain API
pub fn get_network_subnets(
Expand Down Expand Up @@ -123,6 +124,47 @@ pub fn get_current_validators(
Ok(current_validators)
}

/// Get the pending validators of a Subnet by querying the P-Chain API
pub fn get_pending_validators(
rpc_url: &str,
subnet_id: Id,
) -> Result<Vec<AvalancheSubnetValidator>, RpcError> {
let pending_validators_result: GetPendingValidatorsResult =
get_json_rpc_req_result::<GetPendingValidatorsResponse, GetPendingValidatorsResult>(
rpc_url,
"platform.getPendingValidators",
Some(ureq::json!({ "subnetID": subnet_id.to_string() })),
)?;

let mut pending_validators: Vec<AvalancheSubnetValidator> = pending_validators_result
.validators
.iter()
.map(|validator| AvalancheSubnetValidator::from_api_primary_validator(validator, subnet_id))
.collect();
let pending_validators_iter = pending_validators.clone();

// For each pending validator, add related delegators
for pending_validator in pending_validators_iter.iter() {
let delegators: Vec<AvalancheSubnetDelegator> = pending_validators_result
.delegators
.iter()
.filter(|delegator| delegator.node_id == pending_validator.node_id)
.cloned()
.map(Into::into)
.collect();

if !delegators.is_empty() {
pending_validators
.iter_mut()
.find(|validator| validator.node_id == pending_validator.node_id)
.unwrap()
.delegators = Some(delegators);
}
}

Ok(pending_validators)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
16 changes: 15 additions & 1 deletion crates/ash_sdk/src/avalanche/subnets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ pub struct AvalancheSubnet {
/// List of the Subnet's blockchains
pub blockchains: Vec<AvalancheBlockchain>,
/// List of the Subnet's validators
#[serde(default, skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
pub validators: Vec<AvalancheSubnetValidator>,
#[serde(default)]
pub pending_validators: Vec<AvalancheSubnetValidator>,
}

impl AvalancheSubnet {
Expand Down Expand Up @@ -241,16 +243,26 @@ pub struct AvalancheSubnetValidator {
// TODO: Store as DateTime::<Utc>?
pub start_time: u64,
pub end_time: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub stake_amount: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub weight: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub potential_reward: Option<u64>,
pub connected: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub uptime: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub validation_reward_owner: Option<AvalancheOutputOwners>,
#[serde(skip_serializing_if = "Option::is_none")]
pub delegator_count: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub delegator_weight: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub delegators: Option<Vec<AvalancheSubnetDelegator>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub delegation_fee: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub delegation_reward_owner: Option<AvalancheOutputOwners>,
}

Expand Down Expand Up @@ -299,7 +311,9 @@ pub struct AvalancheSubnetDelegator {
pub start_time: u64,
pub end_time: u64,
pub stake_amount: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub potential_reward: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reward_owner: Option<AvalancheOutputOwners>,
}

Expand Down

0 comments on commit fc6351c

Please sign in to comment.