miden_standards/account/auth/
approver.rs1use alloc::collections::BTreeSet;
2use alloc::format;
3use alloc::vec::Vec;
4use core::num::NonZeroU32;
5
6use miden_protocol::account::auth::{AuthScheme, PublicKey, PublicKeyCommitment};
7use miden_protocol::errors::AccountError;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct Approver {
22 pub_key: PublicKeyCommitment,
23 auth_scheme: AuthScheme,
24}
25
26impl Approver {
27 pub fn new(pub_key: PublicKeyCommitment, auth_scheme: AuthScheme) -> Self {
44 Self { pub_key, auth_scheme }
45 }
46
47 pub fn pub_key(&self) -> PublicKeyCommitment {
49 self.pub_key
50 }
51
52 pub fn auth_scheme(&self) -> AuthScheme {
54 self.auth_scheme
55 }
56}
57
58impl From<&PublicKey> for Approver {
59 fn from(pub_key: &PublicKey) -> Self {
60 Self::new(pub_key.to_commitment(), pub_key.auth_scheme())
61 }
62}
63
64#[derive(Debug, Clone, PartialEq, Eq)]
74pub struct ApproverSet {
75 approvers: Vec<Approver>,
76 threshold: NonZeroU32,
77}
78
79impl ApproverSet {
80 pub const MAX_APPROVERS: u8 = 64;
82
83 pub fn new(approvers: Vec<Approver>, threshold: u32) -> Result<Self, AccountError> {
93 let threshold = NonZeroU32::new(threshold)
94 .ok_or_else(|| AccountError::other("threshold must be at least 1"))?;
95
96 if approvers.len() as u64 > u64::from(Self::MAX_APPROVERS) {
97 return Err(AccountError::other(format!(
98 "number of approvers cannot be greater than {}",
99 Self::MAX_APPROVERS
100 )));
101 }
102
103 if threshold.get() > approvers.len() as u32 {
104 return Err(AccountError::other(
105 "threshold cannot be greater than number of approvers",
106 ));
107 }
108
109 let unique_approvers: BTreeSet<_> = approvers.iter().map(Approver::pub_key).collect();
110 if unique_approvers.len() != approvers.len() {
111 return Err(AccountError::other("duplicate approver public keys are not allowed"));
112 }
113
114 Ok(Self { approvers, threshold })
115 }
116
117 pub fn approvers(&self) -> &[Approver] {
119 &self.approvers
120 }
121
122 pub fn threshold(&self) -> NonZeroU32 {
124 self.threshold
125 }
126}
127
128#[cfg(test)]
132mod tests {
133 use alloc::string::ToString;
134
135 use miden_protocol::Word;
136 use miden_protocol::account::auth::AuthScheme;
137
138 use super::*;
139
140 fn approver(seed: u32) -> Approver {
141 Approver::new(PublicKeyCommitment::from(Word::from([seed; 4])), AuthScheme::EcdsaK256Keccak)
142 }
143
144 #[test]
145 fn rejects_zero_threshold() {
146 let err = ApproverSet::new(vec![approver(1)], 0).unwrap_err();
147 assert!(err.to_string().contains("threshold must be at least 1"));
148 }
149
150 #[test]
151 fn rejects_threshold_above_approver_count() {
152 let err = ApproverSet::new(vec![approver(1)], 2).unwrap_err();
153 assert!(err.to_string().contains("threshold cannot be greater than number of approvers"));
154 }
155
156 #[test]
157 fn rejects_approver_count_above_max() {
158 let approvers: Vec<_> = (0..=u32::from(ApproverSet::MAX_APPROVERS)).map(approver).collect();
159 let err = ApproverSet::new(approvers, 1).unwrap_err();
160 assert!(err.to_string().contains("number of approvers cannot be greater than 64"));
161 }
162
163 #[test]
164 fn accepts_approver_count_at_max() {
165 let max_approvers = u32::from(ApproverSet::MAX_APPROVERS);
166 let approvers: Vec<_> = (0..max_approvers).map(approver).collect();
167 let set = ApproverSet::new(approvers, max_approvers).unwrap();
168 assert_eq!(set.approvers().len(), max_approvers as usize);
169 }
170
171 #[test]
172 fn rejects_duplicate_approvers() {
173 let err = ApproverSet::new(vec![approver(1), approver(1)], 2).unwrap_err();
174 assert!(err.to_string().contains("duplicate approver public keys are not allowed"));
175 }
176
177 #[test]
178 fn accepts_valid_set() {
179 let set = ApproverSet::new(vec![approver(1), approver(2)], 2).unwrap();
180 assert_eq!(set.approvers().len(), 2);
181 assert_eq!(set.threshold().get(), 2);
182 }
183}