cardano_serialization_lib/legacy_address/
address.rs

1//! Address creation and parsing
2//!
3//! Address components are:
4//! * `HashedSpendingData` computed from `SpendingData`
5//! * `Attributes`
6//! * `ByronAddressType`
7//!
8//! All this components form an `ExtendedAddr`, which serialized
9//! to binary makes an `Addr`
10//!
11
12use crate::legacy_address::base58;
13use crate::legacy_address::cbor;
14use cbor_event::{self, cbor, de::Deserializer, se::Serializer};
15use cryptoxide::blake2b::Blake2b;
16use cryptoxide::digest::Digest;
17use cryptoxide::sha3;
18use ed25519_bip32::XPub;
19
20use std::{
21    convert::{TryFrom, TryInto},
22    fmt,
23    io::{BufRead, Write},
24};
25use crate::wasm_bindgen;
26
27#[wasm_bindgen]
28#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
29#[cfg_attr(feature = "generic-serialization", derive(Serialize, Deserialize))]
30pub enum ByronAddressType {
31    ATPubKey,
32    ATScript,
33    ATRedeem,
34}
35impl fmt::Display for ByronAddressType {
36    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37        match self {
38            ByronAddressType::ATPubKey => write!(f, "Public Key"),
39            ByronAddressType::ATScript => write!(f, "Script"),
40            ByronAddressType::ATRedeem => write!(f, "Redeem"),
41        }
42    }
43}
44// [TkListLen 1, TkInt (fromEnum t)]
45impl ByronAddressType {
46    fn from_u64(v: u64) -> Option<Self> {
47        match v {
48            0 => Some(ByronAddressType::ATPubKey),
49            1 => Some(ByronAddressType::ATScript),
50            2 => Some(ByronAddressType::ATRedeem),
51            _ => None,
52        }
53    }
54    fn to_byte(self) -> u8 {
55        match self {
56            ByronAddressType::ATPubKey => 0,
57            ByronAddressType::ATScript => 1,
58            ByronAddressType::ATRedeem => 2,
59        }
60    }
61}
62impl cbor_event::se::Serialize for ByronAddressType {
63    fn serialize<'se, W: Write>(
64        &self,
65        serializer: &'se mut Serializer<W>,
66    ) -> cbor_event::Result<&'se mut Serializer<W>> {
67        serializer.write_unsigned_integer(self.to_byte() as u64)
68    }
69}
70impl cbor_event::de::Deserialize for ByronAddressType {
71    fn deserialize<R: BufRead>(reader: &mut Deserializer<R>) -> cbor_event::Result<Self> {
72        match ByronAddressType::from_u64(reader.unsigned_integer()?) {
73            Some(addr_type) => Ok(addr_type),
74            None => Err(cbor_event::Error::CustomError(format!("Invalid ByronAddressType"))),
75        }
76    }
77}
78
79type HDAddressPayload = Vec<u8>;
80
81#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
82pub struct Attributes {
83    pub derivation_path: Option<HDAddressPayload>,
84    pub protocol_magic: Option<u32>,
85}
86impl Attributes {
87    pub fn new_bootstrap_era(hdap: Option<HDAddressPayload>, protocol_magic: Option<u32>) -> Self {
88        Attributes {
89            derivation_path: hdap,
90            protocol_magic,
91        }
92    }
93}
94
95const ATTRIBUTE_NAME_TAG_DERIVATION: u64 = 1;
96const ATTRIBUTE_NAME_TAG_PROTOCOL_MAGIC: u64 = 2;
97
98impl cbor_event::se::Serialize for Attributes {
99    fn serialize<'se, W: Write>(
100        &self,
101        serializer: &'se mut Serializer<W>,
102    ) -> cbor_event::Result<&'se mut Serializer<W>> {
103        let mut len = 0;
104        if let Some(_) = &self.derivation_path {
105            len += 1
106        };
107        if let Some(_) = &self.protocol_magic {
108            len += 1
109        };
110        let serializer = serializer.write_map(cbor_event::Len::Len(len))?;
111        let serializer = match &self.derivation_path {
112            &None => serializer,
113            &Some(ref dp) => serializer
114                .write_unsigned_integer(ATTRIBUTE_NAME_TAG_DERIVATION)?
115                .write_bytes(&dp)?,
116        };
117        let serializer = match &self.protocol_magic {
118            &None => serializer,
119            &Some(protocol_magic) => serializer
120                .write_unsigned_integer(ATTRIBUTE_NAME_TAG_PROTOCOL_MAGIC)?
121                .write_bytes(cbor!(&protocol_magic)?)?,
122        };
123        Ok(serializer)
124    }
125}
126impl cbor_event::de::Deserialize for Attributes {
127    fn deserialize<R: BufRead>(reader: &mut Deserializer<R>) -> cbor_event::Result<Self> {
128        let len = reader.map()?;
129        let mut len = match len {
130            cbor_event::Len::Indefinite => {
131                return Err(cbor_event::Error::CustomError(format!(
132                    "Invalid Attributes: received map of {:?} elements",
133                    len
134                )));
135            }
136            cbor_event::Len::Len(len) => len,
137        };
138        let mut derivation_path = None;
139        let mut protocol_magic = None;
140        while len > 0 {
141            let key = reader.unsigned_integer()?;
142            match key {
143                ATTRIBUTE_NAME_TAG_DERIVATION => derivation_path = Some(reader.bytes()?),
144                ATTRIBUTE_NAME_TAG_PROTOCOL_MAGIC => {
145                    // Yes, this is an integer encoded as CBOR encoded as Bytes in CBOR.
146                    let bytes = reader.bytes()?;
147                    let n = Deserializer::from(std::io::Cursor::new(bytes)).deserialize::<u32>()?;
148                    protocol_magic = Some(n);
149                }
150                _ => {
151                    return Err(cbor_event::Error::CustomError(format!(
152                        "invalid Attribute key {}",
153                        key
154                    )));
155                }
156            }
157            len -= 1;
158        }
159        Ok(Attributes {
160            derivation_path,
161            protocol_magic,
162        })
163    }
164}
165
166// calculate the hash of the data using SHA3 digest then using Blake2b224
167fn sha3_then_blake2b224(data: &[u8]) -> [u8; 28] {
168    let mut sh3 = sha3::Sha3_256::new();
169    let mut sh3_out = [0; 32];
170    sh3.input(data.as_ref());
171    sh3.result(&mut sh3_out);
172
173    let mut b2b = Blake2b::new(28);
174    let mut out = [0; 28];
175    b2b.input(&sh3_out[..]);
176    b2b.result(&mut out);
177    out
178}
179
180fn hash_spending_data(addr_type: ByronAddressType, xpub: &XPub, attrs: &Attributes) -> [u8; 28] {
181    let buf = cbor!(&(&addr_type, &SpendingData(xpub), attrs))
182        .expect("serialize the HashedSpendingData's digest data");
183    sha3_then_blake2b224(&buf)
184}
185
186/// A valid cardano Address that is displayed in base58
187#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
188pub struct Addr(Vec<u8>);
189
190#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
191pub enum AddressMatchXPub {
192    Yes,
193    No,
194}
195
196impl Addr {
197    pub fn deconstruct(&self) -> ExtendedAddr {
198        let mut raw = Deserializer::from(std::io::Cursor::new(&self.0));
199        cbor_event::de::Deserialize::deserialize(&mut raw).unwrap() // unwrap should never fail from addr to extended addr
200    }
201
202    /// Check if the Addr can be reconstructed with a specific xpub
203    pub fn identical_with_pubkey(&self, xpub: &XPub) -> AddressMatchXPub {
204        let ea = self.deconstruct();
205        let newea = ExtendedAddr::new(xpub, ea.attributes);
206        if self == &newea.to_address() {
207            AddressMatchXPub::Yes
208        } else {
209            AddressMatchXPub::No
210        }
211    }
212
213    /// mostly helper of the previous function, so not to have to expose the xpub construction
214    pub fn identical_with_pubkey_raw(&self, xpub: &[u8]) -> AddressMatchXPub {
215        match XPub::from_slice(xpub) {
216            Ok(xpub) => self.identical_with_pubkey(&xpub),
217            _ => AddressMatchXPub::No,
218        }
219    }
220}
221
222impl AsRef<[u8]> for Addr {
223    fn as_ref(&self) -> &[u8] {
224        self.0.as_ref()
225    }
226}
227
228impl TryFrom<&[u8]> for Addr {
229    type Error = cbor_event::Error;
230
231    fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
232        let mut v = Vec::new();
233        // TODO we only want validation of slice here, but we don't have api to do that yet.
234        {
235            let mut raw = Deserializer::from(std::io::Cursor::new(&slice));
236            let _: ExtendedAddr = cbor_event::de::Deserialize::deserialize(&mut raw)?;
237        }
238        v.extend_from_slice(slice);
239        Ok(Addr(v))
240    }
241}
242
243impl ::std::str::FromStr for Addr {
244    type Err = ParseExtendedAddrError;
245    fn from_str(s: &str) -> Result<Self, Self::Err> {
246        let bytes = base58::decode(s).map_err(ParseExtendedAddrError::Base58Error)?;
247        Self::try_from(&bytes[..]).map_err(ParseExtendedAddrError::EncodingError)
248    }
249}
250
251impl From<ExtendedAddr> for Addr {
252    fn from(ea: ExtendedAddr) -> Self {
253        ea.to_address()
254    }
255}
256
257impl fmt::Display for Addr {
258    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
259        write!(f, "{}", base58::encode(&self.0))
260    }
261}
262
263impl cbor_event::se::Serialize for Addr {
264    fn serialize<'se, W: Write>(
265        &self,
266        serializer: &'se mut Serializer<W>,
267    ) -> cbor_event::Result<&'se mut Serializer<W>> {
268        // Addr is already serialized
269        serializer.write_raw_bytes(&self.0)
270    }
271}
272impl cbor_event::de::Deserialize for Addr {
273    fn deserialize<R: BufRead>(reader: &mut Deserializer<R>) -> cbor_event::Result<Self> {
274        let ea: ExtendedAddr = cbor_event::de::Deserialize::deserialize(reader)?;
275        Ok(ea.to_address())
276    }
277}
278
279const EXTENDED_ADDR_LEN: usize = 28;
280
281/// A valid cardano address deconstructed
282#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
283pub struct ExtendedAddr {
284    pub addr: [u8; EXTENDED_ADDR_LEN],
285    pub attributes: Attributes,
286    pub addr_type: ByronAddressType,
287}
288impl ExtendedAddr {
289    pub fn new(xpub: &XPub, attrs: Attributes) -> Self {
290        ExtendedAddr {
291            addr: hash_spending_data(ByronAddressType::ATPubKey, xpub, &attrs),
292            attributes: attrs,
293            addr_type: ByronAddressType::ATPubKey,
294        }
295    }
296
297    // bootstrap era + no hdpayload address
298    pub fn new_simple(xpub: &XPub, protocol_magic: Option<u32>) -> Self {
299        ExtendedAddr::new(xpub, Attributes::new_bootstrap_era(None, protocol_magic))
300    }
301
302    pub fn to_address(&self) -> Addr {
303        Addr(cbor!(self).unwrap()) // unwrap should never fail from strongly typed extended addr to addr
304    }
305}
306#[derive(Debug)]
307pub enum ParseExtendedAddrError {
308    EncodingError(cbor_event::Error),
309    Base58Error(base58::Error),
310}
311
312impl fmt::Display for ParseExtendedAddrError {
313    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
314        use ParseExtendedAddrError::*;
315        match self {
316            EncodingError(_error) => f.write_str("encoding error"),
317            Base58Error(_error) => f.write_str("base58 error"),
318        }
319    }
320}
321
322impl std::error::Error for ParseExtendedAddrError {
323    fn source<'a>(&'a self) -> Option<&'a (dyn std::error::Error + 'static)> {
324        use ParseExtendedAddrError::*;
325        match self {
326            EncodingError(ref error) => Some(error),
327            Base58Error(ref error) => Some(error),
328        }
329    }
330}
331
332impl ::std::str::FromStr for ExtendedAddr {
333    type Err = ParseExtendedAddrError;
334    fn from_str(s: &str) -> Result<Self, Self::Err> {
335        let bytes = base58::decode(s).map_err(ParseExtendedAddrError::Base58Error)?;
336
337        Self::try_from(&bytes[..]).map_err(ParseExtendedAddrError::EncodingError)
338    }
339}
340impl TryFrom<&[u8]> for ExtendedAddr {
341    type Error = cbor_event::Error;
342
343    fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
344        let mut raw = Deserializer::from(std::io::Cursor::new(slice));
345        cbor_event::de::Deserialize::deserialize(&mut raw)
346    }
347}
348impl cbor_event::se::Serialize for ExtendedAddr {
349    fn serialize<'se, W: Write>(
350        &self,
351        serializer: &'se mut Serializer<W>,
352    ) -> cbor_event::Result<&'se mut Serializer<W>> {
353        let addr_bytes = cbor_event::Value::Bytes(self.addr.to_vec());
354        cbor::util::encode_with_crc32_(
355            &(&addr_bytes, &self.attributes, &self.addr_type),
356            serializer,
357        )?;
358        Ok(serializer)
359    }
360}
361impl cbor_event::de::Deserialize for ExtendedAddr {
362    fn deserialize<R: BufRead>(reader: &mut Deserializer<R>) -> cbor_event::Result<Self> {
363        let bytes = cbor::util::raw_with_crc32(reader)?;
364        let mut raw = Deserializer::from(std::io::Cursor::new(bytes));
365        raw.tuple(3, "ExtendedAddr")?;
366        let addr_bytes = raw.bytes()?;
367        let addr = addr_bytes.as_slice().try_into().map_err(|_| {
368            cbor_event::Error::WrongLen(
369                addr_bytes.len() as u64,
370                cbor_event::Len::Len(EXTENDED_ADDR_LEN as u64),
371                "invalid extended address length",
372            )
373        })?;
374        let attributes = cbor_event::de::Deserialize::deserialize(&mut raw)?;
375        let addr_type = cbor_event::de::Deserialize::deserialize(&mut raw)?;
376        Ok(ExtendedAddr {
377            addr,
378            addr_type,
379            attributes,
380        })
381    }
382}
383impl fmt::Display for ExtendedAddr {
384    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
385        write!(f, "{}", self.to_address())
386    }
387}
388
389const SPENDING_DATA_TAG_PUBKEY: u64 = 0;
390
391#[derive(Debug, PartialEq, Eq, Clone)]
392pub struct SpendingData<'a>(&'a XPub);
393
394impl<'a> cbor_event::se::Serialize for SpendingData<'a> {
395    fn serialize<'se, W: Write>(
396        &self,
397        serializer: &'se mut Serializer<W>,
398    ) -> cbor_event::Result<&'se mut Serializer<W>> {
399        let ar: [u8; 64] = self.0.clone().into();
400        serializer
401            .write_array(cbor_event::Len::Len(2))?
402            .write_unsigned_integer(SPENDING_DATA_TAG_PUBKEY)?
403            .write_bytes(&ar[..])
404    }
405}
406
407#[cfg(test)]
408mod tests {
409    use super::{Addr, AddressMatchXPub};
410    use ed25519_bip32::XPub;
411
412    fn assert_same_address(address: Addr, xpub: XPub) {
413        assert_eq!(
414            address.identical_with_pubkey(&xpub),
415            AddressMatchXPub::Yes,
416            "expected public key {} to match address {}",
417            xpub,
418            address,
419        )
420    }
421
422    #[test]
423    fn test_vector_1() {
424        let address     = "DdzFFzCqrhsrcTVhLygT24QwTnNqQqQ8mZrq5jykUzMveU26sxaH529kMpo7VhPrt5pwW3dXeB2k3EEvKcNBRmzCfcQ7dTkyGzTs658C".parse().unwrap();
425        let public_key = XPub::from_bytes([
426            0x6a, 0x50, 0x96, 0x89, 0xc6, 0x53, 0x17, 0x58, 0x65, 0x98, 0x5a, 0xd1, 0xe0, 0xeb,
427            0x5f, 0xf9, 0xad, 0xa6, 0x99, 0x7a, 0xa4, 0x03, 0xe6, 0x48, 0x61, 0x4b, 0x3b, 0x78,
428            0xfc, 0xba, 0x9c, 0x27, 0x30, 0x82, 0x28, 0xd9, 0x87, 0x2a, 0xf8, 0xb6, 0x5b, 0x98,
429            0x7f, 0xf2, 0x3e, 0x1a, 0x20, 0xcd, 0x90, 0xd8, 0x34, 0x6c, 0x31, 0xf0, 0xed, 0xb8,
430            0x99, 0x89, 0x52, 0xdc, 0x67, 0x66, 0x55, 0x80,
431        ]);
432        assert_same_address(address, public_key)
433    }
434
435    #[test]
436    fn test_vector_2() {
437        let address = "DdzFFzCqrht4it4GYgBp4J39FNnKBsPFejSppARXHCf2gGiTJcwXzpRvgDmxPvKQ8aZZmVqcLUz5L66a8Ja46pfKVtFRaKyn9eKdvpaC".parse().unwrap();
438        let public_key = XPub::from_bytes([
439            0xff, 0x7b, 0xf1, 0x29, 0x9d, 0xf3, 0xd7, 0x17, 0x98, 0xae, 0xfd, 0xc4, 0xae, 0xa7,
440            0xdb, 0x2f, 0x8d, 0xb7, 0x60, 0x46, 0x56, 0x94, 0x41, 0xea, 0xe5, 0x8b, 0x72, 0x23,
441            0xb6, 0x8b, 0x44, 0x04, 0x82, 0x15, 0xcb, 0xac, 0x94, 0xbc, 0xb7, 0xf2, 0xcf, 0x33,
442            0x6c, 0x6c, 0x18, 0xbc, 0x3e, 0x71, 0x3f, 0xfd, 0x82, 0x67, 0x59, 0x4f, 0xf6, 0x34,
443            0x93, 0x32, 0xce, 0x4f, 0x98, 0x04, 0xa7, 0xff,
444        ]);
445        assert_same_address(address, public_key)
446    }
447
448    #[test]
449    fn test_vector_3() {
450        let address = "DdzFFzCqrhsvNQtyViTvEdGxfdc5T1E5RorzFWjYodqjhFDy8fQxfDPccmTc4ePbvkiwvRkR8dtqQ1SHpH53fDSoxD17fo9f6WkRjjAA".parse().unwrap();
451        let public_key = XPub::from_bytes([
452            0x5c, 0x36, 0x51, 0xe0, 0xeb, 0x9d, 0x6d, 0xc9, 0x64, 0x07, 0x13, 0x7c, 0xcc, 0x1f,
453            0x37, 0x7a, 0x87, 0x94, 0x61, 0x77, 0xa5, 0x2c, 0xa3, 0x77, 0x2c, 0x6b, 0x4b, 0xeb,
454            0x72, 0x39, 0x50, 0xdc, 0x50, 0x22, 0x46, 0x68, 0x21, 0x8b, 0x8b, 0x36, 0x62, 0x02,
455            0xfe, 0x5b, 0x7d, 0x55, 0x6f, 0x50, 0x1c, 0x5c, 0x4e, 0x2d, 0x58, 0xe0, 0x54, 0x67,
456            0xe1, 0xab, 0xc0, 0x44, 0xc6, 0xc1, 0xbf, 0x8e,
457        ]);
458        assert_same_address(address, public_key)
459    }
460
461    #[test]
462    fn test_vector_4() {
463        let address = "DdzFFzCqrhsn7ZAhKy8mxkzW6G3wryM7K6bH38VAjE2FesJMxia3UviivMvGz146TP1FpDharxTE6nUgCCnZx2fmtKpmxAosg9Tf5b8y".parse().unwrap();
464        let public_key = XPub::from_bytes([
465            0xcd, 0x84, 0x2e, 0x01, 0x0d, 0x81, 0xa6, 0xbe, 0x1e, 0x16, 0x9f, 0xd6, 0x35, 0x21,
466            0xdb, 0xb9, 0x5f, 0x42, 0x41, 0xfc, 0x82, 0x3f, 0x45, 0xb1, 0xcf, 0x1a, 0x1c, 0xb4,
467            0xc5, 0x89, 0x57, 0x27, 0x1d, 0x4d, 0x14, 0x2a, 0x22, 0x94, 0xea, 0x5f, 0xa3, 0x16,
468            0xa4, 0xad, 0xbf, 0xcd, 0x59, 0x7a, 0x7c, 0x89, 0x6a, 0x52, 0xa9, 0xa3, 0xa9, 0xce,
469            0x49, 0x64, 0x4a, 0x10, 0x2d, 0x00, 0x71, 0x99,
470        ]);
471        assert_same_address(address, public_key)
472    }
473
474    #[test]
475    fn test_vector_5() {
476        let address = "DdzFFzCqrhssTCJf4sv664bdQURovAwzx1hNKkMkNLwMNyaxZFuPSDdZTTRMcoDyXHuCiZhbD4umvMJcWGkvFMMzBoBUW5UBdBbDqXGX".parse().unwrap();
477        let public_key = XPub::from_bytes([
478            0x5a, 0xac, 0x2d, 0xd0, 0xa8, 0xdc, 0x5d, 0x61, 0x0a, 0x4b, 0x6f, 0xdf, 0x3f, 0x5e,
479            0xf1, 0xb6, 0x4a, 0xcb, 0x76, 0xb1, 0xe8, 0x1f, 0x6a, 0x35, 0x70, 0x31, 0xfa, 0x19,
480            0xd5, 0xe6, 0x56, 0x9d, 0xcc, 0x37, 0xb7, 0xae, 0x6f, 0x39, 0x15, 0x82, 0xfb, 0x05,
481            0x4b, 0x72, 0xba, 0xda, 0x90, 0xab, 0x14, 0x6c, 0xdd, 0x01, 0x42, 0x0e, 0x4b, 0x40,
482            0x18, 0xf1, 0xa0, 0x55, 0x29, 0x82, 0xd2, 0x31,
483        ]);
484        assert_same_address(address, public_key)
485    }
486
487    #[test]
488    fn test_vector_6() {
489        let address = "DdzFFzCqrhsfi5fFjJUHYPSnfTYrnMohzh3PrrtrVQgwua33HWPKUdTJXo3o77pSGCmDNrjYaAiZmJddaPW9iHyUDatvU2WhX7MgnNMy".parse().unwrap();
490        let public_key = XPub::from_bytes([
491            0x2a, 0x6a, 0xd1, 0x51, 0x09, 0x96, 0xff, 0x2d, 0x10, 0x89, 0xcb, 0x8e, 0xd5, 0xf5,
492            0xc0, 0x61, 0xf6, 0xad, 0x0a, 0xfb, 0xb5, 0x3d, 0x95, 0x40, 0xa0, 0xfc, 0x89, 0xef,
493            0xc0, 0xa2, 0x63, 0xb9, 0x6d, 0xac, 0x00, 0xbd, 0x0d, 0x7b, 0xda, 0x7d, 0x16, 0x3a,
494            0x08, 0xdb, 0x20, 0xba, 0x64, 0xb6, 0x33, 0x4d, 0xca, 0x34, 0xea, 0xc8, 0x2c, 0xf7,
495            0xb4, 0x91, 0xc3, 0x5f, 0x5c, 0xae, 0xc7, 0xb0,
496        ]);
497        assert_same_address(address, public_key)
498    }
499
500    #[test]
501    fn test_vector_7() {
502        let address = "DdzFFzCqrhsy2zYMDQRCF4Nw34C3P7aT5B7JwHFQ6gLAeoHgVXurCLPCm3AeV1nTa1Nd46uDoNt16cnsPFkb4fpLi1J17AmvphCtGFz2".parse().unwrap();
503        let public_key = XPub::from_bytes([
504            0x0c, 0xd2, 0x15, 0x54, 0xa0, 0xf9, 0xb8, 0x25, 0x9c, 0x46, 0x88, 0xdd, 0x00, 0xfc,
505            0x01, 0x88, 0x43, 0x50, 0x79, 0x76, 0x4f, 0xa5, 0x50, 0xfb, 0x57, 0x38, 0x2b, 0xff,
506            0x43, 0xe2, 0xd8, 0xd8, 0x27, 0x27, 0x4e, 0x2a, 0x12, 0x9f, 0x86, 0xc3, 0x80, 0x88,
507            0x34, 0x37, 0x4d, 0xfe, 0x3f, 0xda, 0xa6, 0x28, 0x48, 0x30, 0xb8, 0xf6, 0xe4, 0x0d,
508            0x29, 0x93, 0xde, 0xa2, 0xfb, 0x0a, 0xbe, 0x82,
509        ]);
510        assert_same_address(address, public_key)
511    }
512
513    #[test]
514    fn test_vector_8() {
515        let address = "DdzFFzCqrht8ygB5pLM4uVbS2x4ek2NTDx6R3DJqP7fUaWEkx8RA9UFR8CHitp2R74XLDP876Pe3KLUByHnrWrKWnffpqPpm14rPCxeP".parse().unwrap();
516        let public_key = XPub::from_bytes([
517            0x1f, 0x0a, 0xb8, 0x33, 0xfd, 0xb1, 0xfa, 0x49, 0x58, 0xce, 0x74, 0x04, 0x81, 0x84,
518            0x5b, 0x3a, 0x26, 0x6e, 0xfa, 0xab, 0x2d, 0x65, 0xd1, 0x6b, 0xdd, 0x3d, 0xfe, 0x7f,
519            0xcb, 0xe4, 0x46, 0x30, 0x25, 0x9e, 0xd1, 0x91, 0x98, 0x93, 0x03, 0x9d, 0xfd, 0x40,
520            0x02, 0x4a, 0x72, 0x03, 0x45, 0x5b, 0x03, 0xd6, 0xd0, 0x0d, 0x0a, 0x5c, 0xd6, 0xee,
521            0x82, 0xde, 0x2e, 0xce, 0x73, 0x8a, 0xa1, 0xbf,
522        ]);
523        assert_same_address(address, public_key)
524    }
525
526    #[test]
527    fn test_vector_9() {
528        let address = "DdzFFzCqrhssTywqjv3dw3EakpEydWQcc3phQzR3YF9NPgQN9Ftkx68FfLLnpJ4vhWo9mAjx5EcpM1wNvorSySrpARZGfk5QugHkVs58".parse().unwrap();
529        let public_key = XPub::from_bytes([
530            0x16, 0xf7, 0xd2, 0x55, 0x32, 0x6d, 0x77, 0x6e, 0xc1, 0xb5, 0xed, 0xd2, 0x5f, 0x75,
531            0xd3, 0xe3, 0xeb, 0xe0, 0xb9, 0xd4, 0x9c, 0xdd, 0xb2, 0x46, 0xd8, 0x0c, 0xf4, 0x1b,
532            0x25, 0x24, 0x64, 0xb6, 0x24, 0x50, 0xa2, 0x4e, 0xf5, 0x98, 0x7b, 0x4b, 0xd6, 0x5e,
533            0x0d, 0x25, 0x23, 0x43, 0xab, 0xa8, 0xef, 0x77, 0x93, 0x34, 0x79, 0xde, 0xa8, 0xdd,
534            0xe2, 0x9e, 0xec, 0x56, 0xcc, 0x6a, 0xc0, 0x69,
535        ]);
536        assert_same_address(address, public_key)
537    }
538
539    #[test]
540    fn test_vector_10() {
541        let address = "DdzFFzCqrhsqTG4t3uq5UBqFrxhxGVM6bvF4q1QcZXqUpizFddEEip7dx5rbife2s9o2fRU3hVKhRp4higog7As8z42s4AMw6Pcu8vL4".parse().unwrap();
542        let public_key = XPub::from_bytes([
543            0x97, 0xb8, 0x6c, 0x69, 0xd1, 0x2a, 0xf1, 0x64, 0xdc, 0x87, 0xf2, 0x71, 0x26, 0x8f,
544            0x33, 0xbc, 0x4d, 0xee, 0xb0, 0xdf, 0xd3, 0x73, 0xc3, 0xfd, 0x3b, 0xac, 0xd4, 0x47,
545            0x53, 0xa3, 0x1d, 0xe7, 0x8f, 0x10, 0xe5, 0x55, 0x03, 0x7c, 0xd4, 0x00, 0x43, 0x6c,
546            0xcf, 0xd5, 0x38, 0x0d, 0xbb, 0xcd, 0x4d, 0x7c, 0x28, 0x0a, 0xef, 0x9e, 0xc7, 0x57,
547            0x4a, 0xe0, 0xac, 0xac, 0x0c, 0xf7, 0x9e, 0x89,
548        ]);
549        assert_same_address(address, public_key)
550    }
551}