use crate::node::{Error, Result};
use ed25519_dalek::{Keypair, PublicKey, KEYPAIR_LENGTH};
use hex::{decode, encode};
use std::path::Path;
use tokio::fs;
const REWARD_PUBLIC_KEY_FILENAME: &str = "reward_public_key";
const REWARD_SECRET_KEY_FILENAME: &str = "reward_secret_key";
const NETWORK_KEYPAIR_FILENAME: &str = "network_keypair";
pub(crate) async fn store_network_keypair(
root_dir: &Path,
keypair_as_bytes: [u8; KEYPAIR_LENGTH],
) -> Result<()> {
let keypair_path = root_dir.join(NETWORK_KEYPAIR_FILENAME);
fs::write(keypair_path, encode(keypair_as_bytes)).await?;
Ok(())
}
#[allow(dead_code)]
pub(crate) async fn get_network_keypair(root_dir: &Path) -> Result<Option<Keypair>> {
let path = root_dir.join(NETWORK_KEYPAIR_FILENAME);
if !path.is_file() {
return Ok(None);
}
let keypair_hex_bytes = fs::read(&path).await?;
let keypair_bytes = decode(keypair_hex_bytes).map_err(|err| {
Error::Configuration(format!(
"couldn't hex-decode network keypair bytes read from {}: {}",
path.display(),
err
))
})?;
let keypair = Keypair::from_bytes(&keypair_bytes).map_err(|err| {
Error::Configuration(format!(
"invalid network keypair bytes read from {}: {}",
path.display(),
err
))
})?;
Ok(Some(keypair))
}
pub(crate) async fn store_new_reward_keypair(root_dir: &Path, keypair: &Keypair) -> Result<()> {
let secret_key_path = root_dir.join(REWARD_SECRET_KEY_FILENAME);
let public_key_path = root_dir.join(REWARD_PUBLIC_KEY_FILENAME);
fs::write(secret_key_path, encode(keypair.secret.to_bytes())).await?;
fs::write(public_key_path, encode(keypair.public.to_bytes())).await?;
Ok(())
}
pub(crate) async fn get_reward_pk(root_dir: &Path) -> Result<Option<PublicKey>> {
let path = root_dir.join(REWARD_PUBLIC_KEY_FILENAME);
if !path.is_file() {
return Ok(None);
}
let pk_hex_bytes = fs::read(&path).await?;
let pk_bytes = decode(pk_hex_bytes).map_err(|err| {
Error::Configuration(format!(
"couldn't hex-decode rewards Ed25519 public key bytes from {}: {}",
path.display(),
err
))
})?;
let pk = PublicKey::from_bytes(&pk_bytes).map_err(|err| {
Error::Configuration(format!(
"invalid rewards Ed25519 public key bytes read from {}: {}",
path.display(),
err
))
})?;
Ok(Some(pk))
}
#[cfg(test)]
mod test {
use super::{
get_network_keypair, get_reward_pk, store_network_keypair, store_new_reward_keypair,
};
use eyre::{eyre, Result};
use rand::rngs::OsRng;
use tempfile::{tempdir, TempDir};
#[tokio::test(flavor = "multi_thread")]
async fn pubkey_to_and_from_file() -> Result<()> {
let mut rng = OsRng;
let keypair = ed25519_dalek::Keypair::generate(&mut rng);
let root = create_temp_root()?;
let root_dir = root.path();
store_new_reward_keypair(root_dir, &keypair).await?;
let pk_result = get_reward_pk(root_dir).await?;
assert_eq!(pk_result, Some(keypair.public));
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn keypair_to_and_from_file() -> Result<()> {
let mut rng = OsRng;
let keypair = ed25519_dalek::Keypair::generate(&mut rng);
let root = create_temp_root()?;
let root_dir = root.path();
let keypair_result = get_network_keypair(root_dir).await?;
assert!(keypair_result.is_none());
store_network_keypair(root_dir, keypair.to_bytes()).await?;
let keypair_result = get_network_keypair(root_dir).await?;
if let Some(kp) = keypair_result {
assert_eq!(kp.public, keypair.public);
Ok(())
} else {
Err(eyre!("Network keypair was not read from file"))
}
}
fn create_temp_root() -> Result<TempDir> {
tempdir().map_err(|e| eyre!("Failed to create temp dir: {}", e))
}
}