tempo_validator_config/
lib.rs1#![cfg_attr(not(test), warn(unused_crate_dependencies))]
5
6use std::net::{IpAddr, SocketAddr};
7
8use alloy_primitives::{Address, B256, Keccak256};
9use commonware_codec::DecodeExt as _;
10use commonware_cryptography::{Verifier as _, ed25519};
11use tempo_contracts::precompiles::VALIDATOR_CONFIG_V2_ADDRESS;
12use tempo_precompiles::validator_config_v2::{VALIDATOR_NS_ADD, VALIDATOR_NS_ROTATE};
13
14#[derive(Debug, thiserror::Error)]
15pub enum ValidatorConfigError {
16 #[error("invalid ed25519 public key")]
17 InvalidPublicKey,
18 #[error("invalid ed25519 signature")]
19 InvalidSignature,
20 #[error("signature verification failed")]
21 SignatureVerificationFailed,
22}
23pub struct ValidatorConfig {
24 pub chain_id: u64,
25 pub validator_address: Address,
26 pub public_key: B256,
27 pub ingress: SocketAddr,
28 pub egress: IpAddr,
29}
30
31impl ValidatorConfig {
32 fn base_hasher(&self) -> Keccak256 {
35 let ingress = self.ingress.to_string();
36 let ingress_length = u8::try_from(ingress.len()).expect("ingress length must fit u8");
37
38 let egress = self.egress.to_string();
39 let egress_length = u8::try_from(egress.len()).expect("egress length must fit u8");
40
41 let mut hasher = Keccak256::new();
42 hasher.update(self.chain_id.to_be_bytes());
43 hasher.update(VALIDATOR_CONFIG_V2_ADDRESS.as_slice());
44 hasher.update(self.validator_address.as_slice());
45 hasher.update([ingress_length]);
46 hasher.update(ingress.as_bytes());
47 hasher.update([egress_length]);
48 hasher.update(egress.as_bytes());
49 hasher
50 }
51
52 pub fn add_validator_message_hash(&self, fee_recipient: Address) -> B256 {
54 let mut hasher = self.base_hasher();
55 hasher.update(fee_recipient.as_slice());
56 hasher.finalize()
57 }
58
59 pub fn rotate_validator_message_hash(&self) -> B256 {
61 self.base_hasher().finalize()
62 }
63
64 pub fn check_add_validator_signature(
66 &self,
67 fee_recipient: Address,
68 signature: &[u8],
69 ) -> Result<(), ValidatorConfigError> {
70 let message = self.add_validator_message_hash(fee_recipient);
71 self.check_signature(VALIDATOR_NS_ADD, &message, signature)
72 }
73
74 pub fn check_rotate_validator_signature(
76 &self,
77 signature: &[u8],
78 ) -> Result<(), ValidatorConfigError> {
79 let message = self.rotate_validator_message_hash();
80 self.check_signature(VALIDATOR_NS_ROTATE, &message, signature)
81 }
82
83 fn check_signature(
84 &self,
85 namespace: &[u8],
86 message: &B256,
87 signature: &[u8],
88 ) -> Result<(), ValidatorConfigError> {
89 let public_key = ed25519::PublicKey::decode(self.public_key.as_slice())
90 .map_err(|_| ValidatorConfigError::InvalidPublicKey)?;
91 let sig = ed25519::Signature::decode(signature)
92 .map_err(|_| ValidatorConfigError::InvalidSignature)?;
93 if !public_key.verify(namespace, message.as_slice(), &sig) {
94 return Err(ValidatorConfigError::SignatureVerificationFailed);
95 }
96 Ok(())
97 }
98}