openzeppelin_relayer/utils/
address_derivation.rs1use super::der::extract_public_key_from_der;
8
9#[derive(Debug, thiserror::Error)]
10pub enum AddressDerivationError {
11 #[error("Parse Error: {0}")]
12 ParseError(String),
13}
14
15pub fn derive_ethereum_address_from_der(der: &[u8]) -> Result<[u8; 20], AddressDerivationError> {
17 let pub_key = extract_public_key_from_der(der)
18 .map_err(|e| AddressDerivationError::ParseError(e.to_string()))?;
19
20 let hash = alloy::primitives::keccak256(pub_key);
21
22 let address_bytes = &hash[hash.len() - 20..];
24
25 let mut array = [0u8; 20];
26 array.copy_from_slice(address_bytes);
27
28 Ok(array)
29}
30
31pub fn derive_ethereum_address_from_pem(pem_str: &str) -> Result<[u8; 20], AddressDerivationError> {
33 let pkey =
34 pem::parse(pem_str).map_err(|e| AddressDerivationError::ParseError(e.to_string()))?;
35 let der = pkey.contents();
36 derive_ethereum_address_from_der(der)
37}
38
39pub fn derive_solana_address_from_pem(pem_str: &str) -> Result<String, AddressDerivationError> {
41 let pkey =
42 pem::parse(pem_str).map_err(|e| AddressDerivationError::ParseError(e.to_string()))?;
43 let content = pkey.contents();
44
45 let mut array = [0u8; 32];
46
47 match content.len() {
48 32 => array.copy_from_slice(content),
49 44 => array.copy_from_slice(&content[12..]),
50 _ => {
51 return Err(AddressDerivationError::ParseError(format!(
52 "Unexpected ed25519 public key length: got {} bytes (expected 32 or 44).",
53 content.len()
54 )));
55 }
56 }
57
58 let solana_address = bs58::encode(array).into_string();
59 Ok(solana_address)
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 const VALID_SECP256K1_PEM: &str = "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEjJaJh5wfZwvj8b3bQ4GYikqDTLXWUjMh\nkFs9lGj2N9B17zo37p4PSy99rDio0QHLadpso0rtTJDSISRW9MdOqA==\n-----END PUBLIC KEY-----\n"; const VALID_ED25519_PEM: &str = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAnUV+ReQWxMZ3Z2pC/5aOPPjcc8jzOo0ZgSl7+j4AMLo=\n-----END PUBLIC KEY-----\n";
69
70 #[test]
71 fn test_derive_ethereum_address_from_pem_with_invalid_data() {
72 let invalid_pem = "not-a-valid-pem";
73 let result = derive_ethereum_address_from_pem(invalid_pem);
74 assert!(result.is_err());
75
76 assert!(matches!(result, Err(AddressDerivationError::ParseError(_))));
78 }
79
80 #[test]
81 fn test_derive_ethereum_address_from_pem_with_valid_secp256k1() {
82 let result = derive_ethereum_address_from_pem(VALID_SECP256K1_PEM);
83 assert!(result.is_ok());
84
85 let address = result.unwrap();
86 assert_eq!(address.len(), 20); assert_eq!(
89 format!("0x{}", hex::encode(address)),
90 "0xeeb8861f51b3f3f2204d64bbf7a7eb25e1b4d6cd"
91 );
92 }
93
94 #[test]
95 fn test_derive_ethereum_address_from_der_with_invalid_data() {
96 let invalid_der = &[1, 2, 3];
97 let result = derive_ethereum_address_from_der(invalid_der);
98 assert!(result.is_err());
99
100 assert!(matches!(result, Err(AddressDerivationError::ParseError(_))));
102 }
103
104 #[test]
105 fn test_derive_ethereum_address_from_der_with_valid_secp256k1() {
106 let pem = pem::parse(VALID_SECP256K1_PEM).unwrap();
107 let der = pem.contents();
108 let result = derive_ethereum_address_from_der(der);
109
110 assert!(result.is_ok());
111
112 let address = result.unwrap();
113 assert_eq!(address.len(), 20); assert_eq!(
116 format!("0x{}", hex::encode(address)),
117 "0xeeb8861f51b3f3f2204d64bbf7a7eb25e1b4d6cd"
118 );
119 }
120
121 #[test]
122 fn test_derive_solana_address_from_pem_with_invalid_data() {
123 let invalid_pem = "not-a-valid-pem";
124 let result = derive_solana_address_from_pem(invalid_pem);
125 assert!(result.is_err());
126
127 assert!(matches!(result, Err(AddressDerivationError::ParseError(_))));
129 }
130
131 #[test]
132 fn test_derive_solana_address_from_pem_with_valid_ed25519() {
133 let result = derive_solana_address_from_pem(VALID_ED25519_PEM);
134 assert!(result.is_ok());
135
136 let address = result.unwrap();
137 assert!(!address.is_empty());
139 assert!(address.len() >= 32 && address.len() <= 44);
140
141 assert_eq!(address, "BavUBpkD77FABnevMkBVqV8BDHv7gX8sSoYYJY9WU9L5");
142 }
143
144 #[test]
145 fn test_derive_solana_address_from_pem_with_invalid_key_length() {
146 let invalid_ed25519_der = vec![0u8; 10]; let invalid_pem = pem::Pem::new("PUBLIC KEY", invalid_ed25519_der);
149 let invalid_pem_str = pem::encode(&invalid_pem);
150
151 let result = derive_solana_address_from_pem(&invalid_pem_str);
152 assert!(result.is_err());
153
154 assert!(matches!(result, Err(AddressDerivationError::ParseError(_))));
155 }
156}