1use std::fmt;
8
9use bsv_primitives::hash::{hash160, sha256d};
10
11use crate::ScriptError;
12
13const MAINNET_P2PKH: u8 = 0x00;
15const TESTNET_P2PKH: u8 = 0x6f;
17
18#[derive(Clone, Debug, PartialEq, Eq)]
20pub enum Network {
21 Mainnet,
23 Testnet,
25}
26
27#[derive(Clone, Debug, PartialEq, Eq)]
32pub struct Address {
33 pub address_string: String,
35 pub public_key_hash: [u8; 20],
37 pub network: Network,
39}
40
41impl Address {
42 pub fn from_string(addr: &str) -> Result<Self, ScriptError> {
53 let decoded = bs58::decode(addr)
54 .into_vec()
55 .map_err(|_| ScriptError::InvalidAddress(format!("bad char for '{}'", addr)))?;
56
57 if decoded.len() != 25 {
58 return Err(ScriptError::InvalidAddressLength(addr.to_string()));
59 }
60
61 let checksum = sha256d(&decoded[..21]);
63 if decoded[21..25] != checksum[..4] {
64 return Err(ScriptError::EncodingChecksumFailed);
65 }
66
67 let network = match decoded[0] {
68 MAINNET_P2PKH => Network::Mainnet,
69 TESTNET_P2PKH => Network::Testnet,
70 _ => return Err(ScriptError::UnsupportedAddress(addr.to_string())),
71 };
72
73 let mut pkh = [0u8; 20];
74 pkh.copy_from_slice(&decoded[1..21]);
75
76 Ok(Address {
77 address_string: addr.to_string(),
78 public_key_hash: pkh,
79 network,
80 })
81 }
82
83 pub fn from_public_key_hash(hash: &[u8; 20], network: Network) -> Self {
92 let version = match network {
93 Network::Mainnet => MAINNET_P2PKH,
94 Network::Testnet => TESTNET_P2PKH,
95 };
96
97 let mut payload = Vec::with_capacity(25);
98 payload.push(version);
99 payload.extend_from_slice(hash);
100 let checksum = sha256d(&payload);
101 payload.extend_from_slice(&checksum[..4]);
102
103 let address_string = bs58::encode(&payload).into_string();
104
105 Address {
106 address_string,
107 public_key_hash: *hash,
108 network,
109 }
110 }
111
112 pub fn from_public_key_string(pub_key_hex: &str, mainnet: bool) -> Result<Self, ScriptError> {
123 let pub_key_bytes = hex::decode(pub_key_hex)
124 ?;
125 let h = hash160(&pub_key_bytes);
126 let network = if mainnet { Network::Mainnet } else { Network::Testnet };
127 Ok(Self::from_public_key_hash(&h, network))
128 }
129}
130
131impl fmt::Display for Address {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 write!(f, "{}", self.address_string)
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
149
150 const TEST_PUBLIC_KEY_HASH: &str = "00ac6144c4db7b5790f343cf0477a65fb8a02eb7";
152
153 #[test]
159 fn test_from_string_mainnet() {
160 let address_str = "1E7ucTTWRTahCyViPhxSMor2pj4VGQdFMr";
161 let addr = Address::from_string(address_str).expect("should parse mainnet");
162 assert_eq!(addr.address_string, address_str);
163 assert_eq!(
164 hex::encode(addr.public_key_hash),
165 "8fe80c75c9560e8b56ed64ea3c26e18d2c52211b"
166 );
167 assert_eq!(addr.network, Network::Mainnet);
168 }
169
170 #[test]
176 fn test_from_string_testnet() {
177 let address_str = "mtdruWYVEV1wz5yL7GvpBj4MgifCB7yhPd";
178 let addr = Address::from_string(address_str).expect("should parse testnet");
179 assert_eq!(addr.address_string, address_str);
180 assert_eq!(
181 hex::encode(addr.public_key_hash),
182 "8fe80c75c9560e8b56ed64ea3c26e18d2c52211b"
183 );
184 assert_eq!(addr.network, Network::Testnet);
185 }
186
187 #[test]
189 fn test_from_string_same_pkh_different_networks() {
190 let mainnet_addr = Address::from_string("1E7ucTTWRTahCyViPhxSMor2pj4VGQdFMr")
191 .expect("mainnet should parse");
192 let testnet_addr = Address::from_string("mtdruWYVEV1wz5yL7GvpBj4MgifCB7yhPd")
193 .expect("testnet should parse");
194 assert_eq!(mainnet_addr.public_key_hash, testnet_addr.public_key_hash);
195 }
196
197 #[test]
203 fn test_from_string_short_address() {
204 let result = Address::from_string("ADD8E55");
205 assert!(result.is_err());
206 }
207
208 #[test]
210 fn test_from_string_unsupported_version() {
211 let result = Address::from_string("27BvY7rFguYQvEL872Y7Fo77Y3EBApC2EK");
212 assert!(result.is_err());
213 }
214
215 #[test]
221 fn test_from_public_key_string_mainnet() {
222 let addr = Address::from_public_key_string(
223 "026cf33373a9f3f6c676b75b543180703df225f7f8edbffedc417718a8ad4e89ce",
224 true,
225 )
226 .expect("should create address");
227 assert_eq!(
228 hex::encode(addr.public_key_hash),
229 TEST_PUBLIC_KEY_HASH
230 );
231 assert_eq!(addr.address_string, "114ZWApV4EEU8frr7zygqQcB1V2BodGZuS");
232 assert_eq!(addr.network, Network::Mainnet);
233 }
234
235 #[test]
237 fn test_from_public_key_string_testnet() {
238 let addr = Address::from_public_key_string(
239 "026cf33373a9f3f6c676b75b543180703df225f7f8edbffedc417718a8ad4e89ce",
240 false,
241 )
242 .expect("should create address");
243 assert_eq!(
244 hex::encode(addr.public_key_hash),
245 TEST_PUBLIC_KEY_HASH
246 );
247 assert_eq!(addr.address_string, "mfaWoDuTsFfiunLTqZx4fKpVsUctiDV9jk");
248 assert_eq!(addr.network, Network::Testnet);
249 }
250
251 #[test]
253 fn test_from_public_key_string_invalid() {
254 let result = Address::from_public_key_string("invalid_pubkey", true);
255 assert!(result.is_err());
256 }
257
258 #[test]
264 fn test_from_public_key_hash_mainnet() {
265 let hash_bytes = hex::decode(TEST_PUBLIC_KEY_HASH).expect("valid hex");
266 let mut hash = [0u8; 20];
267 hash.copy_from_slice(&hash_bytes);
268 let addr = Address::from_public_key_hash(&hash, Network::Mainnet);
269 assert_eq!(addr.public_key_hash, hash);
270 assert_eq!(addr.address_string, "114ZWApV4EEU8frr7zygqQcB1V2BodGZuS");
271 assert_eq!(addr.network, Network::Mainnet);
272 }
273
274 #[test]
276 fn test_from_public_key_hash_testnet() {
277 let hash_bytes = hex::decode(TEST_PUBLIC_KEY_HASH).expect("valid hex");
278 let mut hash = [0u8; 20];
279 hash.copy_from_slice(&hash_bytes);
280 let addr = Address::from_public_key_hash(&hash, Network::Testnet);
281 assert_eq!(addr.public_key_hash, hash);
282 assert_eq!(addr.address_string, "mfaWoDuTsFfiunLTqZx4fKpVsUctiDV9jk");
283 assert_eq!(addr.network, Network::Testnet);
284 }
285
286 #[test]
292 fn test_address_to_string_roundtrip_mainnet() {
293 let address_str = "1E7ucTTWRTahCyViPhxSMor2pj4VGQdFMr";
294 let addr = Address::from_string(address_str).expect("should parse");
295 assert_eq!(format!("{}", addr), address_str);
296 }
297
298 #[test]
300 fn test_address_to_string_roundtrip_testnet() {
301 let address_str = "mtdruWYVEV1wz5yL7GvpBj4MgifCB7yhPd";
302 let addr = Address::from_string(address_str).expect("should parse");
303 assert_eq!(format!("{}", addr), address_str);
304 }
305
306 #[test]
308 fn test_public_key_hash_to_address_to_string_roundtrip() {
309 let hash_bytes = hex::decode(TEST_PUBLIC_KEY_HASH).expect("valid hex");
310 let mut hash = [0u8; 20];
311 hash.copy_from_slice(&hash_bytes);
312
313 let addr = Address::from_public_key_hash(&hash, Network::Mainnet);
314 let parsed = Address::from_string(&addr.address_string).expect("should parse back");
315
316 assert_eq!(addr.public_key_hash, parsed.public_key_hash);
317 assert_eq!(addr.address_string, parsed.address_string);
318 assert_eq!(addr.network, parsed.network);
319 }
320}