use elara_core::NodeId;
use std::collections::HashSet;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AuthorityLevel {
Exclusive,
Shared,
Open,
Frozen,
}
#[derive(Debug, Clone)]
pub struct AuthorityProof {
pub claimer: NodeId,
pub state_id: u64,
pub signature: Vec<u8>,
pub timestamp: i64,
}
impl AuthorityProof {
pub fn new(claimer: NodeId, state_id: u64, timestamp: i64) -> Self {
Self {
claimer,
state_id,
signature: Vec::new(), timestamp,
}
}
pub fn is_for_state(&self, state_id: u64) -> bool {
self.state_id == state_id
}
}
#[derive(Debug, Clone)]
pub struct AuthoritySet {
pub state_id: u64,
pub level: AuthorityLevel,
pub authorities: HashSet<NodeId>,
pub delegations: Vec<(NodeId, NodeId)>,
}
impl AuthoritySet {
pub fn exclusive(state_id: u64, owner: NodeId) -> Self {
let mut authorities = HashSet::new();
authorities.insert(owner);
Self {
state_id,
level: AuthorityLevel::Exclusive,
authorities,
delegations: Vec::new(),
}
}
pub fn shared(state_id: u64, owners: Vec<NodeId>) -> Self {
Self {
state_id,
level: AuthorityLevel::Shared,
authorities: owners.into_iter().collect(),
delegations: Vec::new(),
}
}
pub fn open(state_id: u64) -> Self {
Self {
state_id,
level: AuthorityLevel::Open,
authorities: HashSet::new(),
delegations: Vec::new(),
}
}
pub fn frozen(state_id: u64) -> Self {
Self {
state_id,
level: AuthorityLevel::Frozen,
authorities: HashSet::new(),
delegations: Vec::new(),
}
}
pub fn has_authority(&self, node: NodeId) -> bool {
match self.level {
AuthorityLevel::Exclusive | AuthorityLevel::Shared => self.authorities.contains(&node),
AuthorityLevel::Open => true,
AuthorityLevel::Frozen => false,
}
}
pub fn grant(&mut self, granter: NodeId, grantee: NodeId) -> bool {
if self.level != AuthorityLevel::Shared {
return false;
}
if !self.authorities.contains(&granter) {
return false;
}
self.authorities.insert(grantee);
self.delegations.push((granter, grantee));
true
}
pub fn revoke(&mut self, revoker: NodeId, revokee: NodeId) -> bool {
if self.level != AuthorityLevel::Shared {
return false;
}
if !self.authorities.contains(&revoker) {
return false;
}
if self.authorities.len() == 1 && self.authorities.contains(&revokee) {
return false;
}
self.authorities.remove(&revokee);
true
}
}
#[derive(Debug, Clone)]
pub struct LivestreamAuthority {
pub broadcaster: NodeId,
pub visual_authority: AuthoritySet,
pub audio_authority: AuthoritySet,
pub chat_authority: AuthoritySet,
pub moderators: HashSet<NodeId>,
}
impl LivestreamAuthority {
pub fn new(broadcaster: NodeId, stream_id: u64) -> Self {
Self {
broadcaster,
visual_authority: AuthoritySet::exclusive(stream_id, broadcaster),
audio_authority: AuthoritySet::exclusive(stream_id + 1, broadcaster),
chat_authority: AuthoritySet::open(stream_id + 2),
moderators: HashSet::new(),
}
}
pub fn add_moderator(&mut self, moderator: NodeId) {
self.moderators.insert(moderator);
}
pub fn can_mutate_visual(&self, node: NodeId) -> bool {
self.visual_authority.has_authority(node)
}
pub fn can_mutate_audio(&self, node: NodeId) -> bool {
self.audio_authority.has_authority(node)
}
pub fn can_chat(&self, node: NodeId) -> bool {
self.chat_authority.has_authority(node)
}
pub fn is_moderator(&self, node: NodeId) -> bool {
self.moderators.contains(&node) || node == self.broadcaster
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_exclusive_authority() {
let owner = NodeId::new(1);
let other = NodeId::new(2);
let auth = AuthoritySet::exclusive(100, owner);
assert!(auth.has_authority(owner));
assert!(!auth.has_authority(other));
}
#[test]
fn test_shared_authority() {
let node1 = NodeId::new(1);
let node2 = NodeId::new(2);
let node3 = NodeId::new(3);
let mut auth = AuthoritySet::shared(100, vec![node1, node2]);
assert!(auth.has_authority(node1));
assert!(auth.has_authority(node2));
assert!(!auth.has_authority(node3));
assert!(auth.grant(node1, node3));
assert!(auth.has_authority(node3));
}
#[test]
fn test_open_authority() {
let auth = AuthoritySet::open(100);
assert!(auth.has_authority(NodeId::new(1)));
assert!(auth.has_authority(NodeId::new(999)));
}
#[test]
fn test_frozen_authority() {
let auth = AuthoritySet::frozen(100);
assert!(!auth.has_authority(NodeId::new(1)));
assert!(!auth.has_authority(NodeId::new(999)));
}
#[test]
fn test_livestream_authority() {
let broadcaster = NodeId::new(1);
let viewer = NodeId::new(2);
let auth = LivestreamAuthority::new(broadcaster, 1000);
assert!(auth.can_mutate_visual(broadcaster));
assert!(!auth.can_mutate_visual(viewer));
assert!(auth.can_mutate_audio(broadcaster));
assert!(!auth.can_mutate_audio(viewer));
assert!(auth.can_chat(broadcaster));
assert!(auth.can_chat(viewer));
}
}