pub fn is_public_network(network: &str) -> bool {
matches!(
network.trim().to_lowercase().as_str(),
"mainnet" | "public" | "pubnet" | "main"
)
}
pub fn mainnet_allowed() -> bool {
if let Ok(value) = std::env::var("PROCYON_ALLOW_MAINNET") {
return matches!(value.trim().to_lowercase().as_str(), "1" | "true" | "yes");
}
crate::config::AppConfig::load()
.map(|config| config.allow_mainnet)
.unwrap_or(false)
}
pub fn resolve_signing_network(network: Option<&str>) -> Result<String, String> {
let Some(network) = network.map(str::trim).filter(|n| !n.is_empty()) else {
return Err(
"'network' must be stated for an operation that signs and submits. Name the network \
explicitly (e.g. testnet) rather than relying on a default this tool cannot see."
.to_string(),
);
};
if is_public_network(network) && !mainnet_allowed() {
return Err(format!(
"Refusing to sign on '{}': mainnet operations are disabled. This spends real funds, \
so it is off unless the operator turns it on out of band — set `allow_mainnet = true` \
in ~/.config/procyon/config.toml, or PROCYON_ALLOW_MAINNET=1 in the environment. \
Ask the user to do it; you cannot enable it yourself. Nothing was submitted.",
network
));
}
Ok(network.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_public_network_is_recognised_by_its_common_spellings() {
for name in [
"mainnet",
"MAINNET",
" Mainnet ",
"public",
"pubnet",
"main",
] {
assert!(is_public_network(name), "missed {:?}", name);
}
}
#[test]
fn other_networks_are_not_the_public_one() {
for name in ["testnet", "local", "futurenet", "standalone", ""] {
assert!(!is_public_network(name), "false positive on {:?}", name);
}
}
#[test]
fn a_signing_operation_must_name_its_network() {
for absent in [None, Some(""), Some(" ")] {
let err = resolve_signing_network(absent)
.expect_err("an unstated network must not be assumed");
assert!(err.contains("must be stated"), "{}", err);
}
}
#[test]
fn a_non_public_network_passes_through() {
assert_eq!(resolve_signing_network(Some("testnet")).unwrap(), "testnet");
assert_eq!(resolve_signing_network(Some(" local ")).unwrap(), "local");
}
#[test]
fn mainnet_is_refused_unless_the_operator_enabled_it() {
let restore = std::env::var("PROCYON_ALLOW_MAINNET").ok();
std::env::remove_var("PROCYON_ALLOW_MAINNET");
if let Err(err) = resolve_signing_network(Some("mainnet")) {
assert!(err.contains("real funds"), "{}", err);
assert!(err.contains("allow_mainnet"), "{}", err);
assert!(
err.contains("cannot enable it yourself"),
"the message must close the door on the model asking itself: {}",
err
);
assert!(err.contains("Nothing was submitted"), "{}", err);
}
std::env::set_var("PROCYON_ALLOW_MAINNET", "1");
assert_eq!(resolve_signing_network(Some("mainnet")).unwrap(), "mainnet");
std::env::set_var("PROCYON_ALLOW_MAINNET", "0");
assert!(
resolve_signing_network(Some("mainnet")).is_err(),
"an explicit 0 must not enable mainnet"
);
match restore {
Some(value) => std::env::set_var("PROCYON_ALLOW_MAINNET", value),
None => std::env::remove_var("PROCYON_ALLOW_MAINNET"),
}
}
}