1pub mod byron;
11pub mod varuint;
12
13use std::{fmt::Display, io::Cursor, str::FromStr};
14
15use pallas_crypto::hash::Hash;
16use thiserror::Error;
17
18#[derive(Error, Debug)]
19pub enum Error {
20 #[error("error converting from/to bech32 {0}")]
21 BadBech32(bech32::Error),
22
23 #[error("error decoding base58 value")]
24 BadBase58(base58::FromBase58Error),
25
26 #[error("error decoding hex value")]
27 BadHex,
28
29 #[error("unknown or bad string format for address {0}")]
30 UnknownStringFormat(String),
31
32 #[error("address header not found")]
33 MissingHeader,
34
35 #[error("address header is invalid {0:08b}")]
36 InvalidHeader(u8),
37
38 #[error("invalid operation for Byron address")]
39 InvalidForByron,
40
41 #[error("invalid operation for address content")]
42 InvalidForContent,
43
44 #[error("invalid CBOR for Byron address {0}")]
45 InvalidByronCbor(pallas_codec::minicbor::decode::Error),
46
47 #[error("unkown hrp for network {0:08b}")]
48 UnknownNetworkHrp(u8),
49
50 #[error("invalid hash size {0}")]
51 InvalidHashSize(usize),
52
53 #[error("invalid address length {0}")]
54 InvalidAddressLength(usize),
55
56 #[error("invalid pointer data")]
57 InvalidPointerData,
58
59 #[error("variable-length uint error: {0}")]
60 VarUintError(varuint::Error),
61}
62
63pub type PaymentKeyHash = Hash<28>;
64pub type StakeKeyHash = Hash<28>;
65pub type ScriptHash = Hash<28>;
66
67pub type Slot = u64;
68pub type TxIdx = u64;
69pub type CertIdx = u64;
70
71#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
73pub struct Pointer(Slot, TxIdx, CertIdx);
74
75fn slice_to_hash(slice: &[u8]) -> Result<Hash<28>, Error> {
76 if slice.len() == 28 {
77 let mut sized = [0u8; 28];
78 sized.copy_from_slice(slice);
79 Ok(sized.into())
80 } else {
81 Err(Error::InvalidHashSize(slice.len()))
82 }
83}
84
85impl Pointer {
86 pub fn new(slot: Slot, tx_idx: TxIdx, cert_idx: CertIdx) -> Self {
87 Pointer(slot, tx_idx, cert_idx)
88 }
89
90 pub fn parse(bytes: &[u8]) -> Result<Self, Error> {
91 let mut cursor = Cursor::new(bytes);
92 let a = varuint::read(&mut cursor).map_err(Error::VarUintError)?;
93 let b = varuint::read(&mut cursor).map_err(Error::VarUintError)?;
94 let c = varuint::read(&mut cursor).map_err(Error::VarUintError)?;
95
96 Ok(Pointer(a, b, c))
97 }
98
99 pub fn to_vec(&self) -> Vec<u8> {
100 let mut cursor = Cursor::new(vec![]);
101 varuint::write(&mut cursor, self.0);
102 varuint::write(&mut cursor, self.1);
103 varuint::write(&mut cursor, self.2);
104
105 cursor.into_inner()
106 }
107
108 pub fn slot(&self) -> u64 {
109 self.0
110 }
111
112 pub fn tx_idx(&self) -> u64 {
113 self.1
114 }
115
116 pub fn cert_idx(&self) -> u64 {
117 self.2
118 }
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
123pub enum ShelleyPaymentPart {
124 Key(PaymentKeyHash),
125 Script(ScriptHash),
126}
127
128impl ShelleyPaymentPart {
129 pub fn key_hash(hash: Hash<28>) -> Self {
130 Self::Key(hash)
131 }
132
133 pub fn script_hash(hash: Hash<28>) -> Self {
134 Self::Script(hash)
135 }
136
137 pub fn as_hash(&self) -> &Hash<28> {
139 match self {
140 Self::Key(x) => x,
141 Self::Script(x) => x,
142 }
143 }
144
145 pub fn to_vec(&self) -> Vec<u8> {
147 match self {
148 Self::Key(x) => x.to_vec(),
149 Self::Script(x) => x.to_vec(),
150 }
151 }
152
153 pub fn to_hex(&self) -> String {
154 let bytes = self.to_vec();
155 hex::encode(bytes)
156 }
157
158 pub fn to_bech32(&self) -> Result<String, Error> {
159 let hrp = match self {
160 Self::Key(_) => "addr_vkh",
161 Self::Script(_) => "addr_shared_vkh",
162 };
163 let bytes = self.to_vec();
164 encode_bech32(&bytes, hrp)
165 }
166
167 pub fn is_script(&self) -> bool {
169 matches!(self, Self::Script(_))
170 }
171}
172
173#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
175pub enum ShelleyDelegationPart {
176 Key(StakeKeyHash),
177 Script(ScriptHash),
178 Pointer(Pointer),
179 Null,
180}
181
182impl ShelleyDelegationPart {
183 pub fn key_hash(hash: Hash<28>) -> Self {
184 Self::Key(hash)
185 }
186
187 pub fn script_hash(hash: Hash<28>) -> Self {
188 Self::Script(hash)
189 }
190
191 pub fn from_pointer(bytes: &[u8]) -> Result<Self, Error> {
192 let pointer = Pointer::parse(bytes)?;
193 Ok(Self::Pointer(pointer))
194 }
195
196 pub fn as_hash(&self) -> Option<&Hash<28>> {
198 match self {
199 Self::Key(x) => Some(x),
200 Self::Script(x) => Some(x),
201 Self::Pointer(_) => None,
202 Self::Null => None,
203 }
204 }
205
206 pub fn to_vec(&self) -> Vec<u8> {
207 match self {
208 Self::Key(x) => x.to_vec(),
209 Self::Script(x) => x.to_vec(),
210 Self::Pointer(x) => x.to_vec(),
211 Self::Null => vec![],
212 }
213 }
214
215 pub fn to_hex(&self) -> String {
216 let bytes = self.to_vec();
217 hex::encode(bytes)
218 }
219
220 pub fn to_bech32(&self) -> Result<String, Error> {
221 let hrp = match self {
222 Self::Key(_) => "stake_vkh",
223 Self::Script(_) => "stake_shared_vkh",
224 _ => return Err(Error::InvalidForContent),
225 };
226
227 let bytes = self.to_vec();
228 encode_bech32(&bytes, hrp)
229 }
230
231 pub fn is_script(&self) -> bool {
232 matches!(self, ShelleyDelegationPart::Script(_))
233 }
234}
235
236impl StakePayload {
237 fn stake_key(bytes: &[u8]) -> Result<Self, Error> {
238 slice_to_hash(bytes).map(StakePayload::Stake)
239 }
240
241 fn script(bytes: &[u8]) -> Result<Self, Error> {
242 slice_to_hash(bytes).map(StakePayload::Script)
243 }
244
245 pub fn is_script(&self) -> bool {
246 matches!(self, StakePayload::Script(_))
247 }
248
249 pub fn as_hash(&self) -> &Hash<28> {
251 match self {
252 StakePayload::Stake(x) => x,
253 StakePayload::Script(x) => x,
254 }
255 }
256}
257
258#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Hash)]
260pub enum Network {
261 Testnet,
262 Mainnet,
263 Other(u8),
264}
265
266impl From<u8> for Network {
267 fn from(id: u8) -> Self {
268 match id {
269 0 => Network::Testnet,
270 1 => Network::Mainnet,
271 x => Network::Other(x),
272 }
273 }
274}
275
276#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
278pub struct ShelleyAddress(Network, ShelleyPaymentPart, ShelleyDelegationPart);
279
280#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
282pub enum StakePayload {
283 Stake(StakeKeyHash),
284 Script(ScriptHash),
285}
286
287#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
289pub struct StakeAddress(Network, StakePayload);
290
291pub use byron::ByronAddress;
292
293#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
295pub enum Address {
296 Byron(ByronAddress),
297 Shelley(ShelleyAddress),
298 Stake(StakeAddress),
299}
300
301fn encode_bech32(addr: &[u8], hrp: &str) -> Result<String, Error> {
302 let base32 = bech32::ToBase32::to_base32(&addr);
303 bech32::encode(hrp, base32, bech32::Variant::Bech32).map_err(Error::BadBech32)
304}
305
306fn decode_bech32(bech32: &str) -> Result<(String, Vec<u8>), Error> {
307 let (hrp, addr, _) = bech32::decode(bech32).map_err(Error::BadBech32)?;
308 let base10 = bech32::FromBase32::from_base32(&addr).map_err(Error::BadBech32)?;
309 Ok((hrp, base10))
310}
311
312fn parse_network(header: u8) -> Network {
313 let masked = header & 0b0000_1111;
314
315 match masked {
316 0b_0000_0000 => Network::Testnet,
317 0b_0000_0001 => Network::Mainnet,
318 _ => Network::Other(masked),
319 }
320}
321
322macro_rules! parse_shelley_fn {
323 ($name:tt, $payment:tt, pointer) => {
324 fn $name(header: u8, payload: &[u8]) -> Result<Address, Error> {
325 if payload.len() < 29 {
326 return Err(Error::InvalidAddressLength(payload.len()));
327 }
328
329 let net = parse_network(header);
330 let h1 = slice_to_hash(&payload[0..=27])?;
331 let p1 = ShelleyPaymentPart::$payment(h1);
332 let p2 = ShelleyDelegationPart::from_pointer(&payload[28..])?;
333 let addr = ShelleyAddress(net, p1, p2);
334
335 Ok(addr.into())
336 }
337 };
338 ($name:tt, $payment:tt, $delegation:tt) => {
339 fn $name(header: u8, payload: &[u8]) -> Result<Address, Error> {
340 if payload.len() < 56 {
341 return Err(Error::InvalidAddressLength(payload.len()));
342 }
343
344 let net = parse_network(header);
345 let h1 = slice_to_hash(&payload[0..=27])?;
346 let p1 = ShelleyPaymentPart::$payment(h1);
347 let h2 = slice_to_hash(&payload[28..=55])?;
348 let p2 = ShelleyDelegationPart::$delegation(h2);
349 let addr = ShelleyAddress(net, p1, p2);
350
351 Ok(addr.into())
352 }
353 };
354 ($name:tt, $payment:tt) => {
355 fn $name(header: u8, payload: &[u8]) -> Result<Address, Error> {
356 if payload.len() < 28 {
357 return Err(Error::InvalidAddressLength(payload.len()));
358 }
359
360 let net = parse_network(header);
361 let h1 = slice_to_hash(&payload[0..=27])?;
362 let p1 = ShelleyPaymentPart::$payment(h1);
363 let addr = ShelleyAddress(net, p1, ShelleyDelegationPart::Null);
364
365 Ok(addr.into())
366 }
367 };
368}
369
370macro_rules! parse_stake_fn {
371 ($name:tt, $type:tt) => {
372 fn $name(header: u8, payload: &[u8]) -> Result<Address, Error> {
373 if payload.len() < 28 {
374 return Err(Error::InvalidAddressLength(payload.len()));
375 }
376
377 let net = parse_network(header);
378 let p1 = StakePayload::$type(&payload[0..=27])?;
379 let addr = StakeAddress(net, p1);
380
381 Ok(addr.into())
382 }
383 };
384}
385
386parse_shelley_fn!(parse_type_0, key_hash, key_hash);
388parse_shelley_fn!(parse_type_1, script_hash, key_hash);
389parse_shelley_fn!(parse_type_2, key_hash, script_hash);
390parse_shelley_fn!(parse_type_3, script_hash, script_hash);
391parse_shelley_fn!(parse_type_4, key_hash, pointer);
392parse_shelley_fn!(parse_type_5, script_hash, pointer);
393parse_shelley_fn!(parse_type_6, key_hash);
394parse_shelley_fn!(parse_type_7, script_hash);
395
396fn parse_type_8(header: u8, payload: &[u8]) -> Result<Address, Error> {
398 let vec = [&[header], payload].concat();
399 let inner = pallas_codec::minicbor::decode(&vec).map_err(Error::InvalidByronCbor)?;
400 Ok(Address::Byron(inner))
401}
402
403parse_stake_fn!(parse_type_14, stake_key);
405parse_stake_fn!(parse_type_15, script);
406
407fn bytes_to_address(bytes: &[u8]) -> Result<Address, Error> {
408 let header = *bytes.first().ok_or(Error::MissingHeader)?;
409 let payload = &bytes[1..];
410
411 match header & 0b1111_0000 {
412 0b0000_0000 => parse_type_0(header, payload),
413 0b0001_0000 => parse_type_1(header, payload),
414 0b0010_0000 => parse_type_2(header, payload),
415 0b0011_0000 => parse_type_3(header, payload),
416 0b0100_0000 => parse_type_4(header, payload),
417 0b0101_0000 => parse_type_5(header, payload),
418 0b0110_0000 => parse_type_6(header, payload),
419 0b0111_0000 => parse_type_7(header, payload),
420 0b1000_0000 => parse_type_8(header, payload),
421 0b1110_0000 => parse_type_14(header, payload),
422 0b1111_0000 => parse_type_15(header, payload),
423 _ => Err(Error::InvalidHeader(header)),
424 }
425}
426
427fn bech32_to_address(bech32: &str) -> Result<Address, Error> {
428 let (_, bytes) = decode_bech32(bech32)?;
429 bytes_to_address(&bytes)
430}
431
432impl Network {
433 pub fn is_mainnet(&self) -> bool {
434 matches!(self, Network::Mainnet)
435 }
436
437 pub fn value(&self) -> u8 {
438 match self {
439 Network::Testnet => 0,
440 Network::Mainnet => 1,
441 Network::Other(x) => *x,
442 }
443 }
444}
445
446impl ShelleyAddress {
447 pub fn new(
448 network: Network,
449 payment: ShelleyPaymentPart,
450 delegation: ShelleyDelegationPart,
451 ) -> Self {
452 Self(network, payment, delegation)
453 }
454
455 pub fn network(&self) -> Network {
457 self.0
458 }
459
460 pub fn typeid(&self) -> u8 {
462 match (&self.1, &self.2) {
463 (ShelleyPaymentPart::Key(_), ShelleyDelegationPart::Key(_)) => 0b0000,
464 (ShelleyPaymentPart::Script(_), ShelleyDelegationPart::Key(_)) => 0b0001,
465 (ShelleyPaymentPart::Key(_), ShelleyDelegationPart::Script(_)) => 0b0010,
466 (ShelleyPaymentPart::Script(_), ShelleyDelegationPart::Script(_)) => 0b0011,
467 (ShelleyPaymentPart::Key(_), ShelleyDelegationPart::Pointer(_)) => 0b0100,
468 (ShelleyPaymentPart::Script(_), ShelleyDelegationPart::Pointer(_)) => 0b0101,
469 (ShelleyPaymentPart::Key(_), ShelleyDelegationPart::Null) => 0b0110,
470 (ShelleyPaymentPart::Script(_), ShelleyDelegationPart::Null) => 0b0111,
471 }
472 }
473
474 pub fn to_header(&self) -> u8 {
475 let type_id = self.typeid();
476 let type_id = type_id << 4;
477 let network = self.0.value();
478
479 type_id | network
480 }
481
482 pub fn payment(&self) -> &ShelleyPaymentPart {
483 &self.1
484 }
485
486 pub fn delegation(&self) -> &ShelleyDelegationPart {
487 &self.2
488 }
489
490 pub fn hrp(&self) -> Result<&'static str, Error> {
492 match &self.0 {
493 Network::Testnet => Ok("addr_test"),
494 Network::Mainnet => Ok("addr"),
495 Network::Other(x) => Err(Error::UnknownNetworkHrp(*x)),
496 }
497 }
498
499 pub fn to_vec(&self) -> Vec<u8> {
500 let header = self.to_header();
501 let payment = self.1.to_vec();
502 let delegation = self.2.to_vec();
503
504 [&[header], payment.as_slice(), delegation.as_slice()].concat()
505 }
506
507 pub fn to_hex(&self) -> String {
508 let bytes = self.to_vec();
509 hex::encode(bytes)
510 }
511
512 pub fn to_bech32(&self) -> Result<String, Error> {
513 let hrp = self.hrp()?;
514 let bytes = self.to_vec();
515 encode_bech32(&bytes, hrp)
516 }
517
518 pub fn has_script(&self) -> bool {
520 self.payment().is_script() || self.delegation().is_script()
521 }
522}
523
524impl TryFrom<ShelleyAddress> for StakeAddress {
525 type Error = Error;
526
527 fn try_from(value: ShelleyAddress) -> Result<Self, Self::Error> {
528 let payload = match value.delegation() {
529 ShelleyDelegationPart::Key(h) => StakePayload::Stake(*h),
530 ShelleyDelegationPart::Script(h) => StakePayload::Script(*h),
531 _ => return Err(Error::InvalidForContent),
532 };
533
534 Ok(StakeAddress(value.network(), payload))
535 }
536}
537
538impl AsRef<[u8]> for StakePayload {
539 fn as_ref(&self) -> &[u8] {
540 match self {
541 Self::Stake(x) => x.as_ref(),
542 Self::Script(x) => x.as_ref(),
543 }
544 }
545}
546
547impl StakeAddress {
548 pub fn network(&self) -> Network {
550 self.0
551 }
552
553 pub fn typeid(&self) -> u8 {
555 match &self.1 {
556 StakePayload::Stake(_) => 0b1110,
557 StakePayload::Script(_) => 0b1111,
558 }
559 }
560
561 pub fn to_header(&self) -> u8 {
563 let type_id = self.typeid();
564 let type_id = type_id << 4;
565 let network = self.0.value();
566
567 type_id | network
568 }
569
570 pub fn payload(&self) -> &StakePayload {
572 &self.1
573 }
574
575 pub fn hrp(&self) -> Result<&'static str, Error> {
577 match &self.0 {
578 Network::Testnet => Ok("stake_test"),
579 Network::Mainnet => Ok("stake"),
580 Network::Other(x) => Err(Error::UnknownNetworkHrp(*x)),
581 }
582 }
583
584 pub fn to_vec(&self) -> Vec<u8> {
585 let header = self.to_header();
586
587 [&[header], self.1.as_ref()].concat()
588 }
589
590 pub fn to_hex(&self) -> String {
591 let bytes = self.to_vec();
592 hex::encode(bytes)
593 }
594
595 pub fn to_bech32(&self) -> Result<String, Error> {
596 let hrp = self.hrp()?;
597 let bytes = self.to_vec();
598 encode_bech32(&bytes, hrp)
599 }
600
601 pub fn is_script(&self) -> bool {
602 self.payload().is_script()
603 }
604}
605
606impl Address {
607 pub fn to_bech32(&self) -> Result<String, Error> {
609 match self {
610 Address::Byron(_) => Err(Error::InvalidForByron),
611 Address::Shelley(x) => x.to_bech32(),
612 Address::Stake(x) => x.to_bech32(),
613 }
614 }
615
616 pub fn from_bech32(bech32: &str) -> Result<Self, Error> {
618 bech32_to_address(bech32)
619 }
620
621 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
623 bytes_to_address(bytes)
624 }
625
626 pub fn from_hex(bytes: &str) -> Result<Self, Error> {
628 let bytes = hex::decode(bytes).map_err(|_| Error::BadHex)?;
629 bytes_to_address(&bytes)
630 }
631
632 pub fn network(&self) -> Option<Network> {
634 match self {
635 Address::Byron(_) => None,
636 Address::Shelley(x) => Some(x.network()),
637 Address::Stake(x) => Some(x.network()),
638 }
639 }
640
641 pub fn typeid(&self) -> u8 {
643 match self {
644 Address::Byron(x) => x.typeid(),
645 Address::Shelley(x) => x.typeid(),
646 Address::Stake(x) => x.typeid(),
647 }
648 }
649
650 pub fn hrp(&self) -> Result<&'static str, Error> {
652 match self {
653 Address::Byron(_) => Err(Error::InvalidForByron),
654 Address::Shelley(x) => x.hrp(),
655 Address::Stake(x) => x.hrp(),
656 }
657 }
658
659 pub fn has_script(&self) -> bool {
661 match self {
662 Address::Byron(_) => false,
663 Address::Shelley(x) => x.has_script(),
664 Address::Stake(x) => x.is_script(),
665 }
666 }
667
668 pub fn is_enterprise(&self) -> bool {
670 match self {
671 Address::Shelley(x) => matches!(x.delegation(), ShelleyDelegationPart::Null),
672 _ => false,
673 }
674 }
675
676 pub fn to_vec(&self) -> Vec<u8> {
677 match self {
678 Address::Byron(x) => x.to_vec(),
679 Address::Shelley(x) => x.to_vec(),
680 Address::Stake(x) => x.to_vec(),
681 }
682 }
683
684 pub fn to_hex(&self) -> String {
685 match self {
686 Address::Byron(x) => x.to_hex(),
687 Address::Shelley(x) => x.to_hex(),
688 Address::Stake(x) => x.to_hex(),
689 }
690 }
691}
692
693impl Display for Address {
694 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
695 match self {
696 Address::Byron(x) => f.write_str(&x.to_base58()),
697 Address::Shelley(x) => f.write_str(&x.to_bech32().unwrap_or_else(|_| x.to_hex())),
698 Address::Stake(x) => f.write_str(&x.to_bech32().unwrap_or_else(|_| x.to_hex())),
699 }
700 }
701}
702
703impl FromStr for Address {
704 type Err = Error;
705
706 fn from_str(s: &str) -> Result<Self, Self::Err> {
707 if let Ok(x) = Address::from_bech32(s) {
708 return Ok(x);
709 }
710
711 if let Ok(x) = ByronAddress::from_base58(s) {
712 return Ok(x.into());
713 }
714
715 if let Ok(x) = Address::from_hex(s) {
716 return Ok(x);
717 }
718
719 Err(Error::UnknownStringFormat(s.to_owned()))
720 }
721}
722
723impl TryFrom<&[u8]> for Address {
724 type Error = Error;
725
726 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
727 bytes_to_address(value)
728 }
729}
730
731impl From<ByronAddress> for Address {
732 fn from(addr: ByronAddress) -> Self {
733 Address::Byron(addr)
734 }
735}
736
737impl From<ShelleyAddress> for Address {
738 fn from(addr: ShelleyAddress) -> Self {
739 Address::Shelley(addr)
740 }
741}
742
743impl From<StakeAddress> for Address {
744 fn from(addr: StakeAddress) -> Self {
745 Address::Stake(addr)
746 }
747}
748
749#[cfg(test)]
750mod tests {
751 use std::str::FromStr;
752
753 use super::*;
754
755 const MAINNET_TEST_VECTORS: &[(&str, u8)] = &[
756 ("addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgse35a3x", 0u8),
757 ("addr1z8phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gten0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgs9yc0hh", 1u8),
758 ("addr1yx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzerkr0vd4msrxnuwnccdxlhdjar77j6lg0wypcc9uar5d2shs2z78ve", 2u8),
759 ("addr1x8phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gt7r0vd4msrxnuwnccdxlhdjar77j6lg0wypcc9uar5d2shskhj42g", 3u8),
760 ("addr1gx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer5pnz75xxcrzqf96k", 4u8),
761 ("addr128phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gtupnz75xxcrtw79hu", 5u8),
762 ("addr1vx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzers66hrl8", 6u8),
763 ("addr1w8phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gtcyjy7wx", 7u8),
764 ("stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw", 14u8),
765 ("stake178phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gtcccycj5", 15u8),
766 ("37btjrVyb4KDXBNC4haBVPCrro8AQPHwvCMp3RFhhSVWwfFmZ6wwzSK6JK1hY6wHNmtrpTf1kdbva8TCneM2YsiXT7mrzT21EacHnPpz5YyUdj64na", 8u8),
767 ];
768
769 const PAYMENT_PUBLIC_KEY: &str =
770 "addr_vk1w0l2sr2zgfm26ztc6nl9xy8ghsk5sh6ldwemlpmp9xylzy4dtf7st80zhd";
771 const STAKE_PUBLIC_KEY: &str =
772 "stake_vk1px4j0r2fk7ux5p23shz8f3y5y2qam7s954rgf3lg5merqcj6aetsft99wu";
773 const SCRIPT_HASH: &str = "script1cda3khwqv60360rp5m7akt50m6ttapacs8rqhn5w342z7r35m37";
774
775 fn hash_vector_key(key: &str) -> Hash<28> {
776 let (_, x) = decode_bech32(key).unwrap();
777 pallas_crypto::hash::Hasher::<224>::hash(&x)
778 }
779
780 #[test]
781 fn roundtrip_bech32() {
782 for vector in MAINNET_TEST_VECTORS {
783 let original = vector.0;
784 match Address::from_str(original) {
785 Ok(Address::Byron(_)) => (),
786 Ok(addr) => {
787 let ours = addr.to_bech32().unwrap();
788 assert_eq!(original, ours);
789 }
790 _ => panic!("should be able to decode from bech32"),
791 }
792 }
793 }
794
795 #[test]
796 fn roundtrip_string() {
797 for vector in MAINNET_TEST_VECTORS {
798 let original = vector.0;
799 let addr = Address::from_str(original).unwrap();
800 let ours = addr.to_string();
801 assert_eq!(original, ours);
802 }
803 }
804
805 #[test]
806 fn typeid_matches() {
807 for vector in MAINNET_TEST_VECTORS {
808 let original = vector.0;
809 let addr = Address::from_str(original).unwrap();
810 assert_eq!(addr.typeid(), vector.1);
811 }
812 }
813
814 #[test]
815 fn network_matches() {
816 for vector in MAINNET_TEST_VECTORS {
817 let original = vector.0;
818 let addr = Address::from_str(original).unwrap();
819
820 match addr {
821 Address::Byron(_) => assert!(addr.network().is_none()),
822 _ => assert!(matches!(addr.network(), Some(Network::Mainnet))),
823 }
824 }
825 }
826
827 #[test]
828 fn payload_matches() {
829 for vector in MAINNET_TEST_VECTORS {
830 let original = vector.0;
831 let addr = Address::from_str(original).unwrap();
832
833 match addr {
834 Address::Shelley(x) => {
835 match x.payment() {
836 ShelleyPaymentPart::Key(hash) => {
837 let expected = &hash_vector_key(PAYMENT_PUBLIC_KEY);
838 assert_eq!(hash, expected);
839 }
840 ShelleyPaymentPart::Script(hash) => {
841 let (_, expected) = &decode_bech32(SCRIPT_HASH).unwrap();
842 let expected = &Hash::<28>::from_str(&hex::encode(expected)).unwrap();
843 assert_eq!(hash, expected);
844 }
845 };
846
847 match x.delegation() {
848 ShelleyDelegationPart::Key(hash) => {
849 let expected = &hash_vector_key(STAKE_PUBLIC_KEY);
850 assert_eq!(hash, expected);
851 }
852 ShelleyDelegationPart::Script(hash) => {
853 let (_, expected) = &decode_bech32(SCRIPT_HASH).unwrap();
854 let expected = &Hash::<28>::from_str(&hex::encode(expected)).unwrap();
855 assert_eq!(hash, expected);
856 }
857 ShelleyDelegationPart::Pointer(ptr) => {
858 assert_eq!(ptr.slot(), 2498243);
859 assert_eq!(ptr.tx_idx(), 27);
860 assert_eq!(ptr.cert_idx(), 3);
861 }
862 _ => (),
863 };
864 }
865 Address::Stake(x) => match x.payload() {
866 StakePayload::Stake(hash) => {
867 let expected = &hash_vector_key(STAKE_PUBLIC_KEY);
868 assert_eq!(hash, expected);
869 }
870 StakePayload::Script(hash) => {
871 let (_, expected) = &decode_bech32(SCRIPT_HASH).unwrap();
872 let expected = &Hash::<28>::from_str(&hex::encode(expected)).unwrap();
873 assert_eq!(hash, expected);
874 }
875 },
876 Address::Byron(_) => {
877 }
879 };
880 }
881 }
882
883 #[test]
884 fn construct_from_parts() {
885 let payment_hash = hash_vector_key(PAYMENT_PUBLIC_KEY);
886 let delegation_hash = hash_vector_key(STAKE_PUBLIC_KEY);
887
888 let addr: Address = ShelleyAddress::new(
889 Network::Mainnet,
890 ShelleyPaymentPart::key_hash(payment_hash),
891 ShelleyDelegationPart::key_hash(delegation_hash),
892 )
893 .into();
894
895 assert_eq!(addr.to_bech32().unwrap(), MAINNET_TEST_VECTORS[0].0);
896 }
897
898 #[test]
899 fn test_minted_invalid_pointed_address() {
900 let addr = Address::from_hex("40C19D7D05E90EEB6394B53313FE79D47077DE33068C6B813BBE5C9D5681FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F81FFFFFFFFFFFFFFFF7F81FFFFFFFFFFFFFFFF7F");
901 assert!(matches!(addr, Ok(Address::Shelley(_))));
902 }
903
904 #[test]
905 fn test_shelley_into_stake() {
906 let addr = Address::from_bech32("addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgse35a3x").unwrap();
907
908 match addr {
909 Address::Shelley(shelley_addr) => {
910 let stake_addr: StakeAddress = shelley_addr.clone().try_into().unwrap();
911 assert_eq!(stake_addr.network(), shelley_addr.network());
912
913 let stake_hash = stake_addr.payload().as_hash();
914 let shelley_hash = shelley_addr.delegation().as_hash().unwrap();
915 assert_eq!(stake_hash, shelley_hash);
916 }
917 _ => panic!(),
918 }
919 }
920
921 #[test]
922 fn test_minted_extra_bytes_base_address() {
923 let addr = Address::from_hex("015bad085057ac10ecc7060f7ac41edd6f63068d8963ef7d86ca58669e5ecf2d283418a60be5a848a2380eb721000da1e0bbf39733134beca4cb57afb0b35fc89c63061c9914e055001a518c7516");
924 assert!(matches!(addr, Ok(Address::Shelley(_))));
925 }
926}