use crate::{Error, Result, Identity, Fingerprint};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ValidatorStatus {
None,
Waiting,
Active,
Jailed,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ValidatorStats {
pub blocks_authored: u64,
pub forum_posts: u64,
pub uptime: f64,
pub total_rewards: u64,
pub era_points: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidatorIdentity {
pub identity_key: Vec<u8>,
pub fingerprint: String,
pub display_name: Option<String>,
pub website: Option<String>,
pub riot: Option<String>,
}
#[derive(Debug, Clone)]
pub struct ValidatorConfig {
pub author_blocks: bool,
pub stake: u128,
pub commission: u8,
pub session_keys_path: Option<String>,
}
impl Default for ValidatorConfig {
fn default() -> Self {
Self {
author_blocks: false,
stake: 0,
commission: 10,
session_keys_path: None,
}
}
}
pub struct ValidatorMode {
config: ValidatorConfig,
identity: Option<ValidatorIdentity>,
status: ValidatorStatus,
stats: ValidatorStats,
}
impl ValidatorMode {
pub fn new(config: ValidatorConfig) -> Self {
Self {
config,
identity: None,
status: ValidatorStatus::None,
stats: ValidatorStats::default(),
}
}
pub fn from_identity(identity: &Identity, config: ValidatorConfig) -> Self {
let validator_identity = ValidatorIdentity {
identity_key: identity.public_key().as_bytes().to_vec(),
fingerprint: identity.fingerprint().to_hex(),
display_name: None,
website: None,
riot: None,
};
Self {
config,
identity: Some(validator_identity),
status: ValidatorStatus::None,
stats: ValidatorStats::default(),
}
}
pub fn status(&self) -> ValidatorStatus {
self.status
}
pub fn stats(&self) -> &ValidatorStats {
&self.stats
}
pub fn identity(&self) -> Option<&ValidatorIdentity> {
self.identity.as_ref()
}
pub fn set_display_name(&mut self, name: impl Into<String>) {
if let Some(ref mut identity) = self.identity {
identity.display_name = Some(name.into());
}
}
pub async fn register(&mut self) -> Result<()> {
if self.identity.is_none() {
return Err(Error::Config("No identity set".into()));
}
if self.config.stake == 0 {
return Err(Error::Config("No stake configured".into()));
}
tracing::info!(
"Registering validator with stake {} and commission {}%",
self.config.stake,
self.config.commission
);
self.status = ValidatorStatus::Waiting;
Ok(())
}
pub async fn unregister(&mut self) -> Result<()> {
if self.status == ValidatorStatus::None {
return Err(Error::Config("Not a validator".into()));
}
tracing::info!("Unregistering validator");
self.status = ValidatorStatus::None;
Ok(())
}
pub async fn refresh_status(&mut self) -> Result<()> {
Ok(())
}
pub async fn refresh_stats(&mut self) -> Result<()> {
Ok(())
}
pub async fn rotate_session_keys(&self) -> Result<Vec<u8>> {
let dummy_keys = vec![0u8; 128];
Ok(dummy_keys)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validator_mode() {
let config = ValidatorConfig::default();
let mode = ValidatorMode::new(config);
assert_eq!(mode.status(), ValidatorStatus::None);
}
#[test]
fn test_from_identity() {
let identity = Identity::generate().unwrap();
let config = ValidatorConfig::default();
let mode = ValidatorMode::from_identity(&identity, config);
assert!(mode.identity().is_some());
assert_eq!(
mode.identity().unwrap().fingerprint,
identity.fingerprint().to_hex()
);
}
}