Skip to main content

wanning_core/
revocation.rs

1//! 撤销集合(RevocationSet):kill switch。
2//!
3//! 语义对齐 mist-core 的 `RevocationRegistry`/`RevocationSet`(撤销即时生效,
4//! 已撤销委托的新意图一律拒、不耗 nonce/窗口槽)。
5//!
6//! **单向开关:本类型刻意不提供「解除撤销」。** 撤销 = 用户收权,必须新建一份委托
7//! 重新授权。理由:闸的信任模型里,「撤销后永不允许」是四卖点之一(kill switch),
8//! 任何 un-revoke 通路都会让「用户以为已收权」和「agent 还能花钱」之间出现灰色窗口。
9
10use std::collections::BTreeSet;
11
12/// 已撤销的委托 id 集合。
13#[derive(Clone, Debug, Default, PartialEq, Eq)]
14pub struct RevocationSet {
15    revoked: BTreeSet<String>,
16}
17
18impl RevocationSet {
19    pub fn new() -> Self {
20        Self::default()
21    }
22
23    /// 撤销一个委托。重复撤销幂等(返回是否为本次新撤销)。
24    pub fn revoke(&mut self, delegation_id: &str) -> bool {
25        self.revoked.insert(delegation_id.to_string())
26    }
27
28    /// 是否已撤销。
29    pub fn is_revoked(&self, delegation_id: &str) -> bool {
30        self.revoked.contains(delegation_id)
31    }
32
33    /// 已撤销数量(审计用)。
34    pub fn len(&self) -> usize {
35        self.revoked.len()
36    }
37
38    pub fn is_empty(&self) -> bool {
39        self.revoked.is_empty()
40    }
41
42    /// 迭代已撤销的委托 id(有序,供状态哈希/审计导出)。
43    pub fn iter(&self) -> impl Iterator<Item = &String> {
44        self.revoked.iter()
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn revoke_then_is_revoked() {
54        let mut r = RevocationSet::new();
55        assert!(r.is_empty());
56        assert!(!r.is_revoked("d1"));
57        assert!(r.revoke("d1"), "首次撤销返回 true");
58        assert!(r.is_revoked("d1"));
59        assert_eq!(r.len(), 1);
60    }
61
62    #[test]
63    fn revoke_is_idempotent() {
64        let mut r = RevocationSet::new();
65        assert!(r.revoke("d1"));
66        assert!(!r.revoke("d1"), "重复撤销是幂等的,不重复计数");
67        assert_eq!(r.len(), 1);
68    }
69
70    #[test]
71    fn revocation_is_one_way_no_unrevoke_api() {
72        // 这是语义测试:撤销后没有通路回到未撤销态。集合只能变大。
73        let mut r = RevocationSet::new();
74        r.revoke("d1");
75        r.revoke("d2");
76        let before = r.iter().cloned().collect::<Vec<_>>();
77        assert_eq!(before, vec!["d1".to_string(), "d2".to_string()]);
78        // 只读迭代拿到的集合不可能再被缩回(无任何可变借用出口)。
79        assert_eq!(r.len(), 2);
80    }
81}