1use serde::{Deserialize, Serialize};
11use sha2::{Digest, Sha256};
12use std::collections::HashMap;
13use std::time::{SystemTime, UNIX_EPOCH};
14
15pub const MAX_SCORE: u32 = 1000;
21
22pub const SWARM_COORDINATOR_THRESHOLD: u32 = 500;
24
25pub const COLLATERAL_THRESHOLD: u32 = 300;
27
28pub const DECAY_HALF_LIFE_DAYS: u64 = 90;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
37#[repr(u8)]
38pub enum CaptureLayer {
39 Shopping = 1,
41 Referral = 2,
42 Attention = 3,
43 Data = 4,
44 Insurance = 5,
45
46 Compute = 6,
48 Network = 7,
49 Energy = 8,
50 DePINAggregator = 9,
51 InferenceArbitrage = 10,
52 StorageDePIN = 11,
53
54 Skill = 12,
56 CurationSignal = 13,
57 Social = 14,
58 KnowledgeAPI = 15,
59 PersonalModelLicensing = 16,
60
61 Liquidity = 17,
63 GovernanceProxy = 18,
64 InventoryArbitrage = 19,
65 SubAgentManager = 20,
66 ReputationCollateral = 21,
67 SwarmCoordinationFee = 22,
68}
69
70impl CaptureLayer {
71 pub fn group(&self) -> LayerGroup {
73 match *self as u8 {
74 1..=5 => LayerGroup::PassiveUtility,
75 6..=11 => LayerGroup::Infrastructure,
76 12..=16 => LayerGroup::Intelligence,
77 17..=22 => LayerGroup::AggressiveAutopilot,
78 _ => LayerGroup::PassiveUtility,
79 }
80 }
81
82 pub fn reputation_weight(&self) -> f64 {
84 match self {
85 CaptureLayer::Shopping => 1.0,
87 CaptureLayer::Referral => 0.8,
88 CaptureLayer::Attention => 0.5,
89 CaptureLayer::Data => 1.2,
90 CaptureLayer::Insurance => 0.7,
91
92 CaptureLayer::Compute => 1.5,
94 CaptureLayer::Network => 2.0,
95 CaptureLayer::Energy => 1.3,
96 CaptureLayer::DePINAggregator => 1.4,
97 CaptureLayer::InferenceArbitrage => 1.8,
98 CaptureLayer::StorageDePIN => 1.2,
99
100 CaptureLayer::Skill => 2.5,
102 CaptureLayer::CurationSignal => 2.0,
103 CaptureLayer::Social => 1.5,
104 CaptureLayer::KnowledgeAPI => 2.2,
105 CaptureLayer::PersonalModelLicensing => 3.0,
106
107 CaptureLayer::Liquidity => 1.8,
109 CaptureLayer::GovernanceProxy => 1.5,
110 CaptureLayer::InventoryArbitrage => 1.2,
111 CaptureLayer::SubAgentManager => 2.5,
112 CaptureLayer::ReputationCollateral => 2.0,
113 CaptureLayer::SwarmCoordinationFee => 3.0,
114 }
115 }
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
119pub enum LayerGroup {
120 PassiveUtility,
121 Infrastructure,
122 Intelligence,
123 AggressiveAutopilot,
124}
125
126pub trait Attestation: Send + Sync {
132 fn layer(&self) -> CaptureLayer;
134
135 fn timestamp(&self) -> u64;
137
138 fn is_positive(&self) -> bool;
140
141 fn magnitude(&self) -> u64;
143
144 fn metadata(&self) -> Option<&AttestationMetadata>;
146}
147
148#[derive(Debug, Clone, Default, Serialize, Deserialize)]
150pub struct AttestationMetadata {
151 pub lock_duration_days: Option<u16>,
153 pub held_to_maturity: Option<bool>,
155 pub accuracy_percent: Option<u8>,
157 pub conversion_rate: Option<u8>,
159 pub referral_successful: Option<bool>,
161 pub network_reach: Option<u32>,
163 pub uptime_percent: Option<u8>,
165 pub yield_bps: Option<u16>,
167 pub completion_rate: Option<u8>,
169 pub difficulty_tier: Option<u8>,
171 pub verification_multiplier: Option<f32>,
173}
174
175#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
181pub enum CredentialCategory {
182 ProfessionalLicense,
184 TechnicalCertification,
186 AcademicDegree,
188 ProfessionalExperience,
190 SkillDemonstration,
192 ApiContribution,
194}
195
196impl CredentialCategory {
197 pub fn base_weight(&self) -> u32 {
199 match self {
200 CredentialCategory::ProfessionalLicense => 100,
201 CredentialCategory::TechnicalCertification => 75,
202 CredentialCategory::AcademicDegree => 80,
203 CredentialCategory::ProfessionalExperience => 10, CredentialCategory::SkillDemonstration => 50,
205 CredentialCategory::ApiContribution => 5, }
207 }
208
209 pub fn is_per_unit(&self) -> bool {
211 matches!(
212 self,
213 CredentialCategory::ProfessionalExperience | CredentialCategory::ApiContribution
214 )
215 }
216}
217
218#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
220#[repr(u8)]
221pub enum DifficultyTier {
222 EntryLevel = 1,
223 Intermediate = 2,
224 Professional = 3,
225 Advanced = 4,
226 Expert = 5,
227}
228
229impl DifficultyTier {
230 pub fn multiplier(&self) -> f64 {
232 match self {
233 DifficultyTier::EntryLevel => 0.5,
234 DifficultyTier::Intermediate => 0.75,
235 DifficultyTier::Professional => 1.0,
236 DifficultyTier::Advanced => 1.5,
237 DifficultyTier::Expert => 2.0,
238 }
239 }
240
241 pub fn from_u8(val: u8) -> Self {
242 match val {
243 1 => DifficultyTier::EntryLevel,
244 2 => DifficultyTier::Intermediate,
245 4 => DifficultyTier::Advanced,
246 5 => DifficultyTier::Expert,
247 _ => DifficultyTier::Professional,
248 }
249 }
250}
251
252#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
254pub enum VerificationLevel {
255 SelfAttested,
257 DocumentSubmitted,
259 ThirdPartyVerified,
261 OnChainVerified,
263}
264
265impl VerificationLevel {
266 pub fn multiplier(&self) -> f64 {
268 match self {
269 VerificationLevel::SelfAttested => 0.5,
270 VerificationLevel::DocumentSubmitted => 0.7,
271 VerificationLevel::ThirdPartyVerified => 1.0,
272 VerificationLevel::OnChainVerified => 1.25,
273 }
274 }
275
276 pub fn auto_verify(&self) -> bool {
278 matches!(
279 self,
280 VerificationLevel::ThirdPartyVerified | VerificationLevel::OnChainVerified
281 )
282 }
283}
284
285#[derive(Debug, Clone, Serialize, Deserialize)]
295pub struct ProfessionalAttestation {
296 pub category: CredentialCategory,
298 pub credential_name: String,
300 pub issuing_authority: Option<String>,
302 pub credential_id: Option<String>,
304 pub difficulty: DifficultyTier,
306 pub verification: VerificationLevel,
308 pub quantity: Option<u32>,
310 pub timestamp: u64,
312 pub api_weight: Option<u32>,
314}
315
316impl ProfessionalAttestation {
317 pub fn calculate_weight(&self) -> u32 {
319 if let Some(w) = self.api_weight {
321 return w.min(500);
322 }
323
324 let mut weight = self.category.base_weight() as f64;
325
326 if self.category.is_per_unit() {
328 if let Some(qty) = self.quantity {
329 weight *= (qty as f64).sqrt();
330 }
331 }
332
333 weight *= self.difficulty.multiplier();
335
336 weight *= self.verification.multiplier();
338
339 (weight as u32).min(500)
341 }
342}
343
344impl Attestation for ProfessionalAttestation {
345 fn layer(&self) -> CaptureLayer {
346 match self.category {
348 CredentialCategory::ApiContribution => CaptureLayer::KnowledgeAPI,
349 _ => CaptureLayer::Skill,
350 }
351 }
352
353 fn timestamp(&self) -> u64 {
354 self.timestamp
355 }
356
357 fn is_positive(&self) -> bool {
358 true }
360
361 fn magnitude(&self) -> u64 {
362 self.calculate_weight() as u64 * 10_000
364 }
365
366 fn metadata(&self) -> Option<&AttestationMetadata> {
367 None }
369}
370
371#[derive(Debug, Clone, Serialize, Deserialize)]
377pub struct AttestationRecord {
378 pub layer: CaptureLayer,
379 pub timestamp: u64,
380 pub positive: bool,
381 pub magnitude: u64,
382 pub metadata: Option<AttestationMetadata>,
383}
384
385impl Attestation for AttestationRecord {
386 fn layer(&self) -> CaptureLayer {
387 self.layer
388 }
389
390 fn timestamp(&self) -> u64 {
391 self.timestamp
392 }
393
394 fn is_positive(&self) -> bool {
395 self.positive
396 }
397
398 fn magnitude(&self) -> u64 {
399 self.magnitude
400 }
401
402 fn metadata(&self) -> Option<&AttestationMetadata> {
403 self.metadata.as_ref()
404 }
405}
406
407#[derive(Debug, Clone)]
409pub struct VaultAttestation {
410 pub action: VaultAction,
411 pub timestamp: u64,
412 pub amount: u64,
413 pub duration_days: Option<u16>,
414 pub held_to_maturity: bool,
415}
416
417#[derive(Debug, Clone, Copy, PartialEq, Eq)]
418pub enum VaultAction {
419 Initialize,
420 Stack,
421 Unstack,
422 ClaimYield,
423 EarlyWithdrawal,
424}
425
426impl Attestation for VaultAttestation {
427 fn layer(&self) -> CaptureLayer {
428 CaptureLayer::Shopping }
430
431 fn timestamp(&self) -> u64 {
432 self.timestamp
433 }
434
435 fn is_positive(&self) -> bool {
436 match self.action {
437 VaultAction::Initialize => true,
438 VaultAction::Stack => true,
439 VaultAction::ClaimYield => true,
440 VaultAction::Unstack => self.held_to_maturity,
441 VaultAction::EarlyWithdrawal => false,
442 }
443 }
444
445 fn magnitude(&self) -> u64 {
446 self.amount
447 }
448
449 fn metadata(&self) -> Option<&AttestationMetadata> {
450 None
451 }
452}
453
454#[derive(Debug, Clone, Default, Serialize, Deserialize)]
460pub struct ReputationDimension {
461 pub score: u32,
463 pub positive_signals: u64,
465 pub negative_signals: u64,
467 pub last_updated: u64,
469 pub cumulative_magnitude: u64,
471}
472
473impl ReputationDimension {
474 pub fn new() -> Self {
475 Self::default()
476 }
477
478 pub fn apply_decay(&mut self, current_time: u64) {
480 if self.last_updated == 0 {
481 return;
482 }
483
484 let days_inactive = (current_time - self.last_updated) / 86400;
485 if days_inactive > 0 {
486 let decay_factor = 0.5_f64.powf(days_inactive as f64 / DECAY_HALF_LIFE_DAYS as f64);
488 self.score = ((self.score as f64) * decay_factor) as u32;
489 }
490 }
491}
492
493#[derive(Debug, Clone, Serialize, Deserialize)]
499pub struct TrustScore {
500 pub composite: u32,
502
503 pub reliability: u32,
505 pub skill: u32,
506 pub social: u32,
507 pub tenure: u32,
508 pub infrastructure: u32,
509
510 pub tier: TrustTier,
512
513 pub calculated_at: u64,
515
516 pub zk_commitment: String,
518
519 pub threshold_proofs: HashMap<String, bool>,
521}
522
523#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
524pub enum TrustTier {
525 Newcomer,
527 Established,
529 Trusted,
531 Power,
533 Elite,
535}
536
537impl TrustTier {
538 pub fn from_score(score: u32) -> Self {
539 match score {
540 0..=199 => TrustTier::Newcomer,
541 200..=399 => TrustTier::Established,
542 400..=599 => TrustTier::Trusted,
543 600..=799 => TrustTier::Power,
544 _ => TrustTier::Elite,
545 }
546 }
547
548 pub fn collateral_discount_bps(&self) -> u16 {
550 match self {
551 TrustTier::Newcomer => 0,
552 TrustTier::Established => 50, TrustTier::Trusted => 100, TrustTier::Power => 200, TrustTier::Elite => 350, }
557 }
558
559 pub fn max_sub_agents(&self) -> u8 {
561 match self {
562 TrustTier::Newcomer => 0,
563 TrustTier::Established => 1,
564 TrustTier::Trusted => 3,
565 TrustTier::Power => 10,
566 TrustTier::Elite => 50,
567 }
568 }
569}
570
571#[derive(Debug)]
577pub struct ReputationEngine {
578 pub user_pubkey: String,
580
581 pub layer_scores: HashMap<CaptureLayer, ReputationDimension>,
583
584 pub account_created: u64,
586
587 pub total_attestations: u64,
589
590 zk_salt: [u8; 32],
592}
593
594impl ReputationEngine {
595 pub fn new(user_pubkey: String) -> Self {
597 let now = SystemTime::now()
598 .duration_since(UNIX_EPOCH)
599 .unwrap()
600 .as_secs();
601
602 let mut salt = [0u8; 32];
604 let hash = Sha256::digest(format!("{}:{}", user_pubkey, now).as_bytes());
606 salt.copy_from_slice(&hash);
607
608 Self {
609 user_pubkey,
610 layer_scores: HashMap::new(),
611 account_created: now,
612 total_attestations: 0,
613 zk_salt: salt,
614 }
615 }
616
617 pub fn process_attestation<A: Attestation>(&mut self, attestation: &A) {
619 let layer = attestation.layer();
620
621 let delta = self.calculate_score_delta(attestation);
623 let timestamp = attestation.timestamp();
624 let magnitude = attestation.magnitude();
625 let is_positive = attestation.is_positive();
626
627 let dimension = self.layer_scores.entry(layer).or_default();
629
630 if is_positive {
632 dimension.positive_signals += 1;
633 } else {
634 dimension.negative_signals += 1;
635 }
636
637 dimension.cumulative_magnitude += magnitude;
639
640 if delta > 0 {
642 dimension.score = (dimension.score + delta as u32).min(MAX_SCORE);
643 } else {
644 dimension.score = dimension.score.saturating_sub((-delta) as u32);
645 }
646
647 dimension.last_updated = timestamp;
648 self.total_attestations += 1;
649 }
650
651 fn calculate_score_delta<A: Attestation>(&self, attestation: &A) -> i32 {
653 let layer = attestation.layer();
654 let weight = layer.reputation_weight();
655 let base_delta: i32;
656
657 if attestation.is_positive() {
658 base_delta = match layer.group() {
660 LayerGroup::PassiveUtility => 15,
661 LayerGroup::Infrastructure => 30,
662 LayerGroup::Intelligence => 45,
663 LayerGroup::AggressiveAutopilot => 60,
664 };
665
666 let magnitude = attestation.magnitude();
668 let magnitude_bonus = if magnitude > 0 {
669 ((magnitude as f64).ln() * 3.0) as i32
670 } else {
671 0
672 };
673
674 let metadata_bonus = self.calculate_metadata_bonus(attestation);
676
677 ((base_delta + magnitude_bonus + metadata_bonus) as f64 * weight) as i32
678 } else {
679 base_delta = match layer.group() {
681 LayerGroup::PassiveUtility => -50,
682 LayerGroup::Infrastructure => -80,
683 LayerGroup::Intelligence => -100,
684 LayerGroup::AggressiveAutopilot => -150,
685 };
686
687 (base_delta as f64 * weight) as i32
688 }
689 }
690
691 fn calculate_metadata_bonus<A: Attestation>(&self, attestation: &A) -> i32 {
693 let mut bonus = 0i32;
694
695 if let Some(meta) = attestation.metadata() {
696 if let Some(days) = meta.lock_duration_days {
698 bonus += match days {
699 0..=29 => 0,
700 30..=89 => 5,
701 90..=179 => 15,
702 180..=364 => 30,
703 _ => 50,
704 };
705 }
706
707 if meta.held_to_maturity == Some(true) {
709 bonus += 25;
710 }
711
712 if let Some(accuracy) = meta.accuracy_percent {
714 bonus += (accuracy as i32 - 50) / 5; }
716
717 if let Some(uptime) = meta.uptime_percent {
719 if uptime >= 99 {
720 bonus += 20;
721 } else if uptime >= 95 {
722 bonus += 10;
723 }
724 }
725
726 if let Some(rate) = meta.completion_rate {
728 bonus += (rate as i32 - 70) / 3; }
730 }
731
732 bonus
733 }
734
735 pub fn calculate_trust_score(&mut self) -> TrustScore {
737 let now = SystemTime::now()
738 .duration_since(UNIX_EPOCH)
739 .unwrap()
740 .as_secs();
741
742 for dimension in self.layer_scores.values_mut() {
744 dimension.apply_decay(now);
745 }
746
747 let reliability = self.calculate_reliability_score();
749 let skill = self.calculate_skill_score();
750 let social = self.calculate_social_score();
751 let tenure = self.calculate_tenure_score(now);
752 let infrastructure = self.calculate_infrastructure_score();
753
754 let composite = (
756 (reliability as f64 * 0.30) +
757 (skill as f64 * 0.25) +
758 (infrastructure as f64 * 0.20) +
759 (social as f64 * 0.15) +
760 (tenure as f64 * 0.10)
761 ) as u32;
762
763 let composite = composite.min(MAX_SCORE);
764 let tier = TrustTier::from_score(composite);
765
766 let zk_commitment = self.generate_zk_commitment(composite, reliability, skill);
768
769 let mut threshold_proofs = HashMap::new();
771 threshold_proofs.insert(
772 "swarm_coordinator".to_string(),
773 composite >= SWARM_COORDINATOR_THRESHOLD,
774 );
775 threshold_proofs.insert(
776 "collateral_eligible".to_string(),
777 composite >= COLLATERAL_THRESHOLD,
778 );
779 threshold_proofs.insert("trusted".to_string(), composite >= 400);
780 threshold_proofs.insert("power".to_string(), composite >= 600);
781 threshold_proofs.insert("elite".to_string(), composite >= 800);
782
783 TrustScore {
784 composite,
785 reliability,
786 skill,
787 social,
788 tenure,
789 infrastructure,
790 tier,
791 calculated_at: now,
792 zk_commitment,
793 threshold_proofs,
794 }
795 }
796
797 fn calculate_reliability_score(&self) -> u32 {
799 let mut score = 0u32;
800
801 if let Some(dim) = self.layer_scores.get(&CaptureLayer::Shopping) {
803 score += (dim.positive_signals * 20).min(500) as u32;
805
806 let penalty = (dim.negative_signals * 50).min(400) as u32;
808 score = score.saturating_sub(penalty);
809
810 let cred_stacked = dim.cumulative_magnitude / 1_000_000; score += ((cred_stacked as f64).sqrt() * 8.0) as u32;
813
814 score += dim.score / 5;
816 }
817
818 if let Some(dim) = self.layer_scores.get(&CaptureLayer::Insurance) {
820 score += (dim.positive_signals * 8).min(150) as u32;
821 }
822
823 if let Some(dim) = self.layer_scores.get(&CaptureLayer::Liquidity) {
825 score += (dim.positive_signals * 12).min(200) as u32;
826 }
827
828 score.min(MAX_SCORE)
829 }
830
831 fn calculate_skill_score(&self) -> u32 {
833 let mut score = 0u32;
834
835 if let Some(dim) = self.layer_scores.get(&CaptureLayer::Skill) {
837 score += (dim.positive_signals * 20).min(400) as u32;
838 }
839
840 if let Some(dim) = self.layer_scores.get(&CaptureLayer::Data) {
842 let accuracy_ratio = if dim.positive_signals + dim.negative_signals > 0 {
843 dim.positive_signals as f64
844 / (dim.positive_signals + dim.negative_signals) as f64
845 } else {
846 0.5
847 };
848 score += (accuracy_ratio * 200.0) as u32;
849 }
850
851 if let Some(dim) = self.layer_scores.get(&CaptureLayer::CurationSignal) {
853 score += (dim.positive_signals * 15).min(200) as u32;
854 }
855
856 if let Some(dim) = self.layer_scores.get(&CaptureLayer::KnowledgeAPI) {
858 score += (dim.positive_signals * 12).min(150) as u32;
859 }
860
861 if let Some(dim) = self.layer_scores.get(&CaptureLayer::PersonalModelLicensing) {
863 if dim.positive_signals > 0 {
864 score += 100; }
866 }
867
868 score.min(MAX_SCORE)
869 }
870
871 fn calculate_social_score(&self) -> u32 {
873 let mut score = 0u32;
874
875 if let Some(dim) = self.layer_scores.get(&CaptureLayer::Social) {
877 score += (dim.positive_signals * 15).min(400) as u32;
878 }
879
880 if let Some(dim) = self.layer_scores.get(&CaptureLayer::Referral) {
882 score += (dim.positive_signals * 10).min(200) as u32;
883 }
884
885 if let Some(dim) = self.layer_scores.get(&CaptureLayer::SubAgentManager) {
887 score += (dim.positive_signals * 20).min(250) as u32;
888 }
889
890 if let Some(dim) = self.layer_scores.get(&CaptureLayer::SwarmCoordinationFee) {
892 score += (dim.positive_signals * 25).min(200) as u32;
893 }
894
895 score.min(MAX_SCORE)
896 }
897
898 fn calculate_tenure_score(&self, now: u64) -> u32 {
900 let account_age_days = (now - self.account_created) / 86400;
901
902 let tenure_score = ((account_age_days as f64).ln() * 50.0) as u32;
905
906 let activity_bonus = (self.total_attestations as f64 / 10.0).min(200.0) as u32;
908
909 (tenure_score + activity_bonus).min(MAX_SCORE)
910 }
911
912 fn calculate_infrastructure_score(&self) -> u32 {
914 let mut score = 0u32;
915
916 let infra_layers = [
917 CaptureLayer::Compute,
918 CaptureLayer::Network,
919 CaptureLayer::Energy,
920 CaptureLayer::DePINAggregator,
921 CaptureLayer::InferenceArbitrage,
922 CaptureLayer::StorageDePIN,
923 ];
924
925 for layer in infra_layers {
926 if let Some(dim) = self.layer_scores.get(&layer) {
927 score += (dim.positive_signals * 25).min(200) as u32;
929
930 let penalty = (dim.negative_signals * 50).min(100) as u32;
932 score = score.saturating_sub(penalty);
933 }
934 }
935
936 score.min(MAX_SCORE)
937 }
938
939 fn generate_zk_commitment(&self, composite: u32, reliability: u32, skill: u32) -> String {
941 let data = format!(
942 "{}:{}:{}:{}",
943 composite,
944 reliability,
945 skill,
946 hex::encode(&self.zk_salt)
947 );
948 let hash = Sha256::digest(data.as_bytes());
949 hex::encode(hash)
950 }
951
952 pub fn verify_threshold(
954 &mut self,
955 threshold_name: &str,
956 commitment: &str,
957 ) -> Option<bool> {
958 let trust_score = self.calculate_trust_score();
959
960 if trust_score.zk_commitment != commitment {
962 return None;
963 }
964
965 trust_score.threshold_proofs.get(threshold_name).copied()
966 }
967
968 pub fn can_coordinate_swarm(&mut self) -> bool {
970 let score = self.calculate_trust_score();
971 score.composite >= SWARM_COORDINATOR_THRESHOLD
972 }
973
974 pub fn qualifies_for_collateral(&mut self) -> bool {
976 let score = self.calculate_trust_score();
977 score.composite >= COLLATERAL_THRESHOLD
978 }
979
980 pub fn collateral_discount_bps(&mut self) -> u16 {
982 let score = self.calculate_trust_score();
983 score.tier.collateral_discount_bps()
984 }
985
986 pub fn max_sub_agents(&mut self) -> u8 {
988 let score = self.calculate_trust_score();
989 score.tier.max_sub_agents()
990 }
991}
992
993#[derive(Debug, Serialize, Deserialize)]
999pub struct ReputationResponse {
1000 pub user: String,
1001 pub trust_score: TrustScore,
1002 pub capabilities: ReputationCapabilities,
1003 pub layer_activity: HashMap<String, LayerActivity>,
1004}
1005
1006#[derive(Debug, Serialize, Deserialize)]
1007pub struct ReputationCapabilities {
1008 pub can_coordinate_swarm: bool,
1009 pub qualifies_for_collateral: bool,
1010 pub collateral_discount_bps: u16,
1011 pub max_sub_agents: u8,
1012 pub available_layers: Vec<String>,
1013}
1014
1015#[derive(Debug, Serialize, Deserialize)]
1016pub struct LayerActivity {
1017 pub score: u32,
1018 pub positive_signals: u64,
1019 pub negative_signals: u64,
1020 pub last_active: u64,
1021}
1022
1023impl ReputationEngine {
1024 pub fn to_api_response(&mut self) -> ReputationResponse {
1026 let trust_score = self.calculate_trust_score();
1027
1028 let capabilities = ReputationCapabilities {
1029 can_coordinate_swarm: self.can_coordinate_swarm(),
1030 qualifies_for_collateral: self.qualifies_for_collateral(),
1031 collateral_discount_bps: self.collateral_discount_bps(),
1032 max_sub_agents: self.max_sub_agents(),
1033 available_layers: self.get_available_layers(),
1034 };
1035
1036 let mut layer_activity = HashMap::new();
1037 for (layer, dim) in &self.layer_scores {
1038 layer_activity.insert(
1039 format!("{:?}", layer),
1040 LayerActivity {
1041 score: dim.score,
1042 positive_signals: dim.positive_signals,
1043 negative_signals: dim.negative_signals,
1044 last_active: dim.last_updated,
1045 },
1046 );
1047 }
1048
1049 ReputationResponse {
1050 user: self.user_pubkey.clone(),
1051 trust_score,
1052 capabilities,
1053 layer_activity,
1054 }
1055 }
1056
1057 fn get_available_layers(&mut self) -> Vec<String> {
1059 let score = self.calculate_trust_score();
1060 let mut layers = vec![
1061 "Shopping",
1063 "Referral",
1064 "Attention",
1065 "Data",
1066 ];
1067
1068 if score.composite >= 200 {
1069 layers.extend(["Insurance", "Compute", "Storage"]);
1070 }
1071
1072 if score.composite >= 400 {
1073 layers.extend([
1074 "Network",
1075 "Energy",
1076 "DePINAggregator",
1077 "Skill",
1078 "CurationSignal",
1079 ]);
1080 }
1081
1082 if score.composite >= 600 {
1083 layers.extend([
1084 "InferenceArbitrage",
1085 "Social",
1086 "KnowledgeAPI",
1087 "Liquidity",
1088 "GovernanceProxy",
1089 ]);
1090 }
1091
1092 if score.composite >= 800 {
1093 layers.extend([
1094 "PersonalModelLicensing",
1095 "InventoryArbitrage",
1096 "SubAgentManager",
1097 "ReputationCollateral",
1098 "SwarmCoordinationFee",
1099 ]);
1100 }
1101
1102 layers.into_iter().map(String::from).collect()
1103 }
1104}
1105
1106#[cfg(test)]
1111mod tests {
1112 use super::*;
1113
1114 #[test]
1115 fn test_new_user_starts_at_zero() {
1116 let engine = ReputationEngine::new("test_user".to_string());
1117 assert_eq!(engine.total_attestations, 0);
1118 assert!(engine.layer_scores.is_empty());
1119 }
1120
1121 #[test]
1122 fn test_vault_attestation_increases_reliability() {
1123 let mut engine = ReputationEngine::new("test_user".to_string());
1124
1125 let attestation = VaultAttestation {
1126 action: VaultAction::Stack,
1127 timestamp: 1711497600,
1128 amount: 100_000_000, duration_days: Some(90),
1130 held_to_maturity: true,
1131 };
1132
1133 engine.process_attestation(&attestation);
1134
1135 let score = engine.calculate_trust_score();
1136 assert!(score.reliability > 0);
1137 assert!(score.composite > 0);
1138 }
1139
1140 #[test]
1141 fn test_early_withdrawal_decreases_score() {
1142 let mut engine = ReputationEngine::new("test_user".to_string());
1143
1144 for i in 0..10 {
1146 let stack = VaultAttestation {
1147 action: VaultAction::Stack,
1148 timestamp: 1711497600 + i * 86400,
1149 amount: 100_000_000,
1150 duration_days: Some(90),
1151 held_to_maturity: true,
1152 };
1153 engine.process_attestation(&stack);
1154 }
1155
1156 let score_before = engine.calculate_trust_score().composite;
1157 println!("Score before early withdrawal: {}", score_before);
1158 assert!(score_before > 0);
1159
1160 let early = VaultAttestation {
1162 action: VaultAction::EarlyWithdrawal,
1163 timestamp: 1711584000,
1164 amount: 100_000_000,
1165 duration_days: None,
1166 held_to_maturity: false,
1167 };
1168 engine.process_attestation(&early);
1169
1170 let score_after = engine.calculate_trust_score().composite;
1171 println!("Score after early withdrawal: {}", score_after);
1172
1173 let dim = engine.layer_scores.get(&CaptureLayer::Shopping).unwrap();
1175 assert!(dim.negative_signals > 0, "Should have recorded negative signal");
1176 }
1177
1178 #[test]
1179 fn test_swarm_threshold() {
1180 let mut engine = ReputationEngine::new("test_user".to_string());
1181
1182 assert!(!engine.can_coordinate_swarm());
1184
1185 for i in 0..30 {
1188 let attestation = AttestationRecord {
1189 layer: CaptureLayer::Shopping,
1190 timestamp: 1711497600 + i * 86400,
1191 positive: true,
1192 magnitude: 100_000_000, metadata: Some(AttestationMetadata {
1194 lock_duration_days: Some(365),
1195 held_to_maturity: Some(true),
1196 ..Default::default()
1197 }),
1198 };
1199 engine.process_attestation(&attestation);
1200 }
1201
1202 for i in 0..20 {
1204 let attestation = AttestationRecord {
1205 layer: CaptureLayer::Skill,
1206 timestamp: 1711497600 + i * 86400,
1207 positive: true,
1208 magnitude: 10_000_000,
1209 metadata: Some(AttestationMetadata {
1210 accuracy_percent: Some(95),
1211 ..Default::default()
1212 }),
1213 };
1214 engine.process_attestation(&attestation);
1215 }
1216
1217 for i in 0..20 {
1219 let attestation = AttestationRecord {
1220 layer: CaptureLayer::Network,
1221 timestamp: 1711497600 + i * 86400,
1222 positive: true,
1223 magnitude: 5_000_000,
1224 metadata: Some(AttestationMetadata {
1225 uptime_percent: Some(99),
1226 ..Default::default()
1227 }),
1228 };
1229 engine.process_attestation(&attestation);
1230 }
1231
1232 let score = engine.calculate_trust_score();
1234 println!("Score after multi-layer attestations: {}", score.composite);
1235 println!(" Reliability: {}", score.reliability);
1236 println!(" Skill: {}", score.skill);
1237 println!(" Infrastructure: {}", score.infrastructure);
1238
1239 assert!(score.composite >= 400, "Expected score >= 400, got {}", score.composite);
1241 }
1242
1243 #[test]
1244 fn test_tier_classification() {
1245 assert_eq!(TrustTier::from_score(0), TrustTier::Newcomer);
1246 assert_eq!(TrustTier::from_score(199), TrustTier::Newcomer);
1247 assert_eq!(TrustTier::from_score(200), TrustTier::Established);
1248 assert_eq!(TrustTier::from_score(400), TrustTier::Trusted);
1249 assert_eq!(TrustTier::from_score(600), TrustTier::Power);
1250 assert_eq!(TrustTier::from_score(800), TrustTier::Elite);
1251 assert_eq!(TrustTier::from_score(1000), TrustTier::Elite);
1252 }
1253
1254 #[test]
1255 fn test_zk_commitment_deterministic() {
1256 let mut engine = ReputationEngine::new("test_user".to_string());
1257
1258 let score1 = engine.calculate_trust_score();
1259 let score2 = engine.calculate_trust_score();
1260
1261 assert_eq!(score1.zk_commitment, score2.zk_commitment);
1263 }
1264}