1use std::{fmt, io};
4
5use crate::{
6 parameters::NetworkKind,
7 serialization::{SerializationError, ZcashDeserialize, ZcashSerialize},
8 transparent::{opcodes::OpCode, Script},
9};
10
11#[cfg(test)]
12use proptest::prelude::*;
13
14#[derive(
26 Clone, Eq, PartialEq, Hash, serde_with::SerializeDisplay, serde_with::DeserializeFromStr,
27)]
28pub enum Address {
29 PayToScriptHash {
31 network_kind: NetworkKind,
33 script_hash: [u8; 20],
35 },
36
37 PayToPublicKeyHash {
39 network_kind: NetworkKind,
41 pub_key_hash: [u8; 20],
44 },
45}
46
47impl fmt::Debug for Address {
48 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49 let mut debug_struct = f.debug_struct("TransparentAddress");
50
51 match self {
52 Address::PayToScriptHash {
53 network_kind,
54 script_hash,
55 } => debug_struct
56 .field("network_kind", network_kind)
57 .field("script_hash", &hex::encode(script_hash))
58 .finish(),
59 Address::PayToPublicKeyHash {
60 network_kind,
61 pub_key_hash,
62 } => debug_struct
63 .field("network_kind", network_kind)
64 .field("pub_key_hash", &hex::encode(pub_key_hash))
65 .finish(),
66 }
67 }
68}
69
70impl fmt::Display for Address {
71 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72 let mut bytes = io::Cursor::new(Vec::new());
73 let _ = self.zcash_serialize(&mut bytes);
74
75 f.write_str(&bs58::encode(bytes.get_ref()).with_check().into_string())
76 }
77}
78
79impl std::str::FromStr for Address {
80 type Err = SerializationError;
81
82 fn from_str(s: &str) -> Result<Self, Self::Err> {
83 let result = &bs58::decode(s).with_check(None).into_vec();
84
85 match result {
86 Ok(bytes) => Self::zcash_deserialize(&bytes[..]),
87 Err(_) => Err(SerializationError::Parse("t-addr decoding error")),
88 }
89 }
90}
91
92impl ZcashSerialize for Address {
93 fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
94 match self {
95 Address::PayToScriptHash {
96 network_kind,
97 script_hash,
98 } => {
99 writer.write_all(&network_kind.b58_script_address_prefix())?;
100 writer.write_all(script_hash)?
101 }
102 Address::PayToPublicKeyHash {
103 network_kind,
104 pub_key_hash,
105 } => {
106 writer.write_all(&network_kind.b58_pubkey_address_prefix())?;
107 writer.write_all(pub_key_hash)?
108 }
109 }
110
111 Ok(())
112 }
113}
114
115impl ZcashDeserialize for Address {
116 fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
117 let mut version_bytes = [0; 2];
118 reader.read_exact(&mut version_bytes)?;
119
120 let mut hash_bytes = [0; 20];
121 reader.read_exact(&mut hash_bytes)?;
122
123 match version_bytes {
124 zcash_primitives::constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX => {
125 Ok(Address::PayToScriptHash {
126 network_kind: NetworkKind::Mainnet,
127 script_hash: hash_bytes,
128 })
129 }
130 zcash_primitives::constants::testnet::B58_SCRIPT_ADDRESS_PREFIX => {
131 Ok(Address::PayToScriptHash {
132 network_kind: NetworkKind::Testnet,
133 script_hash: hash_bytes,
134 })
135 }
136 zcash_primitives::constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX => {
137 Ok(Address::PayToPublicKeyHash {
138 network_kind: NetworkKind::Mainnet,
139 pub_key_hash: hash_bytes,
140 })
141 }
142 zcash_primitives::constants::testnet::B58_PUBKEY_ADDRESS_PREFIX => {
143 Ok(Address::PayToPublicKeyHash {
144 network_kind: NetworkKind::Testnet,
145 pub_key_hash: hash_bytes,
146 })
147 }
148 _ => Err(SerializationError::Parse("bad t-addr version/type")),
149 }
150 }
151}
152
153impl Address {
154 pub fn from_pub_key_hash(network_kind: NetworkKind, pub_key_hash: [u8; 20]) -> Self {
156 Self::PayToPublicKeyHash {
157 network_kind,
158 pub_key_hash,
159 }
160 }
161
162 pub fn from_script_hash(network_kind: NetworkKind, script_hash: [u8; 20]) -> Self {
164 Self::PayToScriptHash {
165 network_kind,
166 script_hash,
167 }
168 }
169
170 pub fn network_kind(&self) -> NetworkKind {
172 match self {
173 Address::PayToScriptHash { network_kind, .. } => *network_kind,
174 Address::PayToPublicKeyHash { network_kind, .. } => *network_kind,
175 }
176 }
177
178 pub fn is_script_hash(&self) -> bool {
180 matches!(self, Address::PayToScriptHash { .. })
181 }
182
183 pub fn hash_bytes(&self) -> [u8; 20] {
189 match *self {
190 Address::PayToScriptHash { script_hash, .. } => script_hash,
191 Address::PayToPublicKeyHash { pub_key_hash, .. } => pub_key_hash,
192 }
193 }
194
195 pub fn create_script_from_address(&self) -> Script {
198 let mut script_bytes = Vec::new();
199
200 match self {
201 Address::PayToScriptHash { .. } => {
203 script_bytes.push(OpCode::Hash160 as u8);
204 script_bytes.push(OpCode::Push20Bytes as u8);
205 script_bytes.extend(self.hash_bytes());
206 script_bytes.push(OpCode::Equal as u8);
207 }
208 Address::PayToPublicKeyHash { .. } => {
210 script_bytes.push(OpCode::Dup as u8);
211 script_bytes.push(OpCode::Hash160 as u8);
212 script_bytes.push(OpCode::Push20Bytes as u8);
213 script_bytes.extend(self.hash_bytes());
214 script_bytes.push(OpCode::EqualVerify as u8);
215 script_bytes.push(OpCode::CheckSig as u8);
216 }
217 };
218
219 Script::new(&script_bytes)
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use ripemd::{Digest, Ripemd160};
226 use secp256k1::PublicKey;
227 use sha2::Sha256;
228
229 use super::*;
230
231 trait ToAddressWithNetwork {
232 fn to_address(&self, network: NetworkKind) -> Address;
234 }
235
236 impl ToAddressWithNetwork for Script {
237 fn to_address(&self, network_kind: NetworkKind) -> Address {
238 Address::PayToScriptHash {
239 network_kind,
240 script_hash: Address::hash_payload(self.as_raw_bytes()),
241 }
242 }
243 }
244
245 impl ToAddressWithNetwork for PublicKey {
246 fn to_address(&self, network_kind: NetworkKind) -> Address {
247 Address::PayToPublicKeyHash {
248 network_kind,
249 pub_key_hash: Address::hash_payload(&self.serialize()[..]),
250 }
251 }
252 }
253
254 impl Address {
255 #[allow(dead_code)]
263 fn hash_payload(bytes: &[u8]) -> [u8; 20] {
264 let sha_hash = Sha256::digest(bytes);
265 let ripe_hash = Ripemd160::digest(sha_hash);
266 let mut payload = [0u8; 20];
267 payload[..].copy_from_slice(&ripe_hash[..]);
268 payload
269 }
270 }
271
272 #[test]
273 fn pubkey_mainnet() {
274 let _init_guard = zebra_test::init();
275
276 let pub_key = PublicKey::from_slice(&[
277 3, 23, 183, 225, 206, 31, 159, 148, 195, 42, 67, 115, 146, 41, 248, 140, 11, 3, 51, 41,
278 111, 180, 110, 143, 114, 134, 88, 73, 198, 174, 52, 184, 78,
279 ])
280 .expect("A PublicKey from slice");
281
282 let t_addr = pub_key.to_address(NetworkKind::Mainnet);
283
284 assert_eq!(format!("{t_addr}"), "t1bmMa1wJDFdbc2TiURQP5BbBz6jHjUBuHq");
285 }
286
287 #[test]
288 fn pubkey_testnet() {
289 let _init_guard = zebra_test::init();
290
291 let pub_key = PublicKey::from_slice(&[
292 3, 23, 183, 225, 206, 31, 159, 148, 195, 42, 67, 115, 146, 41, 248, 140, 11, 3, 51, 41,
293 111, 180, 110, 143, 114, 134, 88, 73, 198, 174, 52, 184, 78,
294 ])
295 .expect("A PublicKey from slice");
296
297 let t_addr = pub_key.to_address(NetworkKind::Testnet);
298
299 assert_eq!(format!("{t_addr}"), "tmTc6trRhbv96kGfA99i7vrFwb5p7BVFwc3");
300 }
301
302 #[test]
303 fn empty_script_mainnet() {
304 let _init_guard = zebra_test::init();
305
306 let script = Script::new(&[0u8; 20]);
307
308 let t_addr = script.to_address(NetworkKind::Mainnet);
309
310 assert_eq!(format!("{t_addr}"), "t3Y5pHwfgHbS6pDjj1HLuMFxhFFip1fcJ6g");
311 }
312
313 #[test]
314 fn empty_script_testnet() {
315 let _init_guard = zebra_test::init();
316
317 let script = Script::new(&[0; 20]);
318
319 let t_addr = script.to_address(NetworkKind::Testnet);
320
321 assert_eq!(format!("{t_addr}"), "t2L51LcmpA43UMvKTw2Lwtt9LMjwyqU2V1P");
322 }
323
324 #[test]
325 fn from_string() {
326 let _init_guard = zebra_test::init();
327
328 let t_addr: Address = "t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd".parse().unwrap();
329
330 assert_eq!(format!("{t_addr}"), "t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd");
331 }
332
333 #[test]
334 fn debug() {
335 let _init_guard = zebra_test::init();
336
337 let t_addr: Address = "t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd".parse().unwrap();
338
339 assert_eq!(
340 format!("{t_addr:?}"),
341 "TransparentAddress { network_kind: Mainnet, script_hash: \"7d46a730d31f97b1930d3368a967c309bd4d136a\" }"
342 );
343 }
344}
345
346#[cfg(test)]
347proptest! {
348
349 #[test]
350 fn transparent_address_roundtrip(taddr in any::<Address>()) {
351 let _init_guard = zebra_test::init();
352
353 let mut data = Vec::new();
354
355 taddr.zcash_serialize(&mut data).expect("t-addr should serialize");
356
357 let taddr2 = Address::zcash_deserialize(&data[..]).expect("randomized t-addr should deserialize");
358
359 prop_assert_eq![taddr, taddr2];
360 }
361}