agentic_payments/ap2/
verification.rs

1//! Multi-Agent Consensus Verification for Credentials
2
3use super::{Ap2Error, Result};
4use crate::ap2::credentials::VerifiableCredential;
5use crate::ap2::did::DidResolver;
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use tokio::sync::RwLock;
10
11/// Verification Result with consensus details
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct VerificationResult {
14    pub verified: bool,
15    pub consensus_achieved: bool,
16    pub verifier_count: usize,
17    pub approval_count: usize,
18    pub rejection_count: usize,
19    pub threshold_percentage: f64,
20    pub verifier_results: Vec<VerifierResult>,
21    pub timestamp: DateTime<Utc>,
22    pub metadata: HashMap<String, String>,
23}
24
25/// Individual verifier result
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct VerifierResult {
28    pub verifier_id: String,
29    pub verified: bool,
30    pub timestamp: DateTime<Utc>,
31    pub reason: Option<String>,
32}
33
34/// Verifier Node in the consensus network
35#[derive(Debug, Clone)]
36pub struct VerifierNode {
37    pub id: String,
38    pub did: String,
39    pub endpoint: String,
40    pub weight: f64, // Voting weight (default 1.0)
41    pub reputation: f64,
42}
43
44impl VerifierNode {
45    pub fn new(id: String, did: String, endpoint: String) -> Self {
46        Self {
47            id,
48            did,
49            endpoint,
50            weight: 1.0,
51            reputation: 1.0,
52        }
53    }
54
55    pub fn with_weight(mut self, weight: f64) -> Self {
56        self.weight = weight;
57        self
58    }
59
60    pub fn with_reputation(mut self, reputation: f64) -> Self {
61        self.reputation = reputation;
62        self
63    }
64}
65
66/// Consensus Verification Engine
67#[derive(Debug)]
68pub struct ConsensusVerification {
69    threshold: f64,            // Percentage of approval needed (0.0 - 1.0)
70    min_verifiers: usize,      // Minimum number of verifiers required
71    timeout_seconds: u64,      // Verification timeout
72}
73
74impl ConsensusVerification {
75    pub fn new(threshold: f64, min_verifiers: usize) -> Self {
76        Self {
77            threshold: threshold.clamp(0.0, 1.0),
78            min_verifiers,
79            timeout_seconds: 30,
80        }
81    }
82
83    pub fn with_timeout(mut self, timeout_seconds: u64) -> Self {
84        self.timeout_seconds = timeout_seconds;
85        self
86    }
87
88    /// Verify credential with consensus from multiple verifiers
89    pub async fn verify(
90        &self,
91        credential: &VerifiableCredential,
92        verifiers: Vec<VerifierNode>,
93        did_resolver: &DidResolver,
94    ) -> Result<VerificationResult> {
95        if verifiers.len() < self.min_verifiers {
96            return Err(Ap2Error::ConsensusVerificationFailed(format!(
97                "Insufficient verifiers: {} < {}",
98                verifiers.len(),
99                self.min_verifiers
100            )));
101        }
102
103        // First verify the credential signature locally
104        let signature_valid = credential.verify(did_resolver)?;
105        if !signature_valid {
106            return Ok(VerificationResult {
107                verified: false,
108                consensus_achieved: false,
109                verifier_count: 0,
110                approval_count: 0,
111                rejection_count: 0,
112                threshold_percentage: self.threshold,
113                verifier_results: vec![],
114                timestamp: Utc::now(),
115                metadata: HashMap::from([(
116                    "error".to_string(),
117                    "Signature verification failed".to_string(),
118                )]),
119            });
120        }
121
122        // Perform parallel verification with all verifiers
123        let verification_futures: Vec<_> = verifiers
124            .iter()
125            .map(|verifier| self.verify_with_node(credential, verifier, did_resolver))
126            .collect();
127
128        let results = futures::future::join_all(verification_futures).await;
129
130        // Calculate consensus
131        let verifier_results: Vec<VerifierResult> = results.into_iter().flatten().collect();
132
133        let total_weight: f64 = verifiers.iter().map(|v| v.weight * v.reputation).sum();
134        let approval_weight: f64 = verifier_results
135            .iter()
136            .zip(verifiers.iter())
137            .filter(|(r, _)| r.verified)
138            .map(|(_, v)| v.weight * v.reputation)
139            .sum();
140
141        let approval_percentage = if total_weight > 0.0 {
142            approval_weight / total_weight
143        } else {
144            0.0
145        };
146
147        let approval_count = verifier_results.iter().filter(|r| r.verified).count();
148        let rejection_count = verifier_results.len() - approval_count;
149        let consensus_achieved = approval_percentage >= self.threshold;
150
151        Ok(VerificationResult {
152            verified: consensus_achieved,
153            consensus_achieved,
154            verifier_count: verifiers.len(),
155            approval_count,
156            rejection_count,
157            threshold_percentage: self.threshold,
158            verifier_results,
159            timestamp: Utc::now(),
160            metadata: HashMap::from([
161                ("approval_percentage".to_string(), approval_percentage.to_string()),
162                ("total_weight".to_string(), total_weight.to_string()),
163                ("approval_weight".to_string(), approval_weight.to_string()),
164            ]),
165        })
166    }
167
168    /// Verify with a single verifier node
169    async fn verify_with_node(
170        &self,
171        credential: &VerifiableCredential,
172        verifier: &VerifierNode,
173        did_resolver: &DidResolver,
174    ) -> Result<VerifierResult> {
175        // In production, this would make an HTTP request to the verifier endpoint
176        // For now, we simulate verification by checking the credential locally
177        let verified = credential.verify(did_resolver).unwrap_or(false);
178
179        Ok(VerifierResult {
180            verifier_id: verifier.id.clone(),
181            verified,
182            timestamp: Utc::now(),
183            reason: if verified {
184                None
185            } else {
186                Some("Verification failed".to_string())
187            },
188        })
189    }
190}
191
192impl Default for ConsensusVerification {
193    fn default() -> Self {
194        Self::new(0.66, 3) // 2/3 majority with minimum 3 verifiers
195    }
196}
197
198/// Verification Workflow Manager
199#[derive(Debug)]
200pub struct VerificationWorkflow {
201    consensus: ConsensusVerification,
202    verifier_registry: RwLock<HashMap<String, VerifierNode>>,
203}
204
205impl VerificationWorkflow {
206    pub fn new() -> Self {
207        Self {
208            consensus: ConsensusVerification::default(),
209            verifier_registry: RwLock::new(HashMap::new()),
210        }
211    }
212
213    pub fn with_consensus(mut self, consensus: ConsensusVerification) -> Self {
214        self.consensus = consensus;
215        self
216    }
217
218    /// Register a verifier node
219    pub async fn register_verifier(&self, verifier: VerifierNode) {
220        let mut registry = self.verifier_registry.write().await;
221        registry.insert(verifier.id.clone(), verifier);
222    }
223
224    /// Unregister a verifier node
225    pub async fn unregister_verifier(&self, verifier_id: &str) -> bool {
226        let mut registry = self.verifier_registry.write().await;
227        registry.remove(verifier_id).is_some()
228    }
229
230    /// Get all registered verifiers
231    pub async fn get_verifiers(&self) -> Vec<VerifierNode> {
232        let registry = self.verifier_registry.read().await;
233        registry.values().cloned().collect()
234    }
235
236    /// Get verifier by ID
237    pub async fn get_verifier(&self, verifier_id: &str) -> Option<VerifierNode> {
238        let registry = self.verifier_registry.read().await;
239        registry.get(verifier_id).cloned()
240    }
241
242    /// Verify credential with consensus from registered verifiers
243    pub async fn verify_with_consensus(
244        &self,
245        credential: &VerifiableCredential,
246        verifiers: Vec<VerifierNode>,
247        did_resolver: &DidResolver,
248    ) -> Result<VerificationResult> {
249        self.consensus.verify(credential, verifiers, did_resolver).await
250    }
251
252    /// Verify credential with all registered verifiers
253    pub async fn verify_with_all_verifiers(
254        &self,
255        credential: &VerifiableCredential,
256        did_resolver: &DidResolver,
257    ) -> Result<VerificationResult> {
258        let verifiers = self.get_verifiers().await;
259        self.verify_with_consensus(credential, verifiers, did_resolver).await
260    }
261
262    /// Update verifier reputation based on verification result
263    pub async fn update_verifier_reputation(&self, verifier_id: &str, reputation_delta: f64) {
264        let mut registry = self.verifier_registry.write().await;
265        if let Some(verifier) = registry.get_mut(verifier_id) {
266            verifier.reputation = (verifier.reputation + reputation_delta).clamp(0.0, 2.0);
267        }
268    }
269}
270
271impl Default for VerificationWorkflow {
272    fn default() -> Self {
273        Self::new()
274    }
275}
276
277/// Verification Policy - Defines rules for verification
278#[derive(Debug, Clone, Serialize, Deserialize)]
279pub struct VerificationPolicy {
280    pub min_verifiers: usize,
281    pub consensus_threshold: f64,
282    pub timeout_seconds: u64,
283    pub require_signature: bool,
284    pub require_expiration_check: bool,
285    pub allowed_credential_types: Vec<String>,
286}
287
288impl VerificationPolicy {
289    pub fn strict() -> Self {
290        Self {
291            min_verifiers: 5,
292            consensus_threshold: 0.8,
293            timeout_seconds: 30,
294            require_signature: true,
295            require_expiration_check: true,
296            allowed_credential_types: vec!["VerifiableCredential".to_string()],
297        }
298    }
299
300    pub fn standard() -> Self {
301        Self {
302            min_verifiers: 3,
303            consensus_threshold: 0.66,
304            timeout_seconds: 30,
305            require_signature: true,
306            require_expiration_check: true,
307            allowed_credential_types: vec![],
308        }
309    }
310
311    pub fn permissive() -> Self {
312        Self {
313            min_verifiers: 1,
314            consensus_threshold: 0.5,
315            timeout_seconds: 60,
316            require_signature: true,
317            require_expiration_check: false,
318            allowed_credential_types: vec![],
319        }
320    }
321
322    pub fn validate(&self, credential: &VerifiableCredential) -> Result<()> {
323        if self.require_expiration_check && credential.is_expired() {
324            return Err(Ap2Error::Expired);
325        }
326
327        if !self.allowed_credential_types.is_empty() {
328            let has_allowed_type = credential
329                .types
330                .iter()
331                .any(|t| self.allowed_credential_types.contains(t));
332
333            if !has_allowed_type {
334                return Err(Ap2Error::InvalidCredential(
335                    "Credential type not allowed by policy".to_string(),
336                ));
337            }
338        }
339
340        Ok(())
341    }
342}
343
344impl Default for VerificationPolicy {
345    fn default() -> Self {
346        Self::standard()
347    }
348}
349
350#[cfg(test)]
351mod tests {
352    use super::*;
353
354    #[test]
355    fn test_verifier_node_creation() {
356        let node = VerifierNode::new(
357            "verifier1".to_string(),
358            "did:ap2:verifier1".to_string(),
359            "https://verifier1.example.com".to_string(),
360        );
361
362        assert_eq!(node.weight, 1.0);
363        assert_eq!(node.reputation, 1.0);
364    }
365
366    #[test]
367    fn test_consensus_verification_initialization() {
368        let consensus = ConsensusVerification::new(0.66, 3);
369        assert_eq!(consensus.threshold, 0.66);
370        assert_eq!(consensus.min_verifiers, 3);
371    }
372
373    #[test]
374    fn test_verification_policy_validation() {
375        let policy = VerificationPolicy::strict();
376        assert_eq!(policy.min_verifiers, 5);
377        assert_eq!(policy.consensus_threshold, 0.8);
378    }
379
380    #[tokio::test]
381    async fn test_workflow_verifier_registration() {
382        let workflow = VerificationWorkflow::new();
383        let verifier = VerifierNode::new(
384            "test1".to_string(),
385            "did:ap2:test1".to_string(),
386            "https://test1.example.com".to_string(),
387        );
388
389        workflow.register_verifier(verifier.clone()).await;
390
391        let verifiers = workflow.get_verifiers().await;
392        assert_eq!(verifiers.len(), 1);
393        assert_eq!(verifiers[0].id, "test1");
394    }
395}