Skip to content
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
137 changes: 9 additions & 128 deletions src/node-control/commands/src/commands/nodectl/config_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,17 @@
* This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND.
*/
use super::{
config_bind_cmd::BindCmd,
config_elections_cmd::ElectionsCfgCmd,
config_log_cmd::LogCmd,
config_node_cmd::NodeCmd,
config_pool_cmd::PoolCmd,
config_ton_http_api_cmd::TonHttpApiCmd,
config_wallet_cmd::WalletCmd,
master_wallet_cmd::MasterWalletCmd,
utils::{require_config, save_config},
config_bind_cmd::BindCmd, config_elections_cmd::ElectionsCfgCmd, config_log_cmd::LogCmd,
config_node_cmd::NodeCmd, config_pool_cmd::PoolCmd, config_ton_http_api_cmd::TonHttpApiCmd,
config_wallet_cmd::WalletCmd, master_wallet_cmd::MasterWalletCmd, utils::save_config,
};
use anyhow::Context;
use common::{
TonWalletVersion,
app_config::{
AppConfig, ElectionsConfig, HttpConfig, KeyConfig, LogConfig, StakePolicy,
TonHttpApiConfig, WalletConfig,
AppConfig, ElectionsConfig, HttpConfig, KeyConfig, LogConfig, TonHttpApiConfig,
WalletConfig,
},
task_cancellation::CancellationCtx,
ton_utils::tons_f64_to_nanotons,
};
use std::{collections::HashMap, path::Path};

Expand All @@ -47,8 +39,9 @@ pub struct ConfigCmd {
short = 'u',
long = "url",
value_hint = clap::ValueHint::Url,
help = "URL to the node control service API (takes precedence over --config; defaults to http://127.0.0.1:8080 if not --url and --config are provided)",
global = true
help = "URL to the node control service API (takes precedence over --config; defaults to http://127.0.0.1:8080 if not --url, --config, or NODECTL_URL environment variable are provided)",
env = "NODECTL_URL",
global = true,
)]
url: Option<String>,

Expand Down Expand Up @@ -86,8 +79,6 @@ pub enum ConfigAction {
Elections(ElectionsCfgCmd),
/// Manage log configuration
Log(LogCmd),
/// Set the stake policy (shortcut for `elections stake-policy`)
StakePolicy(StakePolicyCmd),
}

#[derive(clap::Args, Clone)]
Expand All @@ -101,26 +92,6 @@ pub struct GenerateCmd {
force: bool,
}

#[derive(clap::Args, Clone)]
pub struct StakePolicyCmd {
#[arg(long = "fixed", conflicts_with_all = ["split50", "minimum", "adaptive_split50"], help = "Fixed stake amount in TON")]
fixed: Option<f64>,
#[arg(long = "split50", conflicts_with_all = ["fixed", "minimum", "adaptive_split50"], help = "Use 50% of available balance")]
split50: bool,
#[arg(long = "minimum", conflicts_with_all = ["fixed", "split50", "adaptive_split50"], help = "Use minimum required stake")]
minimum: bool,
#[arg(long = "adaptive-split50", conflicts_with_all = ["fixed", "split50", "minimum"], help = "Adaptive split: splits when half exceeds effective minimum, otherwise stakes all")]
adaptive_split50: bool,
#[arg(
short = 'n',
long = "node",
help = "Apply policy only to this node (override). Omit to set the default policy for all nodes."
)]
node: Option<String>,
#[arg(long = "reset", help = "Remove a per-node policy override (requires --node)")]
reset: bool,
}

impl ConfigCmd {
pub async fn run(&self, cancellation_ctx: CancellationCtx) -> anyhow::Result<()> {
let url = self.url.as_deref();
Expand All @@ -133,11 +104,10 @@ impl ConfigCmd {
ConfigAction::Wallet(cmd) => cmd.run(config_path, cancellation_ctx, url, token).await,
ConfigAction::Pool(cmd) => cmd.run(config_path, url, token).await,
ConfigAction::Bind(cmd) => cmd.run(config_path, url, token).await,
ConfigAction::TonHttpApi(cmd) => cmd.run(require_config(config_path)?).await,
ConfigAction::TonHttpApi(cmd) => cmd.run(url, token, config_path).await,
ConfigAction::MasterWallet(cmd) => cmd.run(url, token, config_path).await,
ConfigAction::Elections(cmd) => cmd.run(config_path, url, token).await,
ConfigAction::Log(cmd) => cmd.run(config_path, url, token).await,
ConfigAction::StakePolicy(cmd) => cmd.run(require_config(config_path)?).await,
}
}
}
Expand Down Expand Up @@ -175,92 +145,3 @@ impl GenerateCmd {
Ok(())
}
}

impl StakePolicyCmd {
pub async fn run(&self, path: &Path) -> anyhow::Result<()> {
let mut config = AppConfig::load(path)?;

// Handle clearing a per-node override
if self.reset {
let node_id = self
.node
.as_ref()
.ok_or_else(|| anyhow::anyhow!("--reset requires --node <NODE>"))?;
config
.elections
.as_mut()
.ok_or_else(|| anyhow::anyhow!("Elections are not configured"))?
.policy_overrides
.remove(node_id);
save_config(&config, path).context("failed to write config file")?;
let result = StakePolicyClearResult {
ok: true,
node: node_id.clone(),
policy: config.elections.as_ref().map(|e| e.policy.clone()).unwrap_or_default(),
};
println!("{}", serde_json::to_string_pretty(&result)?);
return Ok(());
}

let policy = if let Some(tons) = self.fixed {
StakePolicy::Fixed(tons_f64_to_nanotons(tons))
} else if self.split50 {
StakePolicy::Split50
} else if self.minimum {
StakePolicy::Minimum
} else if self.adaptive_split50 {
StakePolicy::AdaptiveSplit50
} else {
anyhow::bail!(
"No policy specified. Use --fixed, --split50, --minimum, or --adaptive-split50"
);
};

// Update elections config
if let Some(elections) = &mut config.elections {
if let Some(node_id) = &self.node {
elections.policy_overrides.insert(node_id.clone(), policy.clone());
} else {
// Default policy for all nodes
elections.policy = policy.clone();
}
} else {
if self.node.is_some() {
anyhow::bail!(
"Elections are not configured. Set a default policy first before adding per-node overrides."
);
}
config.elections =
Some(ElectionsConfig { policy: policy.clone(), ..Default::default() });
}

save_config(&config, path)?;

let result = StakePolicyResult {
ok: true,
config: path.display().to_string(),
node: self.node.clone(),
policy,
};

println!("{}", serde_json::to_string_pretty(&result)?);
Ok(())
}
}

#[derive(serde::Serialize)]
struct StakePolicyResult {
ok: bool,
config: String,
/// If set, the policy was applied as a per-node override.
#[serde(skip_serializing_if = "Option::is_none")]
node: Option<String>,
policy: StakePolicy,
}

#[derive(serde::Serialize)]
struct StakePolicyClearResult {
ok: bool,
node: String,
policy: StakePolicy,
}
Loading
Loading