1#[cfg(not(feature = "std"))]
7use alloc::{string::String, vec::Vec};
8
9use crate::{
10 fee_model::{AgentStatus, FeeModel, FeeModelParams},
11 immutable::{ImmutableDeployment, ImmutableStatus, LockableConfig},
12 payout::{FeeRouter, PayoutConfig},
13 types::{rUv, Timestamp},
14 Error, Result,
15};
16use serde::{Deserialize, Serialize};
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct ExchangeConfig {
21 pub immutable_deployment: ImmutableDeployment,
23
24 #[serde(skip)]
26 pub fee_model: Option<FeeModel>,
27
28 pub network: NetworkConfig,
30
31 pub security: SecurityConfig,
33
34 pub business_plan: Option<BusinessPlanConfig>,
36
37 #[serde(skip)]
39 pub fee_router: Option<FeeRouter>,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct NetworkConfig {
45 pub chain_id: u64,
47
48 pub network_name: String,
50
51 pub bootstrap_peers: Vec<String>,
53
54 pub listen_address: String,
56
57 pub enable_dark_addressing: bool,
59}
60
61impl Default for NetworkConfig {
62 fn default() -> Self {
63 Self {
64 chain_id: 1,
65 network_name: "qudag-exchange".to_string(),
66 bootstrap_peers: Vec::new(),
67 listen_address: "0.0.0.0:8080".to_string(),
68 enable_dark_addressing: true,
69 }
70 }
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct SecurityConfig {
76 pub require_quantum_signatures: bool,
78
79 pub min_signature_strength: String,
81
82 pub enable_replay_protection: bool,
84
85 pub default_tx_expiry_seconds: u64,
87
88 pub enable_rate_limiting: bool,
90
91 pub max_tx_per_minute: u32,
93}
94
95impl Default for SecurityConfig {
96 fn default() -> Self {
97 Self {
98 require_quantum_signatures: true,
99 min_signature_strength: "ML-DSA-87".to_string(),
100 enable_replay_protection: true,
101 default_tx_expiry_seconds: 300, enable_rate_limiting: true,
103 max_tx_per_minute: 10,
104 }
105 }
106}
107
108impl ExchangeConfig {
109 pub fn new() -> Result<Self> {
111 let mut config = Self {
112 immutable_deployment: ImmutableDeployment::new(),
113 fee_model: None,
114 network: NetworkConfig::default(),
115 security: SecurityConfig::default(),
116 business_plan: None, fee_router: None,
118 };
119
120 config.initialize_fee_model()?;
122 Ok(config)
123 }
124
125 pub fn from_lockable_config(lockable: LockableConfig) -> Result<Self> {
127 let mut config = Self {
128 immutable_deployment: ImmutableDeployment::with_config(lockable.clone())?,
129 fee_model: None,
130 network: NetworkConfig {
131 chain_id: lockable.chain_id,
132 ..NetworkConfig::default()
133 },
134 security: SecurityConfig::default(),
135 business_plan: None, fee_router: None,
137 };
138
139 config.initialize_fee_model()?;
140 Ok(config)
141 }
142
143 fn initialize_fee_model(&mut self) -> Result<()> {
145 let fee_params = self.immutable_deployment.system_config.fee_params.clone();
146 self.fee_model = Some(FeeModel::with_params(fee_params)?);
147 Ok(())
148 }
149
150 pub fn update_fee_params(
152 &mut self,
153 params: FeeModelParams,
154 current_time: Timestamp,
155 ) -> Result<()> {
156 self.immutable_deployment
158 .update_fee_params(params.clone(), current_time)?;
159
160 if let Some(ref mut fee_model) = self.fee_model {
162 fee_model.update_params(params)?;
163 } else {
164 self.fee_model = Some(FeeModel::with_params(params)?);
165 }
166
167 Ok(())
168 }
169
170 pub fn calculate_transaction_fee(
172 &self,
173 transaction_amount: rUv,
174 agent_status: &AgentStatus,
175 current_time: Timestamp,
176 ) -> Result<rUv> {
177 let fee_model = self
178 .fee_model
179 .as_ref()
180 .ok_or_else(|| Error::Other("Fee model not initialized".into()))?;
181
182 fee_model.calculate_fee_amount(transaction_amount, agent_status, current_time)
183 }
184
185 pub fn get_fee_rate(&self, agent_status: &AgentStatus, current_time: Timestamp) -> Result<f64> {
187 let fee_model = self
188 .fee_model
189 .as_ref()
190 .ok_or_else(|| Error::Other("Fee model not initialized".into()))?;
191
192 fee_model.calculate_fee_rate(agent_status, current_time)
193 }
194
195 pub fn enable_immutable_mode(&mut self) -> Result<()> {
197 self.immutable_deployment.enable_immutable_mode()
198 }
199
200 #[cfg(feature = "std")]
202 pub fn lock_system(
203 &mut self,
204 keypair: &qudag_crypto::MlDsaKeyPair,
205 current_time: Timestamp,
206 ) -> Result<()> {
207 self.immutable_deployment.lock_system(keypair, current_time)
208 }
209
210 pub fn can_modify_config(&self, current_time: Timestamp) -> bool {
212 self.immutable_deployment.can_modify_config(current_time)
213 }
214
215 pub fn get_immutable_status(&self, current_time: Timestamp) -> ImmutableStatus {
217 self.immutable_deployment.get_status(current_time)
218 }
219
220 pub fn update_network_config(
222 &mut self,
223 network: NetworkConfig,
224 current_time: Timestamp,
225 ) -> Result<()> {
226 if !self.can_modify_config(current_time) {
227 return Err(Error::Other(
228 "Cannot modify network configuration: system is immutably locked".into(),
229 ));
230 }
231
232 self.immutable_deployment.system_config.chain_id = network.chain_id;
234 self.network = network;
235 Ok(())
236 }
237
238 pub fn update_security_config(
240 &mut self,
241 security: SecurityConfig,
242 current_time: Timestamp,
243 ) -> Result<()> {
244 if !self.can_modify_config(current_time) {
245 return Err(Error::Other(
246 "Cannot modify security configuration: system is immutably locked".into(),
247 ));
248 }
249
250 self.security = security;
251 Ok(())
252 }
253
254 pub fn validate(&self) -> Result<()> {
256 self.immutable_deployment.system_config.validate()?;
258
259 if self.network.chain_id == 0 {
261 return Err(Error::Other("chain_id must be greater than 0".into()));
262 }
263
264 if self.network.network_name.is_empty() {
265 return Err(Error::Other("network_name cannot be empty".into()));
266 }
267
268 if self.security.default_tx_expiry_seconds == 0 {
270 return Err(Error::Other(
271 "default_tx_expiry_seconds must be greater than 0".into(),
272 ));
273 }
274
275 if self.security.max_tx_per_minute == 0 {
276 return Err(Error::Other(
277 "max_tx_per_minute must be greater than 0".into(),
278 ));
279 }
280
281 Ok(())
282 }
283
284 pub fn get_summary(&self, current_time: Timestamp) -> ConfigSummary {
286 let immutable_status = self.get_immutable_status(current_time);
287 let fee_params = &self.immutable_deployment.system_config.fee_params;
288
289 let business_plan_summary = self.business_plan.as_ref().map(|bp| {
290 let total_contributors = self
291 .fee_router
292 .as_ref()
293 .map(|router| router.get_payout_history(None).len() as u32)
294 .unwrap_or(0);
295
296 BusinessPlanSummary {
297 enabled: bp.enabled,
298 auto_distribution_enabled: bp.enable_auto_distribution,
299 vault_management_enabled: bp.enable_vault_management,
300 role_earnings_enabled: bp.enable_role_earnings,
301 bounty_rewards_enabled: bp.enable_bounty_rewards,
302 total_contributors,
303 min_payout_threshold: bp.payout_config.min_payout_threshold.amount(),
304 system_fee_percentage: bp.payout_config.system_fee_percentage,
305 }
306 });
307
308 ConfigSummary {
309 network_name: self.network.network_name.clone(),
310 chain_id: self.network.chain_id,
311 immutable_status,
312 fee_model_summary: FeeModelSummary {
313 f_min: fee_params.f_min,
314 f_max: fee_params.f_max,
315 f_min_verified: fee_params.f_min_verified,
316 f_max_verified: fee_params.f_max_verified,
317 time_constant_days: fee_params.time_constant_seconds / (24 * 60 * 60),
318 usage_threshold: fee_params.usage_threshold_ruv,
319 },
320 security_enabled: self.security.require_quantum_signatures,
321 dark_addressing_enabled: self.network.enable_dark_addressing,
322 business_plan_summary,
323 }
324 }
325
326 #[cfg(feature = "std")]
328 pub fn governance_override(
329 &mut self,
330 governance_keypair: &qudag_crypto::MlDsaKeyPair,
331 current_time: Timestamp,
332 ) -> Result<()> {
333 self.immutable_deployment
334 .governance_override(governance_keypair, current_time)
335 }
336
337 pub fn enable_business_plan(&mut self, config: BusinessPlanConfig) -> Result<()> {
339 config.payout_config.validate()?;
341
342 if config.enable_auto_distribution {
344 self.fee_router = Some(FeeRouter::new(config.payout_config.clone()));
345 }
346
347 self.business_plan = Some(config);
348 Ok(())
349 }
350
351 pub fn disable_business_plan(&mut self) {
353 self.business_plan = None;
354 self.fee_router = None;
355 }
356
357 pub fn has_business_plan(&self) -> bool {
359 self.business_plan.as_ref().map_or(false, |bp| bp.enabled)
360 }
361
362 pub fn fee_router(&self) -> Option<&FeeRouter> {
364 self.fee_router.as_ref()
365 }
366
367 pub fn fee_router_mut(&mut self) -> Option<&mut FeeRouter> {
369 self.fee_router.as_mut()
370 }
371
372 pub fn update_business_plan(
374 &mut self,
375 config: BusinessPlanConfig,
376 current_time: Timestamp,
377 ) -> Result<()> {
378 if !self.can_modify_config(current_time) {
379 return Err(Error::Other(
380 "Cannot modify business plan configuration: system is immutably locked".into(),
381 ));
382 }
383
384 config.payout_config.validate()?;
386
387 if config.enable_auto_distribution {
389 if let Some(ref mut fee_router) = self.fee_router {
390 fee_router.update_config(config.payout_config.clone())?;
391 } else {
392 self.fee_router = Some(FeeRouter::new(config.payout_config.clone()));
393 }
394 } else {
395 self.fee_router = None;
396 }
397
398 self.business_plan = Some(config);
399 Ok(())
400 }
401
402 pub fn to_bytes(&self) -> Result<Vec<u8>> {
404 bincode::serialize(self).map_err(|e| Error::SerializationError(e.to_string()))
405 }
406
407 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
409 let mut config: Self =
410 bincode::deserialize(bytes).map_err(|e| Error::SerializationError(e.to_string()))?;
411
412 config.initialize_fee_model()?;
414
415 if let Some(ref business_plan) = config.business_plan {
417 if business_plan.enabled && business_plan.enable_auto_distribution {
418 config.fee_router = Some(FeeRouter::new(business_plan.payout_config.clone()));
419 }
420 }
421
422 config.validate()?;
423 Ok(config)
424 }
425}
426
427impl Default for ExchangeConfig {
428 fn default() -> Self {
429 Self::new().expect("Default configuration should be valid")
430 }
431}
432
433#[derive(Debug, Clone, Serialize, Deserialize)]
435pub struct BusinessPlanConfig {
436 pub enabled: bool,
438
439 pub payout_config: PayoutConfig,
441
442 pub enable_vault_management: bool,
444
445 pub enable_auto_distribution: bool,
447
448 pub enable_role_earnings: bool,
450
451 pub enable_bounty_rewards: bool,
453
454 pub governance: GovernanceConfig,
456}
457
458impl Default for BusinessPlanConfig {
459 fn default() -> Self {
460 Self {
461 enabled: false, payout_config: PayoutConfig::default(),
463 enable_vault_management: false,
464 enable_auto_distribution: false,
465 enable_role_earnings: false,
466 enable_bounty_rewards: false,
467 governance: GovernanceConfig::default(),
468 }
469 }
470}
471
472#[derive(Debug, Clone, Serialize, Deserialize)]
474pub struct GovernanceConfig {
475 pub allow_custom_percentages: bool,
477
478 pub require_approval_threshold: Option<rUv>,
480
481 pub enable_voting: bool,
483
484 pub min_voting_period_seconds: u64,
486}
487
488impl Default for GovernanceConfig {
489 fn default() -> Self {
490 Self {
491 allow_custom_percentages: true,
492 require_approval_threshold: Some(rUv::new(10000)), enable_voting: false,
494 min_voting_period_seconds: 7 * 24 * 60 * 60, }
496 }
497}
498
499#[derive(Debug, Clone, Serialize, Deserialize)]
501pub struct ConfigSummary {
502 pub network_name: String,
503 pub chain_id: u64,
504 pub immutable_status: ImmutableStatus,
505 pub fee_model_summary: FeeModelSummary,
506 pub security_enabled: bool,
507 pub dark_addressing_enabled: bool,
508 pub business_plan_summary: Option<BusinessPlanSummary>,
509}
510
511#[derive(Debug, Clone, Serialize, Deserialize)]
513pub struct BusinessPlanSummary {
514 pub enabled: bool,
515 pub auto_distribution_enabled: bool,
516 pub vault_management_enabled: bool,
517 pub role_earnings_enabled: bool,
518 pub bounty_rewards_enabled: bool,
519 pub total_contributors: u32,
520 pub min_payout_threshold: u64,
521 pub system_fee_percentage: f64,
522}
523
524#[derive(Debug, Clone, Serialize, Deserialize)]
526pub struct FeeModelSummary {
527 pub f_min: f64,
528 pub f_max: f64,
529 pub f_min_verified: f64,
530 pub f_max_verified: f64,
531 pub time_constant_days: u64,
532 pub usage_threshold: u64,
533}
534
535pub struct ExchangeConfigBuilder {
537 network: NetworkConfig,
538 security: SecurityConfig,
539 fee_params: FeeModelParams,
540 enable_immutable: bool,
541 business_plan: Option<BusinessPlanConfig>,
542}
543
544impl ExchangeConfigBuilder {
545 pub fn new() -> Self {
547 Self {
548 network: NetworkConfig::default(),
549 security: SecurityConfig::default(),
550 fee_params: FeeModelParams::default(),
551 enable_immutable: false,
552 business_plan: None,
553 }
554 }
555
556 pub fn with_network(mut self, network: NetworkConfig) -> Self {
558 self.network = network;
559 self
560 }
561
562 pub fn with_security(mut self, security: SecurityConfig) -> Self {
564 self.security = security;
565 self
566 }
567
568 pub fn with_fee_params(mut self, fee_params: FeeModelParams) -> Self {
570 self.fee_params = fee_params;
571 self
572 }
573
574 pub fn with_immutable_mode(mut self) -> Self {
576 self.enable_immutable = true;
577 self
578 }
579
580 pub fn with_chain_id(mut self, chain_id: u64) -> Self {
582 self.network.chain_id = chain_id;
583 self
584 }
585
586 pub fn with_network_name(mut self, name: impl Into<String>) -> Self {
588 self.network.network_name = name.into();
589 self
590 }
591
592 pub fn with_business_plan(mut self, business_plan: BusinessPlanConfig) -> Self {
594 self.business_plan = Some(business_plan);
595 self
596 }
597
598 pub fn with_basic_business_plan(mut self) -> Self {
600 let mut bp_config = BusinessPlanConfig::default();
601 bp_config.enabled = true;
602 bp_config.enable_auto_distribution = true;
603 bp_config.enable_role_earnings = true;
604 self.business_plan = Some(bp_config);
605 self
606 }
607
608 pub fn build(self) -> Result<ExchangeConfig> {
610 let lockable_config = LockableConfig {
611 fee_params: self.fee_params,
612 chain_id: self.network.chain_id,
613 ..LockableConfig::default()
614 };
615
616 let mut config = ExchangeConfig::from_lockable_config(lockable_config)?;
617 config.network = self.network;
618 config.security = self.security;
619
620 if self.enable_immutable {
621 config.enable_immutable_mode()?;
622 }
623
624 if let Some(business_plan) = self.business_plan {
626 config.enable_business_plan(business_plan)?;
627 }
628
629 config.validate()?;
630 Ok(config)
631 }
632}
633
634impl Default for ExchangeConfigBuilder {
635 fn default() -> Self {
636 Self::new()
637 }
638}
639
640#[cfg(test)]
641mod tests {
642 use super::*;
643
644 #[test]
645 fn test_config_creation() {
646 let config = ExchangeConfig::new().unwrap();
647 assert!(config.fee_model.is_some());
648 assert_eq!(config.network.chain_id, 1);
649 assert_eq!(config.network.network_name, "qudag-exchange");
650 config.validate().unwrap();
651 }
652
653 #[test]
654 fn test_config_builder() {
655 let config = ExchangeConfigBuilder::new()
656 .with_chain_id(42)
657 .with_network_name("test-network")
658 .with_immutable_mode()
659 .build()
660 .unwrap();
661
662 assert_eq!(config.network.chain_id, 42);
663 assert_eq!(config.network.network_name, "test-network");
664 assert!(config.immutable_deployment.config.enabled);
665 }
666
667 #[test]
668 fn test_fee_calculation() {
669 let config = ExchangeConfig::new().unwrap();
670 let agent = AgentStatus::new_unverified(Timestamp::new(0));
671 let current_time = Timestamp::new(1000);
672
673 let fee = config
674 .calculate_transaction_fee(rUv::new(1000), &agent, current_time)
675 .unwrap();
676
677 assert_eq!(fee.amount(), 1); let rate = config.get_fee_rate(&agent, current_time).unwrap();
681 assert!((rate - 0.001).abs() < 1e-10);
682 }
683
684 #[test]
685 fn test_config_modification_restrictions() {
686 let mut config = ExchangeConfig::new().unwrap();
687 config.enable_immutable_mode().unwrap();
688
689 let current_time = Timestamp::new(1000);
690
691 assert!(config.can_modify_config(current_time));
693
694 let new_params = FeeModelParams::default();
695 config.update_fee_params(new_params, current_time).unwrap();
696
697 config.immutable_deployment.config.locked_at = Some(current_time);
699 config.immutable_deployment.config.lock_signature =
700 Some(crate::immutable::ImmutableSignature {
701 algorithm: "ML-DSA-87".to_string(),
702 public_key: vec![1, 2, 3],
703 signature: vec![4, 5, 6],
704 config_hash: crate::types::Hash::from_bytes([0u8; 32]),
705 });
706
707 let post_grace = Timestamp::new(current_time.value() + 25 * 60 * 60);
709 assert!(!config.can_modify_config(post_grace));
710
711 let result = config.update_fee_params(FeeModelParams::default(), post_grace);
712 assert!(result.is_err());
713 }
714
715 #[test]
716 fn test_config_summary() {
717 let config = ExchangeConfig::new().unwrap();
718 let current_time = Timestamp::new(1000);
719
720 let summary = config.get_summary(current_time);
721 assert_eq!(summary.network_name, "qudag-exchange");
722 assert_eq!(summary.chain_id, 1);
723 assert!(!summary.immutable_status.enabled);
724 assert_eq!(summary.fee_model_summary.f_min, 0.001);
725 }
726
727 #[test]
728 fn test_config_serialization() {
729 let config = ExchangeConfig::new().unwrap();
730
731 let bytes = config.to_bytes().unwrap();
732 let restored = ExchangeConfig::from_bytes(&bytes).unwrap();
733
734 assert!(restored.fee_model.is_some());
736 assert_eq!(config.network.chain_id, restored.network.chain_id);
737 assert_eq!(config.network.network_name, restored.network.network_name);
738 }
739
740 #[test]
741 fn test_network_config_validation() {
742 let mut config = ExchangeConfig::new().unwrap();
743
744 config.validate().unwrap();
746
747 config.network.chain_id = 0;
749 assert!(config.validate().is_err());
750
751 config.network.chain_id = 1;
753 config.network.network_name = String::new();
754 assert!(config.validate().is_err());
755 }
756}