44use crate :: cip19:: { VarIntDecoder , VarIntEncoder } ;
55use crate :: types:: { KeyHash , ScriptHash } ;
66use anyhow:: { anyhow, bail, Result } ;
7+ use crc:: { Crc , CRC_32_ISO_HDLC } ;
8+ use minicbor:: data:: IanaTag ;
79use serde_with:: { hex:: Hex , serde_as} ;
810
911/// a Byron-era address
10- #[ derive( Debug , Clone , PartialEq , Eq , serde:: Serialize , serde:: Deserialize ) ]
12+ #[ derive( Debug , Clone , PartialEq , Eq , Hash , serde:: Serialize , serde:: Deserialize ) ]
1113pub struct ByronAddress {
1214 /// Raw payload
1315 pub payload : Vec < u8 > ,
1416}
1517
18+ impl ByronAddress {
19+ fn compute_crc32 ( & self ) -> u32 {
20+ const CRC32 : Crc < u32 > = Crc :: < u32 > :: new ( & CRC_32_ISO_HDLC ) ;
21+ CRC32 . checksum ( & self . payload )
22+ }
23+
24+ pub fn to_string ( & self ) -> Result < String > {
25+ let crc = self . compute_crc32 ( ) ;
26+
27+ let mut buf = Vec :: new ( ) ;
28+ {
29+ let mut enc = minicbor:: Encoder :: new ( & mut buf) ;
30+ enc. array ( 2 ) ?;
31+ enc. tag ( IanaTag :: Cbor ) ?;
32+ enc. bytes ( & self . payload ) ?;
33+ enc. u32 ( crc) ?;
34+ }
35+
36+ Ok ( bs58:: encode ( buf) . into_string ( ) )
37+ }
38+
39+ pub fn from_string ( s : & str ) -> Result < Self > {
40+ let bytes = bs58:: decode ( s) . into_vec ( ) ?;
41+ let mut dec = minicbor:: Decoder :: new ( & bytes) ;
42+
43+ let len = dec. array ( ) ?. unwrap_or ( 0 ) ;
44+ if len != 2 {
45+ anyhow:: bail!( "Invalid Byron address CBOR array length" ) ;
46+ }
47+
48+ let tag = dec. tag ( ) ?;
49+ if tag != IanaTag :: Cbor . into ( ) {
50+ anyhow:: bail!( "Invalid Byron address CBOR tag, expected 24" ) ;
51+ }
52+
53+ let payload = dec. bytes ( ) ?. to_vec ( ) ;
54+ let crc = dec. u32 ( ) ?;
55+
56+ let address = ByronAddress { payload } ;
57+ let computed = address. compute_crc32 ( ) ;
58+
59+ if crc != computed {
60+ anyhow:: bail!( "Byron address CRC mismatch" ) ;
61+ }
62+
63+ Ok ( address)
64+ }
65+
66+ pub fn to_bytes_key ( & self ) -> Result < Vec < u8 > > {
67+ let crc = self . compute_crc32 ( ) ;
68+
69+ let mut buf = Vec :: new ( ) ;
70+ {
71+ let mut enc = minicbor:: Encoder :: new ( & mut buf) ;
72+ enc. array ( 2 ) ?;
73+ enc. tag ( minicbor:: data:: IanaTag :: Cbor ) ?;
74+ enc. bytes ( & self . payload ) ?;
75+ enc. u32 ( crc) ?;
76+ }
77+
78+ Ok ( buf)
79+ }
80+ }
81+
1682/// Address network identifier
1783#[ derive( Debug , Clone , PartialEq , Eq , Hash , serde:: Serialize , serde:: Deserialize ) ]
1884pub enum AddressNetwork {
@@ -170,11 +236,85 @@ impl ShelleyAddress {
170236 data. extend ( delegation_hash) ;
171237 Ok ( bech32:: encode :: < bech32:: Bech32 > ( hrp, & data) ?)
172238 }
239+
240+ pub fn to_bytes_key ( & self ) -> Result < Vec < u8 > > {
241+ let network_bits = match self . network {
242+ AddressNetwork :: Main => 1u8 ,
243+ AddressNetwork :: Test => 0u8 ,
244+ } ;
245+
246+ let ( payment_hash, payment_bits) : ( & Vec < u8 > , u8 ) = match & self . payment {
247+ ShelleyAddressPaymentPart :: PaymentKeyHash ( data) => ( data, 0 ) ,
248+ ShelleyAddressPaymentPart :: ScriptHash ( data) => ( data, 1 ) ,
249+ } ;
250+
251+ let mut data = Vec :: new ( ) ;
252+
253+ match & self . delegation {
254+ ShelleyAddressDelegationPart :: None => {
255+ let header = network_bits | ( payment_bits << 4 ) | ( 3 << 5 ) ;
256+ data. push ( header) ;
257+ data. extend ( payment_hash) ;
258+ }
259+ ShelleyAddressDelegationPart :: StakeKeyHash ( hash) => {
260+ let header = network_bits | ( payment_bits << 4 ) | ( 0 << 5 ) ;
261+ data. push ( header) ;
262+ data. extend ( payment_hash) ;
263+ data. extend ( hash) ;
264+ }
265+ ShelleyAddressDelegationPart :: ScriptHash ( hash) => {
266+ let header = network_bits | ( payment_bits << 4 ) | ( 1 << 5 ) ;
267+ data. push ( header) ;
268+ data. extend ( payment_hash) ;
269+ data. extend ( hash) ;
270+ }
271+ ShelleyAddressDelegationPart :: Pointer ( pointer) => {
272+ let header = network_bits | ( payment_bits << 4 ) | ( 2 << 5 ) ;
273+ data. push ( header) ;
274+ data. extend ( payment_hash) ;
275+
276+ let mut encoder = VarIntEncoder :: new ( ) ;
277+ encoder. push ( pointer. slot ) ;
278+ encoder. push ( pointer. tx_index ) ;
279+ encoder. push ( pointer. cert_index ) ;
280+ data. extend ( encoder. to_vec ( ) ) ;
281+ }
282+ }
283+
284+ Ok ( data)
285+ }
286+
287+ pub fn stake_address_string ( & self ) -> Result < Option < String > > {
288+ let network_bit = match self . network {
289+ AddressNetwork :: Main => 1 ,
290+ AddressNetwork :: Test => 0 ,
291+ } ;
292+
293+ match & self . delegation {
294+ ShelleyAddressDelegationPart :: StakeKeyHash ( key_hash) => {
295+ let mut data = Vec :: with_capacity ( 29 ) ;
296+ data. push ( network_bit | ( 0b1110 << 4 ) ) ;
297+ data. extend_from_slice ( key_hash) ;
298+ let stake = StakeAddress :: from_binary ( & data) ?. to_string ( ) ?;
299+ Ok ( Some ( stake) )
300+ }
301+ ShelleyAddressDelegationPart :: ScriptHash ( script_hash) => {
302+ let mut data = Vec :: with_capacity ( 29 ) ;
303+ data. push ( network_bit | ( 0b1111 << 4 ) ) ;
304+ data. extend_from_slice ( script_hash) ;
305+ let stake = StakeAddress :: from_binary ( & data) ?. to_string ( ) ?;
306+ Ok ( Some ( stake) )
307+ }
308+ // TODO: Use chain store to resolve pointer delegation addresses
309+ ShelleyAddressDelegationPart :: Pointer ( _pointer) => Ok ( None ) ,
310+ ShelleyAddressDelegationPart :: None => Ok ( None ) ,
311+ }
312+ }
173313}
174314
175315/// Payload of a stake address
176316#[ serde_as]
177- #[ derive( Debug , Clone , PartialEq , Eq , serde:: Serialize , serde:: Deserialize ) ]
317+ #[ derive( Debug , Clone , PartialEq , Eq , Hash , serde:: Serialize , serde:: Deserialize ) ]
178318pub enum StakeAddressPayload {
179319 /// Stake key
180320 StakeKeyHash ( #[ serde_as( as = "Hex" ) ] Vec < u8 > ) ,
@@ -196,7 +336,7 @@ impl StakeAddressPayload {
196336}
197337
198338/// A stake address
199- #[ derive( Debug , Clone , PartialEq , Eq , serde:: Serialize , serde:: Deserialize ) ]
339+ #[ derive( Debug , Clone , PartialEq , Eq , Hash , serde:: Serialize , serde:: Deserialize ) ]
200340pub struct StakeAddress {
201341 /// Network id
202342 pub network : AddressNetwork ,
@@ -271,10 +411,28 @@ impl StakeAddress {
271411 data. extend ( stake_hash) ;
272412 Ok ( bech32:: encode :: < bech32:: Bech32 > ( hrp, & data) ?)
273413 }
414+
415+ pub fn to_bytes_key ( & self ) -> Result < Vec < u8 > > {
416+ let mut out = Vec :: new ( ) ;
417+ let ( bits, hash) : ( u8 , & [ u8 ] ) = match & self . payload {
418+ StakeAddressPayload :: StakeKeyHash ( h) => ( 0b1110 , h) ,
419+ StakeAddressPayload :: ScriptHash ( h) => ( 0b1111 , h) ,
420+ } ;
421+
422+ let net_bit = match self . network {
423+ AddressNetwork :: Main => 1 ,
424+ AddressNetwork :: Test => 0 ,
425+ } ;
426+
427+ let header = net_bit | ( bits << 4 ) ;
428+ out. push ( header) ;
429+ out. extend_from_slice ( hash) ;
430+ Ok ( out)
431+ }
274432}
275433
276434/// A Cardano address
277- #[ derive( Debug , Clone , PartialEq , Eq , serde:: Serialize , serde:: Deserialize ) ]
435+ #[ derive( Debug , Clone , PartialEq , Eq , Hash , serde:: Serialize , serde:: Deserialize ) ]
278436pub enum Address {
279437 None ,
280438 Byron ( ByronAddress ) ,
@@ -306,10 +464,9 @@ impl Address {
306464 } else if text. starts_with ( "stake1" ) || text. starts_with ( "stake_test1" ) {
307465 Ok ( Self :: Stake ( StakeAddress :: from_string ( text) ?) )
308466 } else {
309- if let Ok ( bytes) = bs58:: decode ( text) . into_vec ( ) {
310- Ok ( Self :: Byron ( ByronAddress { payload : bytes } ) )
311- } else {
312- Ok ( Self :: None )
467+ match ByronAddress :: from_string ( text) {
468+ Ok ( byron) => Ok ( Self :: Byron ( byron) ) ,
469+ Err ( _) => Ok ( Self :: None ) ,
313470 }
314471 }
315472 }
@@ -318,11 +475,46 @@ impl Address {
318475 pub fn to_string ( & self ) -> Result < String > {
319476 match self {
320477 Self :: None => Err ( anyhow ! ( "No address" ) ) ,
321- Self :: Byron ( byron) => Ok ( bs58 :: encode ( & byron. payload ) . into_string ( ) ) ,
478+ Self :: Byron ( byron) => byron. to_string ( ) ,
322479 Self :: Shelley ( shelley) => shelley. to_string ( ) ,
323480 Self :: Stake ( stake) => stake. to_string ( ) ,
324481 }
325482 }
483+
484+ pub fn to_bytes_key ( & self ) -> Result < Vec < u8 > > {
485+ match self {
486+ Address :: Byron ( b) => b. to_bytes_key ( ) ,
487+
488+ Address :: Shelley ( s) => s. to_bytes_key ( ) ,
489+
490+ Address :: Stake ( stake) => stake. to_bytes_key ( ) ,
491+
492+ Address :: None => Err ( anyhow ! ( "No address to convert" ) ) ,
493+ }
494+ }
495+
496+ pub fn kind ( & self ) -> & ' static str {
497+ match self {
498+ Address :: Byron ( _) => "byron" ,
499+ Address :: Shelley ( _) => "shelley" ,
500+ Address :: Stake ( _) => "stake" ,
501+ Address :: None => "none" ,
502+ }
503+ }
504+
505+ pub fn is_script ( & self ) -> bool {
506+ match self {
507+ Address :: Shelley ( shelley) => match shelley. payment {
508+ ShelleyAddressPaymentPart :: PaymentKeyHash ( _) => false ,
509+ ShelleyAddressPaymentPart :: ScriptHash ( _) => true ,
510+ } ,
511+ Address :: Stake ( stake) => match stake. payload {
512+ StakeAddressPayload :: StakeKeyHash ( _) => false ,
513+ StakeAddressPayload :: ScriptHash ( _) => true ,
514+ } ,
515+ Address :: Byron ( _) | Address :: None => false ,
516+ }
517+ }
326518}
327519
328520// -- Tests --
@@ -336,7 +528,7 @@ mod tests {
336528 let payload = vec ! [ 42 ] ;
337529 let address = Address :: Byron ( ByronAddress { payload } ) ;
338530 let text = address. to_string ( ) . unwrap ( ) ;
339- assert_eq ! ( text, "j " ) ;
531+ assert_eq ! ( text, "8MMy4x9jE734Gz " ) ;
340532
341533 let unpacked = Address :: from_string ( & text) . unwrap ( ) ;
342534 assert_eq ! ( address, unpacked) ;
@@ -546,6 +738,30 @@ mod tests {
546738 assert_eq ! ( address, unpacked) ;
547739 }
548740
741+ #[ test]
742+ fn shelley_to_stake_address_string_mainnet ( ) {
743+ let normal_address = ShelleyAddress :: from_string ( "addr1q82peck5fynytkgjsp9vnpul59zswsd4jqnzafd0mfzykma625r684xsx574ltpznecr9cnc7n9e2hfq9lyart3h5hpszffds5" ) . expect ( "valid normal address" ) ;
744+ let script_address = ShelleyAddress :: from_string ( "addr1zx0whlxaw4ksygvuljw8jxqlw906tlql06ern0gtvvzhh0c6409492020k6xml8uvwn34wrexagjh5fsk5xk96jyxk2qhlj6gf" ) . expect ( "valid script address" ) ;
745+
746+ let normal_stake_address = normal_address
747+ . stake_address_string ( )
748+ . expect ( "stake_address_string should not fail" )
749+ . expect ( "normal address should have stake credential" ) ;
750+ let script_stake_address = script_address
751+ . stake_address_string ( )
752+ . expect ( "stake_address_string should not fail" )
753+ . expect ( "script address should have stake credential" ) ;
754+
755+ assert_eq ! (
756+ normal_stake_address,
757+ "stake1uxa92par6ngr202l4s3fuupjufu0fju4t5szljw34cm6tscq40449"
758+ ) ;
759+ assert_eq ! (
760+ script_stake_address,
761+ "stake1uyd2hj6j4848mdrdln7x8fc6hpunw5ft6yct2rtzafzrt9qh0m28h"
762+ ) ;
763+ }
764+
549765 #[ test]
550766 fn stake_address_from_binary_mainnet_stake ( ) {
551767 // First withdrawal on Mainnet
0 commit comments