Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use AddPermissionlessValidatorTx #90

Merged
merged 4 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
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 @@
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 @@
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 @@
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 {

Check warning on line 259 in crates/ash_cli/src/avalanche/validator.rs

View workflow job for this annotation

GitHub Actions / Test

manual implementation of `Option::map`
Some(_) => Some(signer_parsed),
None => None,
},
wait,
)
.await
Expand Down Expand Up @@ -261,6 +309,8 @@
delegation_fee,
private_key,
key_encoding,
signer,
signer_format,
wait,
} => add(
&validator.network,
Expand All @@ -272,6 +322,8 @@
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
Loading