Skip to main content

blvm_sdk/governance/
multisig.rs

1//! # Multisig Operations
2//!
3//! Multisig threshold logic and signature collection.
4
5use std::collections::HashSet;
6
7use crate::governance::error::{GovernanceError, GovernanceResult};
8use crate::governance::{PublicKey, Signature};
9
10/// A multisig configuration
11#[derive(Debug, Clone)]
12pub struct Multisig {
13    threshold: usize,
14    total: usize,
15    public_keys: Vec<PublicKey>,
16}
17
18impl Multisig {
19    /// Create a new multisig configuration
20    pub fn new(
21        threshold: usize,
22        total: usize,
23        public_keys: Vec<PublicKey>,
24    ) -> GovernanceResult<Self> {
25        if threshold == 0 {
26            return Err(GovernanceError::InvalidThreshold { threshold, total });
27        }
28
29        if threshold > total {
30            return Err(GovernanceError::InvalidThreshold { threshold, total });
31        }
32
33        if public_keys.len() != total {
34            return Err(GovernanceError::InvalidMultisig(format!(
35                "Expected {} public keys, got {}",
36                total,
37                public_keys.len()
38            )));
39        }
40
41        // Check for duplicate public keys
42        let unique_keys: HashSet<_> = public_keys.iter().collect();
43        if unique_keys.len() != public_keys.len() {
44            return Err(GovernanceError::InvalidMultisig(
45                "Duplicate public keys not allowed".to_string(),
46            ));
47        }
48
49        Ok(Self {
50            threshold,
51            total,
52            public_keys,
53        })
54    }
55
56    /// Verify a set of signatures against a message
57    pub fn verify(&self, message: &[u8], signatures: &[Signature]) -> GovernanceResult<bool> {
58        if signatures.len() < self.threshold {
59            return Err(GovernanceError::InsufficientSignatures {
60                got: signatures.len(),
61                need: self.threshold,
62            });
63        }
64
65        let valid_signatures = self.collect_valid_signatures(message, signatures)?;
66        Ok(valid_signatures.len() >= self.threshold)
67    }
68
69    /// Collect valid signatures and return their indices
70    pub fn collect_valid_signatures(
71        &self,
72        message: &[u8],
73        signatures: &[Signature],
74    ) -> GovernanceResult<Vec<usize>> {
75        let mut valid_indices = Vec::new();
76
77        for signature in signatures.iter() {
78            // Try to verify against each public key
79            for (j, public_key) in self.public_keys.iter().enumerate() {
80                if crate::governance::verify_signature(signature, message, public_key)? {
81                    valid_indices.push(j);
82                    break;
83                }
84            }
85        }
86
87        Ok(valid_indices)
88    }
89
90    /// Get the threshold
91    pub fn threshold(&self) -> usize {
92        self.threshold
93    }
94
95    /// Get the total number of keys
96    pub fn total(&self) -> usize {
97        self.total
98    }
99
100    /// Get the public keys
101    pub fn public_keys(&self) -> &[PublicKey] {
102        &self.public_keys
103    }
104
105    /// Check if a signature is valid for this multisig
106    pub fn is_valid_signature(
107        &self,
108        signature: &Signature,
109        message: &[u8],
110    ) -> GovernanceResult<Option<usize>> {
111        for (i, public_key) in self.public_keys.iter().enumerate() {
112            if crate::governance::verify_signature(signature, message, public_key)? {
113                return Ok(Some(i));
114            }
115        }
116        Ok(None)
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use crate::governance::GovernanceKeypair;
124
125    #[test]
126    fn test_multisig_creation() {
127        let keypairs: Vec<_> = (0..5)
128            .map(|_| GovernanceKeypair::generate().unwrap())
129            .collect();
130        let public_keys: Vec<_> = keypairs.iter().map(|kp| kp.public_key()).collect();
131
132        let multisig = Multisig::new(3, 5, public_keys).unwrap();
133        assert_eq!(multisig.threshold(), 3);
134        assert_eq!(multisig.total(), 5);
135    }
136
137    #[test]
138    fn test_invalid_threshold() {
139        let keypairs: Vec<_> = (0..5)
140            .map(|_| GovernanceKeypair::generate().unwrap())
141            .collect();
142        let public_keys: Vec<_> = keypairs.iter().map(|kp| kp.public_key()).collect();
143
144        // Threshold too high
145        let result = Multisig::new(6, 5, public_keys.clone());
146        assert!(result.is_err());
147
148        // Threshold zero
149        let result = Multisig::new(0, 5, public_keys);
150        assert!(result.is_err());
151    }
152
153    #[test]
154    fn test_multisig_verification() {
155        let keypairs: Vec<_> = (0..5)
156            .map(|_| GovernanceKeypair::generate().unwrap())
157            .collect();
158        let public_keys: Vec<_> = keypairs.iter().map(|kp| kp.public_key()).collect();
159
160        let multisig = Multisig::new(3, 5, public_keys).unwrap();
161        let message = b"test message";
162
163        // Sign with 3 keys (meets threshold)
164        let signatures: Vec<_> = keypairs[0..3]
165            .iter()
166            .map(|kp| crate::sign_message(&kp.secret_key, message).unwrap())
167            .collect();
168
169        let result = multisig.verify(message, &signatures).unwrap();
170        assert!(result);
171    }
172
173    #[test]
174    fn test_insufficient_signatures() {
175        let keypairs: Vec<_> = (0..5)
176            .map(|_| GovernanceKeypair::generate().unwrap())
177            .collect();
178        let public_keys: Vec<_> = keypairs.iter().map(|kp| kp.public_key()).collect();
179
180        let multisig = Multisig::new(3, 5, public_keys).unwrap();
181        let message = b"test message";
182
183        // Sign with only 2 keys (below threshold)
184        let signatures: Vec<_> = keypairs[0..2]
185            .iter()
186            .map(|kp| crate::sign_message(&kp.secret_key, message).unwrap())
187            .collect();
188
189        let result = multisig.verify(message, &signatures);
190        assert!(result.is_err());
191    }
192
193    #[test]
194    fn test_duplicate_public_keys() {
195        let keypair = GovernanceKeypair::generate().unwrap();
196        let public_key = keypair.public_key();
197
198        // Create multisig with duplicate keys
199        let public_keys = vec![public_key.clone(), public_key];
200
201        let result = Multisig::new(2, 2, public_keys);
202        assert!(result.is_err());
203    }
204}