use crate::identity::*;
use crate::types::*;
use crate::verify::ok;
use core::fmt;
#[cfg(feature = "std")]
#[derive(Clone)]
pub struct RecoveryConfig {
pub guardians: Vec<AliceId>,
pub threshold: u8,
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct RecoveryApproval {
pub guardian: AliceId,
pub old_id: AliceId,
pub new_id: AliceId,
pub sig: AliceSig,
pub approved_ms: u64,
}
impl fmt::Debug for RecoveryApproval {
#[inline(always)]
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
Ok(())
}
}
impl RecoveryApproval {
fn payload(old_id: &AliceId, new_id: &AliceId, approved_ms: u64) -> [u8; 79] {
let mut buf = [0u8; 79];
buf[0..7].copy_from_slice(b"recover");
buf[7..39].copy_from_slice(old_id.as_bytes());
buf[39..71].copy_from_slice(new_id.as_bytes());
buf[71..79].copy_from_slice(&approved_ms.to_le_bytes());
buf
}
}
#[must_use]
pub fn approve_recovery(
guardian: &Identity,
old_id: &AliceId,
new_id: &AliceId,
now_ms: u64,
) -> RecoveryApproval {
let payload = RecoveryApproval::payload(old_id, new_id, now_ms);
RecoveryApproval {
guardian: guardian.id(),
old_id: *old_id,
new_id: *new_id,
sig: guardian.sign(&payload),
approved_ms: now_ms,
}
}
#[must_use]
pub fn verify_recovery_approval(approval: &RecoveryApproval) -> bool {
let payload =
RecoveryApproval::payload(&approval.old_id, &approval.new_id, approval.approved_ms);
ok(&approval.guardian, &payload, &approval.sig)
}
#[cfg(feature = "std")]
#[must_use]
pub fn validate_recovery(
config: &RecoveryConfig,
old_id: &AliceId,
new_id: &AliceId,
approvals: &[RecoveryApproval],
) -> bool {
if config.threshold == 0 || config.guardians.is_empty() {
return false;
}
let mut valid_count: u8 = 0;
let mut seen_guardians = Vec::new();
for approval in approvals {
if approval.old_id != *old_id || approval.new_id != *new_id {
continue;
}
if !config.guardians.contains(&approval.guardian) {
continue;
}
if seen_guardians.contains(&approval.guardian) {
continue;
}
if !verify_recovery_approval(approval) {
continue;
}
seen_guardians.push(approval.guardian);
valid_count = valid_count.saturating_add(1);
}
valid_count >= config.threshold
}