montana/Montana-Protocol/Code/crates/montana-node/src/commands/init.rs

94 lines
3.2 KiB
Rust
Raw Normal View History

use std::path::PathBuf;
use crate::identity::{default_data_dir, save_identity, Identity, NodeError, IDENTITY_FILE_SIZE};
pub struct InitArgs {
pub data_dir: Option<PathBuf>,
pub mnemonic: Option<String>,
pub entropy_hex: Option<String>,
pub force: bool,
}
pub fn run(args: InitArgs) -> Result<(), NodeError> {
let data_dir = args.data_dir.unwrap_or_else(default_data_dir);
let identity = match (args.mnemonic.as_deref(), args.entropy_hex.as_deref()) {
(Some(_), Some(_)) => {
return Err(NodeError::InvalidArguments(
"укажите либо --mnemonic, либо --entropy, не оба сразу".into(),
))
},
(Some(m), None) => Identity::from_mnemonic(m.trim())?,
(None, Some(hex)) => {
let entropy = parse_entropy_hex(hex)?;
Identity::from_entropy(&entropy)?
},
(None, None) => {
let mut entropy = [0u8; 32];
getrandom::getrandom(&mut entropy)
.map_err(|e| NodeError::InvalidArguments(format!("getrandom: {e}")))?;
Identity::from_entropy(&entropy)?
},
};
let path = save_identity(&data_dir, &identity, args.force)?;
println!("=== montana-node init ===");
println!();
println!("data-dir : {}", data_dir.display());
println!("identity : {}", path.display());
println!("file size : {IDENTITY_FILE_SIZE} bytes");
println!("mode : 0600 (только владелец)");
println!();
println!("--- мнемоника (24 слова — запишите в надёжное место) ---");
print_mnemonic_grid(&identity.mnemonic);
println!();
println!("--- терминальные идентификаторы ---");
println!("account_id : {}", hex_lower(&identity.account_id()));
println!("node_id : {}", hex_lower(&identity.node_id()));
println!(
"master_seed_fp : {} (8-байтный отпечаток, не секрет)",
hex_lower(&identity.master_seed_fingerprint())
);
println!();
println!("Секретные ключи (account_sk/node_sk/mlkem_sk) сохранены в identity.bin");
println!("и не выводятся на экран. Для backup используйте мнемонику выше.");
Ok(())
}
fn parse_entropy_hex(s: &str) -> Result<[u8; 32], NodeError> {
let clean: String = s.chars().filter(|c| !c.is_whitespace()).collect();
if clean.len() != 64 {
return Err(NodeError::InvalidEntropyHex);
}
let mut out = [0u8; 32];
for i in 0..32 {
let byte = u8::from_str_radix(&clean[i * 2..i * 2 + 2], 16)
.map_err(|_| NodeError::InvalidEntropyHex)?;
out[i] = byte;
}
Ok(out)
}
fn hex_lower(bytes: &[u8]) -> String {
let mut s = String::with_capacity(bytes.len() * 2);
for b in bytes {
s.push_str(&format!("{b:02x}"));
}
s
}
fn print_mnemonic_grid(mnemonic: &str) {
let words: Vec<&str> = mnemonic.split(' ').collect();
for (i, w) in words.iter().enumerate() {
print!(" [{:>2}] {:<10}", i + 1, w);
if (i + 1) % 4 == 0 {
println!();
}
}
if words.len() % 4 != 0 {
println!();
}
}