1pub mod dispatch;
7
8use tempo_contracts::precompiles::VALIDATOR_CONFIG_ADDRESS;
9pub use tempo_contracts::precompiles::{IValidatorConfig, ValidatorConfigError};
10use tempo_precompiles_macros::{Storable, contract};
11
12use crate::{
13 error::{Result, TempoPrecompileError},
14 ip_validation::ensure_address_is_ip_port,
15 storage::{Handler, Mapping},
16};
17use alloy::primitives::{Address, B256};
18use tracing::trace;
19
20#[derive(Debug, Storable)]
22struct Validator {
23 public_key: B256,
25 active: bool,
27 index: u64,
29 validator_address: Address,
31 inbound_address: String,
33 outbound_address: String,
36}
37
38#[contract(addr = VALIDATOR_CONFIG_ADDRESS)]
43pub struct ValidatorConfig {
44 owner: Address,
46 validators_array: Vec<Address>,
48 validators: Mapping<Address, Validator>,
50 next_dkg_ceremony: u64,
52}
53
54impl ValidatorConfig {
55 pub fn initialize(&mut self, owner: Address) -> Result<()> {
57 trace!(address=%self.address, %owner, "Initializing validator config precompile");
58
59 self.__initialize()?;
61 self.owner.write(owner)
62 }
63
64 pub fn owner(&self) -> Result<Address> {
66 self.owner.read()
67 }
68
69 pub fn check_owner(&self, caller: Address) -> Result<()> {
71 if self.owner()? != caller {
72 return Err(ValidatorConfigError::unauthorized())?;
73 }
74
75 Ok(())
76 }
77
78 pub fn change_owner(
83 &mut self,
84 sender: Address,
85 call: IValidatorConfig::changeOwnerCall,
86 ) -> Result<()> {
87 self.check_owner(sender)?;
88 self.owner.write(call.newOwner)
89 }
90
91 pub fn validator_count(&self) -> Result<u64> {
93 self.validators_array.len().map(|c| c as u64)
94 }
95
96 pub fn validators_array(&self, index: u64) -> Result<Address> {
101 match self.validators_array.at(index as usize)? {
102 Some(elem) => elem.read(),
103 None => Err(TempoPrecompileError::array_oob()),
104 }
105 }
106
107 pub fn validators(&self, validator: Address) -> Result<IValidatorConfig::Validator> {
109 let validator_info = self.validators[validator].read()?;
110 Ok(IValidatorConfig::Validator {
111 publicKey: validator_info.public_key,
112 active: validator_info.active,
113 index: validator_info.index,
114 validatorAddress: validator_info.validator_address,
115 inboundAddress: validator_info.inbound_address,
116 outboundAddress: validator_info.outbound_address,
117 })
118 }
119
120 fn validator_exists(&self, validator: Address) -> Result<bool> {
123 let validator = self.validators[validator].read()?;
124 Ok(!validator.public_key.is_zero())
125 }
126
127 pub fn get_validators(&self) -> Result<Vec<IValidatorConfig::Validator>> {
129 let count = self.validators_array.len()?;
130 let mut validators = Vec::with_capacity(count);
131
132 for i in 0..count {
133 let validator_address = self.validators_array[i].read()?;
135
136 let Validator {
137 public_key,
138 active,
139 index,
140 validator_address: _,
141 inbound_address,
142 outbound_address,
143 } = self.validators[validator_address].read()?;
144
145 validators.push(IValidatorConfig::Validator {
146 publicKey: public_key,
147 active,
148 index,
149 validatorAddress: validator_address,
150 inboundAddress: inbound_address,
151 outboundAddress: outbound_address,
152 });
153 }
154
155 Ok(validators)
156 }
157
158 pub fn add_validator(
170 &mut self,
171 sender: Address,
172 call: IValidatorConfig::addValidatorCall,
173 ) -> Result<()> {
174 if call.publicKey.is_zero() {
176 return Err(ValidatorConfigError::invalid_public_key())?;
177 }
178
179 self.check_owner(sender)?;
181
182 if self.validator_exists(call.newValidatorAddress)? {
184 return Err(ValidatorConfigError::validator_already_exists())?;
185 }
186
187 if self.storage.spec().is_t2() {
191 ensure_address_is_ip_port(&call.inboundAddress).map_err(|err| {
192 ValidatorConfigError::not_host_port(
193 "inboundAddress".to_string(),
194 call.inboundAddress.clone(),
195 err.to_string(),
196 )
197 })?;
198 ensure_address_is_ip_port(&call.outboundAddress).map_err(|err| {
199 ValidatorConfigError::not_ip_port(
200 "outboundAddress".to_string(),
201 call.outboundAddress.clone(),
202 err.to_string(),
203 )
204 })?;
205 } else {
206 ensure_address_is_ip_port(&call.inboundAddress).map_err(|err| {
207 ValidatorConfigError::not_host_port(
208 "inboundAddress".to_string(),
209 call.inboundAddress.clone(),
210 format!("{err:?}"),
211 )
212 })?;
213 ensure_address_is_ip_port(&call.outboundAddress).map_err(|err| {
214 ValidatorConfigError::not_ip_port(
215 "outboundAddress".to_string(),
216 call.outboundAddress.clone(),
217 format!("{err:?}"),
218 )
219 })?;
220 }
221
222 let count = self.validator_count()?;
224 let validator = Validator {
225 public_key: call.publicKey,
226 active: call.active,
227 index: count,
228 validator_address: call.newValidatorAddress,
229 inbound_address: call.inboundAddress,
230 outbound_address: call.outboundAddress,
231 };
232 self.validators[call.newValidatorAddress].write(validator)?;
233
234 self.validators_array.push(call.newValidatorAddress)
236 }
237
238 pub fn update_validator(
260 &mut self,
261 sender: Address,
262 call: IValidatorConfig::updateValidatorCall,
263 ) -> Result<()> {
264 if call.publicKey.is_zero() {
266 return Err(ValidatorConfigError::invalid_public_key())?;
267 }
268
269 if !self.validator_exists(sender)? {
271 return Err(ValidatorConfigError::validator_not_found())?;
272 }
273
274 let old_validator = self.validators[sender].read()?;
276
277 if call.newValidatorAddress != sender {
279 if self.validator_exists(call.newValidatorAddress)? {
280 return Err(ValidatorConfigError::validator_already_exists())?;
281 }
282
283 self.validators_array[old_validator.index as usize].write(call.newValidatorAddress)?;
285
286 self.validators[sender].delete()?;
288 }
289
290 if self.storage.spec().is_t2() {
291 ensure_address_is_ip_port(&call.inboundAddress).map_err(|err| {
292 ValidatorConfigError::not_host_port(
293 "inboundAddress".to_string(),
294 call.inboundAddress.clone(),
295 err.to_string(),
296 )
297 })?;
298 ensure_address_is_ip_port(&call.outboundAddress).map_err(|err| {
299 ValidatorConfigError::not_ip_port(
300 "outboundAddress".to_string(),
301 call.outboundAddress.clone(),
302 err.to_string(),
303 )
304 })?;
305 } else {
306 ensure_address_is_ip_port(&call.inboundAddress).map_err(|err| {
307 ValidatorConfigError::not_host_port(
308 "inboundAddress".to_string(),
309 call.inboundAddress.clone(),
310 format!("{err:?}"),
311 )
312 })?;
313 ensure_address_is_ip_port(&call.outboundAddress).map_err(|err| {
314 ValidatorConfigError::not_ip_port(
315 "outboundAddress".to_string(),
316 call.outboundAddress.clone(),
317 format!("{err:?}"),
318 )
319 })?;
320 }
321
322 let updated_validator = Validator {
323 public_key: call.publicKey,
324 active: old_validator.active,
325 index: old_validator.index,
326 validator_address: call.newValidatorAddress,
327 inbound_address: call.inboundAddress,
328 outbound_address: call.outboundAddress,
329 };
330
331 self.validators[call.newValidatorAddress].write(updated_validator)
332 }
333
334 pub fn change_validator_status(
342 &mut self,
343 sender: Address,
344 call: IValidatorConfig::changeValidatorStatusCall,
345 ) -> Result<()> {
346 self.check_owner(sender)?;
347
348 if !self.validator_exists(call.validator)? {
349 return Err(ValidatorConfigError::validator_not_found())?;
350 }
351
352 let mut validator = self.validators[call.validator].read()?;
353 validator.active = call.active;
354 self.validators[call.validator].write(validator)
355 }
356
357 pub fn change_validator_status_by_index(
365 &mut self,
366 sender: Address,
367 call: IValidatorConfig::changeValidatorStatusByIndexCall,
368 ) -> Result<()> {
369 self.check_owner(sender)?;
370
371 let validator_address = match self.validators_array.at(call.index as usize)? {
373 Some(elem) => elem.read()?,
374 None => return Err(ValidatorConfigError::validator_not_found())?,
375 };
376
377 let mut validator = self.validators[validator_address].read()?;
378 validator.active = call.active;
379 self.validators[validator_address].write(validator)
380 }
381
382 pub fn get_next_full_dkg_ceremony(&self) -> Result<u64> {
386 self.next_dkg_ceremony.read()
387 }
388
389 pub fn next_dkg_ceremony(&self) -> Result<u64> {
391 self.next_dkg_ceremony.read()
392 }
393
394 pub fn set_next_full_dkg_ceremony(
401 &mut self,
402 sender: Address,
403 call: IValidatorConfig::setNextFullDkgCeremonyCall,
404 ) -> Result<()> {
405 self.check_owner(sender)?;
406 self.next_dkg_ceremony.write(call.epoch)
407 }
408}
409
410#[cfg(test)]
411mod tests {
412 use super::*;
413 use crate::storage::{StorageCtx, hashmap::HashMapStorageProvider};
414 use alloy::primitives::Address;
415 use alloy_primitives::FixedBytes;
416
417 #[test]
418 fn test_owner_initialization_and_change() -> eyre::Result<()> {
419 let mut storage = HashMapStorageProvider::new(1);
420 let owner1 = Address::random();
421 let owner2 = Address::random();
422 StorageCtx::enter(&mut storage, || {
423 let mut validator_config = ValidatorConfig::new();
424
425 validator_config.initialize(owner1)?;
427
428 let current_owner = validator_config.owner()?;
430 assert_eq!(
431 current_owner, owner1,
432 "Owner should be owner1 after initialization"
433 );
434
435 validator_config.change_owner(
437 owner1,
438 IValidatorConfig::changeOwnerCall { newOwner: owner2 },
439 )?;
440
441 let current_owner = validator_config.owner()?;
443 assert_eq!(current_owner, owner2, "Owner should be owner2 after change");
444
445 Ok(())
446 })
447 }
448
449 #[test]
450 fn test_owner_only_functions() -> eyre::Result<()> {
451 let mut storage = HashMapStorageProvider::new(1);
452 let owner1 = Address::random();
453 let owner2 = Address::random();
454 let validator1 = Address::random();
455 let validator2 = Address::random();
456 StorageCtx::enter(&mut storage, || {
457 let mut validator_config = ValidatorConfig::new();
458
459 validator_config.initialize(owner1)?;
461
462 let public_key = FixedBytes::<32>::from([0x44; 32]);
464 validator_config.add_validator(
465 owner1,
466 IValidatorConfig::addValidatorCall {
467 newValidatorAddress: validator1,
468 publicKey: public_key,
469 inboundAddress: "192.168.1.1:8000".to_string(),
470 active: true,
471 outboundAddress: "192.168.1.1:9000".to_string(),
472 },
473 )?;
474
475 let validators = validator_config.get_validators()?;
477 assert_eq!(validators.len(), 1, "Should have 1 validator");
478 assert_eq!(validators[0].validatorAddress, validator1);
479 assert_eq!(validators[0].publicKey, public_key);
480 assert!(validators[0].active, "New validator should be active");
481
482 validator_config.change_validator_status(
484 owner1,
485 IValidatorConfig::changeValidatorStatusCall {
486 validator: validator1,
487 active: false,
488 },
489 )?;
490
491 let validators = validator_config.get_validators()?;
493 assert!(!validators[0].active, "Validator should be inactive");
494
495 let res = validator_config.add_validator(
497 owner2,
498 IValidatorConfig::addValidatorCall {
499 newValidatorAddress: validator2,
500 publicKey: FixedBytes::<32>::from([0x66; 32]),
501 inboundAddress: "192.168.1.2:8000".to_string(),
502 active: true,
503 outboundAddress: "192.168.1.2:9000".to_string(),
504 },
505 );
506 assert_eq!(res, Err(ValidatorConfigError::unauthorized().into()));
507
508 let res = validator_config.change_validator_status(
510 owner2,
511 IValidatorConfig::changeValidatorStatusCall {
512 validator: validator1,
513 active: true,
514 },
515 );
516 assert_eq!(res, Err(ValidatorConfigError::unauthorized().into()));
517
518 Ok(())
519 })
520 }
521
522 #[test]
523 fn test_validator_lifecycle() -> eyre::Result<()> {
524 let mut storage = HashMapStorageProvider::new(1);
525 StorageCtx::enter(&mut storage, || {
526 let owner = Address::from([0x01; 20]);
527
528 let mut validator_config = ValidatorConfig::new();
529 validator_config.initialize(owner)?;
530
531 let validator1 = Address::from([0x11; 20]);
532 let public_key1 = FixedBytes::<32>::from([0x21; 32]);
533 let inbound1 = "192.168.1.1:8000".to_string();
534 let outbound1 = "192.168.1.1:9000".to_string();
535 validator_config.add_validator(
536 owner,
537 IValidatorConfig::addValidatorCall {
538 newValidatorAddress: validator1,
539 publicKey: public_key1,
540 inboundAddress: inbound1.clone(),
541 active: true,
542 outboundAddress: outbound1,
543 },
544 )?;
545
546 let result = validator_config.add_validator(
548 owner,
549 IValidatorConfig::addValidatorCall {
550 newValidatorAddress: validator1,
551 publicKey: FixedBytes::<32>::from([0x22; 32]),
552 inboundAddress: "192.168.1.1:8000".to_string(),
553 active: true,
554 outboundAddress: "192.168.1.1:9000".to_string(),
555 },
556 );
557 assert_eq!(
558 result,
559 Err(ValidatorConfigError::validator_already_exists().into()),
560 "Should return ValidatorAlreadyExists error"
561 );
562
563 let validator2 = Address::from([0x12; 20]);
565 let public_key2 = FixedBytes::<32>::from([0x22; 32]);
566 validator_config.add_validator(
567 owner,
568 IValidatorConfig::addValidatorCall {
569 newValidatorAddress: validator2,
570 publicKey: public_key2,
571 inboundAddress: "192.168.1.2:8000".to_string(),
572 active: true,
573 outboundAddress: "192.168.1.2:9000".to_string(),
574 },
575 )?;
576
577 let validator3 = Address::from([0x13; 20]);
578 let public_key3 = FixedBytes::<32>::from([0x23; 32]);
579 validator_config.add_validator(
580 owner,
581 IValidatorConfig::addValidatorCall {
582 newValidatorAddress: validator3,
583 publicKey: public_key3,
584 inboundAddress: "192.168.1.3:8000".to_string(),
585 active: false,
586 outboundAddress: "192.168.1.3:9000".to_string(),
587 },
588 )?;
589
590 let validator4 = Address::from([0x14; 20]);
591 let public_key4 = FixedBytes::<32>::from([0x24; 32]);
592 validator_config.add_validator(
593 owner,
594 IValidatorConfig::addValidatorCall {
595 newValidatorAddress: validator4,
596 publicKey: public_key4,
597 inboundAddress: "192.168.1.4:8000".to_string(),
598 active: true,
599 outboundAddress: "192.168.1.4:9000".to_string(),
600 },
601 )?;
602
603 let validator5 = Address::from([0x15; 20]);
604 let public_key5 = FixedBytes::<32>::from([0x25; 32]);
605 validator_config.add_validator(
606 owner,
607 IValidatorConfig::addValidatorCall {
608 newValidatorAddress: validator5,
609 publicKey: public_key5,
610 inboundAddress: "192.168.1.5:8000".to_string(),
611 active: true,
612 outboundAddress: "192.168.1.5:9000".to_string(),
613 },
614 )?;
615
616 let mut validators = validator_config.get_validators()?;
618
619 assert_eq!(validators.len(), 5, "Should have 5 validators");
621
622 validators.sort_by_key(|v| v.validatorAddress);
624
625 assert_eq!(validators[0].validatorAddress, validator1);
627 assert_eq!(validators[0].publicKey, public_key1);
628 assert_eq!(validators[0].inboundAddress, inbound1);
629 assert!(validators[0].active);
630
631 assert_eq!(validators[1].validatorAddress, validator2);
632 assert_eq!(validators[1].publicKey, public_key2);
633 assert_eq!(validators[1].inboundAddress, "192.168.1.2:8000");
634 assert!(validators[1].active);
635
636 assert_eq!(validators[2].validatorAddress, validator3);
637 assert_eq!(validators[2].publicKey, public_key3);
638 assert_eq!(validators[2].inboundAddress, "192.168.1.3:8000");
639 assert!(!validators[2].active);
640
641 assert_eq!(validators[3].validatorAddress, validator4);
642 assert_eq!(validators[3].publicKey, public_key4);
643 assert_eq!(validators[3].inboundAddress, "192.168.1.4:8000");
644 assert!(validators[3].active);
645
646 assert_eq!(validators[4].validatorAddress, validator5);
647 assert_eq!(validators[4].publicKey, public_key5);
648 assert_eq!(validators[4].inboundAddress, "192.168.1.5:8000");
649 assert!(validators[4].active);
650
651 let public_key1_new = FixedBytes::<32>::from([0x31; 32]);
653 let short_inbound1 = "10.0.0.1:8000".to_string();
654 let short_outbound1 = "10.0.0.1:9000".to_string();
655 validator_config.update_validator(
656 validator1,
657 IValidatorConfig::updateValidatorCall {
658 newValidatorAddress: validator1,
659 publicKey: public_key1_new,
660 inboundAddress: short_inbound1.clone(),
661 outboundAddress: short_outbound1,
662 },
663 )?;
664
665 let validator2_new = Address::from([0x22; 20]);
667 validator_config.update_validator(
668 validator2,
669 IValidatorConfig::updateValidatorCall {
670 newValidatorAddress: validator2_new,
671 publicKey: public_key2,
672 inboundAddress: "192.168.1.2:8000".to_string(),
673 outboundAddress: "192.168.1.2:9000".to_string(),
674 },
675 )?;
676
677 let validator3_new = Address::from([0x23; 20]);
679 let long_inbound3 = "192.169.1.3:8000".to_string();
680 let long_outbound3 = "192.168.1.3:9000".to_string();
681 validator_config.update_validator(
682 validator3,
683 IValidatorConfig::updateValidatorCall {
684 newValidatorAddress: validator3_new,
685 publicKey: public_key3,
686 inboundAddress: long_inbound3.clone(),
687 outboundAddress: long_outbound3,
688 },
689 )?;
690
691 let mut validators = validator_config.get_validators()?;
693
694 assert_eq!(validators.len(), 5, "Should still have 5 validators");
696
697 validators.sort_by_key(|v| v.validatorAddress);
699
700 assert_eq!(validators[0].validatorAddress, validator1);
702 assert_eq!(
703 validators[0].publicKey, public_key1_new,
704 "PublicKey should be updated"
705 );
706 assert_eq!(
707 validators[0].inboundAddress, short_inbound1,
708 "Address should be updated to short"
709 );
710 assert!(validators[0].active);
711
712 assert_eq!(validators[1].validatorAddress, validator4);
714 assert_eq!(validators[1].publicKey, public_key4);
715 assert_eq!(validators[1].inboundAddress, "192.168.1.4:8000");
716 assert!(validators[1].active);
717
718 assert_eq!(validators[2].validatorAddress, validator5);
720 assert_eq!(validators[2].publicKey, public_key5);
721 assert_eq!(validators[2].inboundAddress, "192.168.1.5:8000");
722 assert!(validators[2].active);
723
724 assert_eq!(validators[3].validatorAddress, validator2_new);
726 assert_eq!(
727 validators[3].publicKey, public_key2,
728 "PublicKey should be same"
729 );
730 assert_eq!(
731 validators[3].inboundAddress, "192.168.1.2:8000",
732 "IP should be same"
733 );
734 assert!(validators[3].active);
735
736 assert_eq!(validators[4].validatorAddress, validator3_new);
738 assert_eq!(
739 validators[4].publicKey, public_key3,
740 "PublicKey should be same"
741 );
742 assert_eq!(
743 validators[4].inboundAddress, long_inbound3,
744 "Address should be updated to long"
745 );
746 assert!(!validators[4].active);
747
748 Ok(())
749 })
750 }
751
752 #[test]
753 fn test_owner_cannot_update_validator() -> eyre::Result<()> {
754 let mut storage = HashMapStorageProvider::new(1);
755 let owner = Address::random();
756 let validator = Address::random();
757 StorageCtx::enter(&mut storage, || {
758 let mut validator_config = ValidatorConfig::new();
759 validator_config.initialize(owner)?;
760
761 let public_key = FixedBytes::<32>::from([0x21; 32]);
763 validator_config.add_validator(
764 owner,
765 IValidatorConfig::addValidatorCall {
766 newValidatorAddress: validator,
767 publicKey: public_key,
768 inboundAddress: "192.168.1.1:8000".to_string(),
769 active: true,
770 outboundAddress: "192.168.1.1:9000".to_string(),
771 },
772 )?;
773
774 let result = validator_config.update_validator(
776 owner,
777 IValidatorConfig::updateValidatorCall {
778 newValidatorAddress: validator,
779 publicKey: FixedBytes::<32>::from([0x22; 32]),
780 inboundAddress: "10.0.0.1:8000".to_string(),
781 outboundAddress: "10.0.0.1:9000".to_string(),
782 },
783 );
784
785 assert_eq!(
786 result,
787 Err(ValidatorConfigError::validator_not_found().into()),
788 "Should return ValidatorNotFound error"
789 );
790
791 Ok(())
792 })
793 }
794
795 #[test]
796 fn test_validator_rotation_clears_all_slots() -> eyre::Result<()> {
797 let mut storage = HashMapStorageProvider::new(1);
798 let owner = Address::random();
799 let validator1 = Address::random();
800 let validator2 = Address::random();
801 StorageCtx::enter(&mut storage, || {
802 let mut validator_config = ValidatorConfig::new();
803 validator_config.initialize(owner)?;
804
805 let long_inbound = "192.168.1.1:8000".to_string();
807 let long_outbound = "192.168.1.1:9000".to_string();
808 let public_key = FixedBytes::<32>::from([0x21; 32]);
809
810 validator_config.add_validator(
811 owner,
812 IValidatorConfig::addValidatorCall {
813 newValidatorAddress: validator1,
814 publicKey: public_key,
815 inboundAddress: long_inbound,
816 active: true,
817 outboundAddress: long_outbound,
818 },
819 )?;
820
821 validator_config.update_validator(
823 validator1,
824 IValidatorConfig::updateValidatorCall {
825 newValidatorAddress: validator2,
826 publicKey: public_key,
827 inboundAddress: "10.0.0.1:8000".to_string(),
828 outboundAddress: "10.0.0.1:9000".to_string(),
829 },
830 )?;
831
832 let validator = validator_config.validators[validator1].read()?;
834
835 assert_eq!(
837 validator.public_key,
838 B256::ZERO,
839 "Old validator public key should be cleared"
840 );
841 assert_eq!(
842 validator.validator_address,
843 Address::ZERO,
844 "Old validator address should be cleared"
845 );
846 assert_eq!(validator.index, 0, "Old validator index should be cleared");
847 assert!(!validator.active, "Old validator should be inactive");
848 assert_eq!(
849 validator.inbound_address,
850 String::default(),
851 "Old validator inbound address should be cleared"
852 );
853 assert_eq!(
854 validator.outbound_address,
855 String::default(),
856 "Old validator outbound address should be cleared"
857 );
858
859 Ok(())
860 })
861 }
862
863 #[test]
864 fn test_next_dkg_ceremony() -> eyre::Result<()> {
865 let mut storage = HashMapStorageProvider::new(1);
866 let owner = Address::random();
867 let non_owner = Address::random();
868 StorageCtx::enter(&mut storage, || {
869 let mut validator_config = ValidatorConfig::new();
870 validator_config.initialize(owner)?;
871
872 assert_eq!(validator_config.get_next_full_dkg_ceremony()?, 0);
874
875 validator_config.set_next_full_dkg_ceremony(
877 owner,
878 IValidatorConfig::setNextFullDkgCeremonyCall { epoch: 42 },
879 )?;
880 assert_eq!(validator_config.get_next_full_dkg_ceremony()?, 42);
881
882 let result = validator_config.set_next_full_dkg_ceremony(
884 non_owner,
885 IValidatorConfig::setNextFullDkgCeremonyCall { epoch: 100 },
886 );
887 assert_eq!(result, Err(ValidatorConfigError::unauthorized().into()));
888
889 assert_eq!(validator_config.get_next_full_dkg_ceremony()?, 42);
891
892 Ok(())
893 })
894 }
895
896 #[test]
897 fn test_ipv4_with_port_is_host_port() {
898 ensure_address_is_ip_port("127.0.0.1:8000").unwrap();
899 }
900
901 #[test]
902 fn test_ipv6_with_port_is_host_port() {
903 ensure_address_is_ip_port("[::1]:8000").unwrap();
904 }
905
906 #[test]
907 fn test_add_validator_rejects_zero_public_key() -> eyre::Result<()> {
908 let mut storage = HashMapStorageProvider::new(1);
909 let owner = Address::random();
910 let validator = Address::random();
911 StorageCtx::enter(&mut storage, || {
912 let mut validator_config = ValidatorConfig::new();
913 validator_config.initialize(owner)?;
914
915 let zero_public_key = FixedBytes::<32>::ZERO;
916 let result = validator_config.add_validator(
917 owner,
918 IValidatorConfig::addValidatorCall {
919 newValidatorAddress: validator,
920 publicKey: zero_public_key,
921 inboundAddress: "192.168.1.1:8000".to_string(),
922 active: true,
923 outboundAddress: "192.168.1.1:9000".to_string(),
924 },
925 );
926
927 assert_eq!(
928 result,
929 Err(ValidatorConfigError::invalid_public_key().into()),
930 "Should reject zero public key"
931 );
932
933 let validators = validator_config.get_validators()?;
935 assert_eq!(validators.len(), 0, "Should have no validators");
936
937 Ok(())
938 })
939 }
940
941 #[test]
942 fn test_update_validator_rejects_zero_public_key() -> eyre::Result<()> {
943 let mut storage = HashMapStorageProvider::new(1);
944 let owner = Address::random();
945 let validator = Address::random();
946 StorageCtx::enter(&mut storage, || {
947 let mut validator_config = ValidatorConfig::new();
948 validator_config.initialize(owner)?;
949
950 let original_public_key = FixedBytes::<32>::from([0x44; 32]);
951 validator_config.add_validator(
952 owner,
953 IValidatorConfig::addValidatorCall {
954 newValidatorAddress: validator,
955 publicKey: original_public_key,
956 inboundAddress: "192.168.1.1:8000".to_string(),
957 active: true,
958 outboundAddress: "192.168.1.1:9000".to_string(),
959 },
960 )?;
961
962 let zero_public_key = FixedBytes::<32>::ZERO;
963 let result = validator_config.update_validator(
964 validator,
965 IValidatorConfig::updateValidatorCall {
966 newValidatorAddress: validator,
967 publicKey: zero_public_key,
968 inboundAddress: "192.168.1.1:8000".to_string(),
969 outboundAddress: "192.168.1.1:9000".to_string(),
970 },
971 );
972
973 assert_eq!(
974 result,
975 Err(ValidatorConfigError::invalid_public_key().into()),
976 "Should reject zero public key in update"
977 );
978
979 let validators = validator_config.get_validators()?;
981 assert_eq!(validators.len(), 1, "Should still have 1 validator");
982 assert_eq!(
983 validators[0].publicKey, original_public_key,
984 "Original public key should be preserved"
985 );
986
987 Ok(())
988 })
989 }
990
991 #[test]
992 fn test_validators_array_returns_correct_address() -> eyre::Result<()> {
993 let mut storage = HashMapStorageProvider::new(1);
994 let owner = Address::random();
995 let validator1 = Address::random();
996 let validator2 = Address::random();
997 StorageCtx::enter(&mut storage, || {
998 let mut validator_config = ValidatorConfig::new();
999 validator_config.initialize(owner)?;
1000
1001 let public_key1 = FixedBytes::<32>::from([0x11; 32]);
1002 let public_key2 = FixedBytes::<32>::from([0x22; 32]);
1003
1004 validator_config.add_validator(
1006 owner,
1007 IValidatorConfig::addValidatorCall {
1008 newValidatorAddress: validator1,
1009 publicKey: public_key1,
1010 inboundAddress: "192.168.1.1:8000".to_string(),
1011 active: true,
1012 outboundAddress: "192.168.1.1:9000".to_string(),
1013 },
1014 )?;
1015
1016 validator_config.add_validator(
1017 owner,
1018 IValidatorConfig::addValidatorCall {
1019 newValidatorAddress: validator2,
1020 publicKey: public_key2,
1021 inboundAddress: "192.168.1.2:8000".to_string(),
1022 active: true,
1023 outboundAddress: "192.168.1.2:9000".to_string(),
1024 },
1025 )?;
1026
1027 assert_eq!(validator_config.validators_array(0)?, validator1);
1029 assert_eq!(validator_config.validators_array(1)?, validator2);
1030
1031 assert_ne!(validator_config.validators_array(0)?, Address::ZERO);
1033 assert_ne!(validator_config.validators_array(1)?, Address::ZERO);
1034
1035 Ok(())
1036 })
1037 }
1038
1039 #[test]
1040 fn test_next_dkg_ceremony_returns_correct_value() -> eyre::Result<()> {
1041 let mut storage = HashMapStorageProvider::new(1);
1042 let owner = Address::random();
1043 StorageCtx::enter(&mut storage, || {
1044 let mut validator_config = ValidatorConfig::new();
1045 validator_config.initialize(owner)?;
1046
1047 assert_eq!(validator_config.get_next_full_dkg_ceremony()?, 0);
1049
1050 validator_config.set_next_full_dkg_ceremony(
1052 owner,
1053 IValidatorConfig::setNextFullDkgCeremonyCall { epoch: 100 },
1054 )?;
1055
1056 let result = validator_config.get_next_full_dkg_ceremony()?;
1058 assert_eq!(result, 100);
1059 assert_ne!(result, 0);
1060 assert_ne!(result, 1);
1061
1062 Ok(())
1063 })
1064 }
1065}