Skip to content

Commit

Permalink
feat: use AddPermissionlessValidatorTx (#90)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nuttymoon authored Feb 13, 2024
2 parents 1d3c15a + 73b9083 commit da808cb
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 40 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@

[workspace]
members = ["crates/ash_cli", "crates/ash_sdk"]
resolver = "2"

[workspace.package]
version = "0.4.0"
version = "0.4.1-rc.1"
edition = "2021"
authors = ["E36 Knots"]
homepage = "https://ash.center"
Expand Down
2 changes: 1 addition & 1 deletion crates/ash_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ categories.workspace = true
keywords.workspace = true

[dependencies]
ash_sdk = { path = "../ash_sdk", version = "0.4.0" }
ash_sdk = { path = "../ash_sdk", version = "0.4.1-rc.1" }
clap = { version = "4.0.32", features = ["derive", "env", "cargo", "string"] }
colored = "2.0.0"
exitcode = "1.1.2"
Expand Down
2 changes: 1 addition & 1 deletion crates/ash_cli/src/avalanche/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ enum BlockchainSubcommands {
/// Private key to sign the transaction with (must be a control key)
#[arg(long, short = 'p', env = "AVALANCHE_PRIVATE_KEY")]
private_key: String,
/// Private key format
/// Private key encoding (cb58 or hex)
#[arg(
long,
short = 'e',
Expand Down
2 changes: 1 addition & 1 deletion crates/ash_cli/src/avalanche/subnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ enum SubnetSubcommands {
/// Private key to sign the transaction with
#[arg(long, short = 'p', env = "AVALANCHE_PRIVATE_KEY")]
private_key: String,
/// Private key format
/// Private key encoding (cb58 or hex)
#[arg(
long,
short = 'e',
Expand Down
70 changes: 61 additions & 9 deletions crates/ash_cli/src/avalanche/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,20 @@ use crate::{
avalanche::{wallet::*, *},
utils::{error::CliError, parsing::*, templating::*, version_tx_cmd},
};
use ash_sdk::avalanche::{subnets::AvalancheSubnetType, AVAX_PRIMARY_NETWORK_ID};
use ash_sdk::avalanche::{
nodes::ProofOfPossession, subnets::AvalancheSubnetType, AVAX_PRIMARY_NETWORK_ID,
};
use async_std::task;
use clap::{Parser, Subcommand};
use chrono::Utc;
use clap::{Parser, Subcommand, ValueEnum};
use std::fmt::Display;

/// Node signer format
#[derive(Display, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub(crate) enum SignerFormat {
Str,
Json,
}

/// Interact with Avalanche validators
#[derive(Parser)]
Expand Down Expand Up @@ -45,26 +56,33 @@ enum ValidatorSubcommands {
id: String,
/// Validator weight (permissioned Subnet) or stake in AVAX (elastic Subnet)
stake_or_weight: u64,
/// Start time of the validation (YYYY-MM-DDTHH:MM:SSZ)
/// Start time of the validation (YYYY-MM-DDTHH:MM:SSZ), defaults to now
#[arg(long, short = 'S')]
start_time: String,
start_time: Option<String>,
/// End time of the validation (YYYY-MM-DDTHH:MM:SSZ)
#[arg(long, short = 'E')]
end_time: String,
/// Delegation fee (percentage)
/// Delegation fee (percentage), defaults to 2%
#[arg(long, short = 'f', default_value = "2")]
delegation_fee: u32,
/// Private key to sign the transaction with
#[arg(long, short = 'p', env = "AVALANCHE_PRIVATE_KEY")]
private_key: String,
/// Private key format
/// Private key encoding (cb58 or hex)
#[arg(
long,
short = 'e',
default_value = "cb58",
env = "AVALANCHE_KEY_ENCODING"
)]
key_encoding: PrivateKeyEncoding,
/// Signer (BLS public key and PoP) in "public_key:PoP" or JSON format
/// (e.g. '{"publicKey":"public_key","proofOfPossession":"pop"}')
#[arg(long, short = 'B')]
signer: Option<String>,
/// Signer format (str or json)
#[arg(long, short = 'F', default_value = "str")]
signer_format: SignerFormat,
/// Whether to wait for transaction acceptance
#[arg(long, short = 'w')]
wait: bool,
Expand Down Expand Up @@ -176,18 +194,43 @@ fn add(
subnet_id: &str,
id: &str,
stake_or_weight: u64,
start_time: String,
start_time: Option<String>,
end_time: String,
delegation_fee: u32,
private_key: &str,
key_encoding: PrivateKeyEncoding,
signer: Option<String>,
signer_format: SignerFormat,
wait: bool,
config: Option<&str>,
json: bool,
) -> Result<(), CliError> {
let node_id_parsed = parse_node_id(id)?;
let start_time_parsed = parse_datetime(&start_time)?;
let start_time_parsed = match start_time {
Some(start_time) => parse_datetime(&start_time)?,
None => Utc::now(),
};
let end_time_parsed = parse_datetime(&end_time)?;
let signer_parsed = match signer.clone() {
Some(signer_str) => match signer_format {
SignerFormat::Str => {
let parts: Vec<&str> = signer_str.split(':').collect();
if parts.len() != 2 {
return Err(CliError::dataerr(
"Signer must be in the format 'public_key:PoP'".to_string(),
));
}
serde_json::from_value::<ProofOfPossession>(serde_json::json!({
"publicKey": parts[0],
"proofOfPossession": parts[1]
}))
.map_err(|e| CliError::dataerr(format!("Error parsing signer: {e}")))?
}
SignerFormat::Json => serde_json::from_str(&signer_str)
.map_err(|e| CliError::dataerr(format!("Error parsing signer: {e}")))?,
},
None => ProofOfPossession::default(),
};

let mut network = load_network(network_name, config)?;
update_network_subnets(&mut network)?;
Expand All @@ -204,14 +247,19 @@ fn add(
let validator = match subnet.subnet_type {
AvalancheSubnetType::PrimaryNetwork => task::block_on(async {
subnet
.add_avalanche_validator(
.add_validator_permissionless(
&wallet,
node_id_parsed,
subnet.id,
// Multiply by 1 billion to convert from AVAX to nAVAX
stake_or_weight * 1_000_000_000,
start_time_parsed,
end_time_parsed,
delegation_fee,
match signer {
Some(_) => Some(signer_parsed),
None => None,
},
wait,
)
.await
Expand Down Expand Up @@ -261,6 +309,8 @@ pub(crate) fn parse(
delegation_fee,
private_key,
key_encoding,
signer,
signer_format,
wait,
} => add(
&validator.network,
Expand All @@ -272,6 +322,8 @@ pub(crate) fn parse(
delegation_fee,
&private_key,
key_encoding,
signer,
signer_format,
wait,
config,
json,
Expand Down
2 changes: 1 addition & 1 deletion crates/ash_cli/src/avalanche/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ enum WalletSubcommands {
/// Private key of the wallet
#[arg(env = "AVALANCHE_PRIVATE_KEY")]
private_key: String,
/// Private key format
/// Private key encoding (cb58 or hex)
#[arg(
long,
short = 'e',
Expand Down
2 changes: 1 addition & 1 deletion crates/ash_cli/src/avalanche/x.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ enum XSubcommands {
/// Private key to sign the transaction with
#[arg(long, short = 'p', env = "AVALANCHE_PRIVATE_KEY")]
private_key: String,
/// Private key format
/// Private key encoding (cb58 or hex)
#[arg(
long,
short = 'e',
Expand Down
11 changes: 11 additions & 0 deletions crates/ash_cli/src/utils/templating.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ pub(crate) fn template_validator_info(
let elastic_subnet_info = &formatdoc!(
"
Connected: {}
Signer (BLS):
Public key: {}
PoP: {}
Uptime: {}
Stake amount: {}
Potential reward: {}
Expand All @@ -175,6 +178,14 @@ pub(crate) fn template_validator_info(
Threshold: {}
Addresses: {}",
type_colorize(&validator.connected),
type_colorize(&match validator.signer {
Some(ref signer) => format!("0x{}", hex::encode(signer.public_key.clone())),
None => String::from("None"),
}),
type_colorize(&match validator.signer {
Some(ref signer) => format!("0x{}", hex::encode(signer.proof_of_possession.clone())),
None => String::from("None"),
}),
type_colorize(&validator.uptime.unwrap_or_default()),
type_colorize(&validator.stake_amount.unwrap_or_default()),
type_colorize(&validator.potential_reward.unwrap_or_default()),
Expand Down
3 changes: 1 addition & 2 deletions crates/ash_sdk/src/avalanche/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@
// Module that contains code to interact with Avalanche nodes

use crate::{avalanche::jsonrpc::info::*, errors::*};
pub use avalanche_types::key::bls::private_key::Key as BlsPrivateKey;
pub use avalanche_types::key::bls::{private_key::Key as BlsPrivateKey, ProofOfPossession};
use avalanche_types::{
ids::node::Id as NodeId,
jsonrpc::info::{GetNodeVersionResult, UptimeResult, VmVersions},
key::bls::ProofOfPossession,
};
use rcgen::{Certificate, CertificateParams, DistinguishedName, DnType, PKCS_RSA_SHA256};
use rustls_pemfile::certs;
Expand Down
25 changes: 11 additions & 14 deletions crates/ash_sdk/src/avalanche/subnets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::{
use avalanche_types::{
ids::{node::Id as NodeId, Id},
jsonrpc::platformvm::{ApiPrimaryDelegator, ApiPrimaryValidator},
key::bls::ProofOfPossession,
utils::urls::extract_scheme_host_port_path_chain_alias,
};
use chrono::{DateTime, Utc};
Expand Down Expand Up @@ -121,35 +122,28 @@ impl AvalancheSubnet {
})
}

/// Add a validator to the Primary Network
/// Fail if the Subnet is not the Primary Network
pub async fn add_avalanche_validator(
/// Add a validator a permissionless Subnet
pub async fn add_validator_permissionless(
&self,
wallet: &AvalancheWallet,
node_id: NodeId,
subnet_id: Id,
stake_amount: u64,
start_time: DateTime<Utc>,
end_time: DateTime<Utc>,
reward_fee_percent: u32,
signer: Option<ProofOfPossession>,
check_acceptance: bool,
) -> Result<AvalancheSubnetValidator, AshError> {
// Check if the Subnet is the Primary Network
if self.subnet_type != AvalancheSubnetType::PrimaryNetwork {
return Err(AvalancheSubnetError::OperationNotAllowed {
operation: "add_avalanche_validator".to_string(),
subnet_id: self.id.to_string(),
subnet_type: self.subnet_type.to_string(),
}
.into());
}

let tx_id = p::add_avalanche_validator(
let tx_id = p::add_permissionless_subnet_validator(
wallet,
node_id,
subnet_id,
stake_amount,
start_time,
end_time,
reward_fee_percent,
signer,
check_acceptance,
)
.await?;
Expand Down Expand Up @@ -370,6 +364,8 @@ pub struct AvalancheSubnetValidator {
pub potential_reward: Option<u64>,
pub connected: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub signer: Option<ProofOfPossession>,
#[serde(skip_serializing_if = "Option::is_none")]
pub uptime: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub validation_reward_owner: Option<AvalancheOutputOwners>,
Expand Down Expand Up @@ -397,6 +393,7 @@ impl AvalancheSubnetValidator {
weight: validator.weight,
potential_reward: validator.potential_reward,
connected: validator.connected,
signer: validator.signer.clone(),
uptime: validator.uptime,
validation_reward_owner: validator
.validation_reward_owner
Expand Down
Loading

0 comments on commit da808cb

Please sign in to comment.