ckb_sdk/types/
address.rs

1use std::convert::{TryFrom, TryInto};
2use std::fmt;
3use std::str::FromStr;
4
5use bech32::{self, convert_bits, ToBase32, Variant};
6use ckb_hash::blake2b_256;
7use ckb_types::{
8    bytes::Bytes,
9    core::ScriptHashType,
10    packed::{Byte32, Script},
11    prelude::*,
12    H160, H256,
13};
14use serde_derive::{Deserialize, Serialize};
15
16use super::NetworkType;
17use crate::constants::{
18    MultisigScript, ACP_TYPE_HASH_AGGRON, ACP_TYPE_HASH_LINA, SIGHASH_TYPE_HASH,
19};
20pub use old_addr::{Address as OldAddress, AddressFormat as OldAddressFormat};
21
22#[derive(Hash, Eq, PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
23#[repr(u8)]
24pub enum AddressType {
25    // full version identifies the hash_type and vm_version
26    Full = 0x00,
27    // short version for locks with popular code_hash, deprecated
28    Short = 0x01,
29    // full version with hash_type = "Data", deprecated
30    FullData = 0x02,
31    // full version with hash_type = "Type", deprecated
32    FullType = 0x04,
33}
34
35impl AddressType {
36    pub fn from_u8(value: u8) -> Result<AddressType, String> {
37        match value {
38            0x00 => Ok(AddressType::Full),
39            0x01 => Ok(AddressType::Short),
40            0x02 => Ok(AddressType::FullData),
41            0x04 => Ok(AddressType::FullType),
42            _ => Err(format!("Invalid address type value: {}", value)),
43        }
44    }
45}
46
47#[derive(Hash, Eq, PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
48#[repr(u8)]
49pub enum CodeHashIndex {
50    /// SECP256K1 + blake160, args: `blake160(PK)`
51    Sighash = 0x00,
52    /// SECP256K1 + multisig, args: `multisig script hash`
53    Multisig = 0x01,
54    /// anyone_can_pay, args: `blake160(PK)`
55    Acp = 0x02,
56}
57
58impl CodeHashIndex {
59    pub fn from_u8(value: u8) -> Result<CodeHashIndex, String> {
60        match value {
61            0x00 => Ok(CodeHashIndex::Sighash),
62            0x01 => Ok(CodeHashIndex::Multisig),
63            0x02 => Ok(CodeHashIndex::Acp),
64            _ => Err(format!("Invalid code hash index value: {}", value)),
65        }
66    }
67}
68
69#[derive(Hash, Eq, PartialEq, Clone)]
70pub enum AddressPayload {
71    // Remain the address format before ckb2021.
72    Short {
73        index: CodeHashIndex,
74        hash: H160,
75    },
76    Full {
77        hash_type: ScriptHashType,
78        code_hash: Byte32,
79        args: Bytes,
80    },
81}
82
83impl AddressPayload {
84    pub fn new_short(index: CodeHashIndex, hash: H160) -> AddressPayload {
85        AddressPayload::Short { index, hash }
86    }
87
88    pub fn new_full(hash_type: ScriptHashType, code_hash: Byte32, args: Bytes) -> AddressPayload {
89        AddressPayload::Full {
90            hash_type,
91            code_hash,
92            args,
93        }
94    }
95    #[deprecated(since = "0.100.0-rc5", note = "Use AddressType::Full instead")]
96    pub fn new_full_data(code_hash: Byte32, args: Bytes) -> AddressPayload {
97        Self::new_full(ScriptHashType::Data, code_hash, args)
98    }
99    #[deprecated(since = "0.100.0-rc5", note = "Use AddressType::Full instead")]
100    pub fn new_full_type(code_hash: Byte32, args: Bytes) -> AddressPayload {
101        Self::new_full(ScriptHashType::Type, code_hash, args)
102    }
103
104    pub fn ty(&self, is_new: bool) -> AddressType {
105        match self {
106            AddressPayload::Short { .. } => AddressType::Short,
107            AddressPayload::Full { hash_type, .. } => match (hash_type, is_new) {
108                (ScriptHashType::Data, false) => AddressType::FullData,
109                (ScriptHashType::Type, false) => AddressType::FullType,
110                _ => AddressType::Full,
111            },
112        }
113    }
114
115    pub fn is_short(&self) -> bool {
116        matches!(self, AddressPayload::Short { .. })
117    }
118    pub fn is_short_acp(&self) -> bool {
119        matches!(
120            self,
121            AddressPayload::Short {
122                index: CodeHashIndex::Acp,
123                ..
124            }
125        )
126    }
127
128    pub fn hash_type(&self) -> ScriptHashType {
129        match self {
130            AddressPayload::Short { .. } => ScriptHashType::Type,
131            AddressPayload::Full { hash_type, .. } => *hash_type,
132        }
133    }
134
135    /// Get the code hash of an address
136    ///
137    /// # Panics
138    ///
139    /// When current addres is short format anyone-can-pay address, and the
140    /// network type is not `Mainnet` or `Testnet` this function will panic.
141    pub fn code_hash(&self, network: Option<NetworkType>) -> Byte32 {
142        match self {
143            AddressPayload::Short { index, .. } => match index {
144                CodeHashIndex::Sighash => SIGHASH_TYPE_HASH.clone().pack(),
145                CodeHashIndex::Multisig => MultisigScript::Legacy.script_id().code_hash.clone().pack(),
146                CodeHashIndex::Acp => match network {
147                    Some(NetworkType::Mainnet) => ACP_TYPE_HASH_LINA.clone().pack(),
148                    Some(NetworkType::Testnet) => ACP_TYPE_HASH_AGGRON.clone().pack(),
149                    _ => panic!("network type must be `mainnet` or `testnet` when handle short format anyone-can-pay address"),
150                }
151            },
152            AddressPayload::Full { code_hash, .. } => code_hash.clone(),
153        }
154    }
155
156    pub fn args(&self) -> Bytes {
157        match self {
158            AddressPayload::Short { hash, .. } => Bytes::from(hash.as_bytes().to_vec()),
159            AddressPayload::Full { args, .. } => args.clone(),
160        }
161    }
162
163    pub fn from_pubkey(pubkey: &secp256k1::PublicKey) -> AddressPayload {
164        // Serialize pubkey as compressed format
165        let hash = H160::from_slice(&blake2b_256(&pubkey.serialize()[..])[0..20])
166            .expect("Generate hash(H160) from pubkey failed");
167        AddressPayload::from_pubkey_hash(hash)
168    }
169
170    pub fn from_pubkey_hash(hash: H160) -> AddressPayload {
171        let index = CodeHashIndex::Sighash;
172        AddressPayload::Short { index, hash }
173    }
174
175    pub fn display_with_network(&self, network: NetworkType, is_new: bool) -> String {
176        let hrp = network.to_prefix();
177        let (data, variant) = if is_new {
178            // payload = 0x00 | code_hash | hash_type | args
179            let code_hash = self.code_hash(Some(network));
180            let hash_type = self.hash_type();
181            let args = self.args();
182            let mut data = vec![0u8; 34 + args.len()];
183            data[0] = 0x00;
184            data[1..33].copy_from_slice(code_hash.as_slice());
185            data[33] = hash_type as u8;
186            data[34..].copy_from_slice(args.as_ref());
187            (data, bech32::Variant::Bech32m)
188        } else {
189            match self {
190                // payload = 0x01 | code_hash_index | args
191                AddressPayload::Short { index, hash } => {
192                    let mut data = vec![0u8; 22];
193                    data[0] = 0x01;
194                    data[1] = (*index) as u8;
195                    data[2..].copy_from_slice(hash.as_bytes());
196                    // short address always use bech32
197                    (data, bech32::Variant::Bech32)
198                }
199                AddressPayload::Full {
200                    code_hash, args, ..
201                } => {
202                    // payload = 0x02/0x04 | code_hash | args
203                    let mut data = vec![0u8; 33 + args.len()];
204                    data[0] = self.ty(false) as u8;
205                    data[1..33].copy_from_slice(code_hash.as_slice());
206                    data[33..].copy_from_slice(args.as_ref());
207                    (data, bech32::Variant::Bech32)
208                }
209            }
210        };
211        bech32::encode(hrp, data.to_base32(), variant)
212            .unwrap_or_else(|_| panic!("Encode address failed: payload={:?}", self))
213    }
214}
215
216impl fmt::Debug for AddressPayload {
217    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
218        let hash_type = match self.hash_type() {
219            ScriptHashType::Type => "type",
220            ScriptHashType::Data => "data",
221            ScriptHashType::Data1 => "data1",
222            ScriptHashType::Data2 => "data2",
223            _ => "unsupported hash_type",
224        };
225        f.debug_struct("AddressPayload")
226            .field("hash_type", &hash_type)
227            .field("code_hash", &self.code_hash(None))
228            .field("args", &self.args())
229            .finish()
230    }
231}
232
233impl From<&AddressPayload> for Script {
234    fn from(payload: &AddressPayload) -> Script {
235        Script::new_builder()
236            .hash_type(payload.hash_type())
237            .code_hash(payload.code_hash(None))
238            .args(payload.args().pack())
239            .build()
240    }
241}
242
243impl From<Script> for AddressPayload {
244    #[allow(clippy::fallible_impl_from, clippy::if_same_then_else)]
245    fn from(lock: Script) -> AddressPayload {
246        let hash_type: ScriptHashType = lock.hash_type().try_into().expect("Invalid hash_type");
247        let code_hash = lock.code_hash();
248        let code_hash_h256: H256 = code_hash.unpack();
249        let args = lock.args().raw_data();
250        if hash_type == ScriptHashType::Type
251            && code_hash_h256 == SIGHASH_TYPE_HASH
252            && args.len() == 20
253        {
254            let index = CodeHashIndex::Sighash;
255            let hash = H160::from_slice(args.as_ref()).unwrap();
256            AddressPayload::Short { index, hash }
257        } else if hash_type == MultisigScript::Legacy.script_id().hash_type
258            && code_hash_h256 == MultisigScript::Legacy.script_id().code_hash
259            && args.len() == 20
260        {
261            let index = CodeHashIndex::Multisig;
262            let hash = H160::from_slice(args.as_ref()).unwrap();
263            AddressPayload::Short { index, hash }
264        } else if hash_type == MultisigScript::V2.script_id().hash_type
265            && code_hash_h256 == MultisigScript::V2.script_id().code_hash
266            && args.len() == 20
267        {
268            // V2 has different code-hash from Legacy, use AddressPayload::Full
269            AddressPayload::Full {
270                hash_type,
271                code_hash,
272                args,
273            }
274        } else if hash_type == ScriptHashType::Type
275            && (code_hash_h256 == ACP_TYPE_HASH_LINA || code_hash_h256 == ACP_TYPE_HASH_AGGRON)
276            && args.len() == 20
277        {
278            // NOTE: anoney-can-pay script args can larger than 20 bytes, here
279            // args.len() != 20 is not a short format address, see RFC21 for
280            // more details.
281            let index = CodeHashIndex::Acp;
282            let hash = H160::from_slice(args.as_ref()).unwrap();
283            AddressPayload::Short { index, hash }
284        } else {
285            AddressPayload::Full {
286                hash_type,
287                code_hash,
288                args,
289            }
290        }
291    }
292}
293
294#[derive(Hash, Eq, PartialEq, Clone)]
295pub struct Address {
296    network: NetworkType,
297    payload: AddressPayload,
298    is_new: bool,
299}
300
301impl Address {
302    pub fn new(network: NetworkType, payload: AddressPayload, is_new: bool) -> Address {
303        Address {
304            network,
305            payload,
306            is_new,
307        }
308    }
309    /// The network type of current address
310    pub fn network(&self) -> NetworkType {
311        self.network
312    }
313    /// The address payload
314    pub fn payload(&self) -> &AddressPayload {
315        &self.payload
316    }
317    /// If true the address is ckb2021 format
318    pub fn is_new(&self) -> bool {
319        self.is_new
320    }
321}
322
323impl fmt::Debug for Address {
324    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
325        let hash_type = match self.payload.hash_type() {
326            ScriptHashType::Type => "type",
327            ScriptHashType::Data => "data",
328            ScriptHashType::Data1 => "data1",
329            ScriptHashType::Data2 => "data2",
330            _ => "unsupported hash_type",
331        };
332        f.debug_struct("Address")
333            .field("network", &self.network)
334            .field("hash_type", &hash_type)
335            .field("code_hash", &self.payload.code_hash(Some(self.network)))
336            .field("args", &self.payload.args())
337            .field("is_new", &self.is_new)
338            .finish()
339    }
340}
341
342impl From<&Address> for Script {
343    fn from(addr: &Address) -> Script {
344        Script::new_builder()
345            .hash_type(addr.payload.hash_type())
346            .code_hash(addr.payload.code_hash(Some(addr.network)))
347            .args(addr.payload.args().pack())
348            .build()
349    }
350}
351
352impl fmt::Display for Address {
353    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
354        write!(
355            f,
356            "{}",
357            self.payload.display_with_network(self.network, self.is_new)
358        )
359    }
360}
361
362impl FromStr for Address {
363    type Err = String;
364
365    fn from_str(input: &str) -> Result<Self, Self::Err> {
366        let (hrp, data, variant) = bech32::decode(input).map_err(|err| err.to_string())?;
367        let network =
368            NetworkType::from_prefix(&hrp).ok_or_else(|| format!("Invalid hrp: {}", hrp))?;
369        let data = convert_bits(&data, 5, 8, false).unwrap();
370        let ty = AddressType::from_u8(data[0])?;
371        match ty {
372            // payload = 0x01 | code_hash_index | args
373            AddressType::Short => {
374                if variant != Variant::Bech32 {
375                    return Err("short address must use bech32 encoding".to_string());
376                }
377                if data.len() != 22 {
378                    return Err(format!("Invalid input data length {}", data.len()));
379                }
380                let index = CodeHashIndex::from_u8(data[1])?;
381                let hash = H160::from_slice(&data[2..22]).unwrap();
382                let payload = AddressPayload::Short { index, hash };
383                Ok(Address {
384                    network,
385                    payload,
386                    is_new: false,
387                })
388            }
389            // payload = 0x02/0x04 | code_hash | args
390            AddressType::FullData | AddressType::FullType => {
391                if variant != Variant::Bech32 {
392                    return Err(
393                        "non-ckb2021 format full address must use bech32 encoding".to_string()
394                    );
395                }
396                if data.len() < 33 {
397                    return Err(format!("Insufficient data length: {}", data.len()));
398                }
399                let hash_type = if ty == AddressType::FullData {
400                    ScriptHashType::Data
401                } else {
402                    ScriptHashType::Type
403                };
404                let code_hash = Byte32::from_slice(&data[1..33]).unwrap();
405                let args = Bytes::from(data[33..].to_vec());
406                let payload = AddressPayload::Full {
407                    hash_type,
408                    code_hash,
409                    args,
410                };
411                Ok(Address {
412                    network,
413                    payload,
414                    is_new: false,
415                })
416            }
417            // payload = 0x00 | code_hash | hash_type | args
418            AddressType::Full => {
419                if variant != Variant::Bech32m {
420                    return Err("ckb2021 format full address must use bech32m encoding".to_string());
421                }
422                if data.len() < 34 {
423                    return Err(format!("Insufficient data length: {}", data.len()));
424                }
425                let code_hash = Byte32::from_slice(&data[1..33]).unwrap();
426                let hash_type =
427                    ScriptHashType::try_from(data[33]).map_err(|err| err.to_string())?;
428                let args = Bytes::from(data[34..].to_vec());
429                let payload = AddressPayload::Full {
430                    hash_type,
431                    code_hash,
432                    args,
433                };
434                Ok(Address {
435                    network,
436                    payload,
437                    is_new: true,
438                })
439            }
440        }
441    }
442}
443
444mod old_addr {
445    use super::{
446        bech32, blake2b_256, convert_bits, Deserialize, NetworkType, Script, ScriptHashType,
447        Serialize, ToBase32, H160, H256,
448    };
449    use ckb_crypto::secp::Pubkey;
450    use ckb_types::prelude::*;
451
452    // \x01 is the P2PH version
453    const P2PH_MARK: &[u8] = b"\x01P2PH";
454
455    #[derive(Hash, Eq, PartialEq, Debug, Clone, Copy, Serialize, Deserialize, Default)]
456    pub enum AddressFormat {
457        // SECP256K1 algorithm	PK
458        #[allow(dead_code)]
459        Sp2k,
460        // SECP256R1 algorithm	PK
461        #[allow(dead_code)]
462        Sp2r,
463        // SECP256K1 + blake160	blake160(pk)
464        #[default]
465        P2ph,
466        // Alias of SP2K	PK
467        #[allow(dead_code)]
468        P2pk,
469    }
470
471    impl AddressFormat {
472        pub fn from_bytes(format: &[u8]) -> Result<AddressFormat, String> {
473            match format {
474                P2PH_MARK => Ok(AddressFormat::P2ph),
475                _ => Err(format!("Unsupported address format data: {:?}", format)),
476            }
477        }
478
479        pub fn to_bytes(self) -> Result<Vec<u8>, String> {
480            match self {
481                AddressFormat::P2ph => Ok(P2PH_MARK.to_vec()),
482                _ => Err(format!("Unsupported address format: {:?}", self)),
483            }
484        }
485    }
486
487    #[derive(Hash, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)]
488    pub struct Address {
489        format: AddressFormat,
490        hash: H160,
491    }
492
493    impl Address {
494        pub fn new_default(hash: H160) -> Address {
495            let format = AddressFormat::P2ph;
496            Address { format, hash }
497        }
498
499        pub fn hash(&self) -> &H160 {
500            &self.hash
501        }
502
503        pub fn lock_script(&self, code_hash: H256) -> Script {
504            Script::new_builder()
505                .args(self.hash.as_bytes().pack())
506                .code_hash(code_hash.pack())
507                .hash_type(ScriptHashType::Data)
508                .build()
509        }
510
511        pub fn from_pubkey(format: AddressFormat, pubkey: &Pubkey) -> Result<Address, String> {
512            if format != AddressFormat::P2ph {
513                return Err("Only support P2PH for now".to_owned());
514            }
515            // Serialize pubkey as compressed format
516            let hash = H160::from_slice(&blake2b_256(pubkey.serialize())[0..20])
517                .expect("Generate hash(H160) from pubkey failed");
518            Ok(Address { format, hash })
519        }
520
521        pub fn from_lock_arg(bytes: &[u8]) -> Result<Address, String> {
522            let format = AddressFormat::P2ph;
523            let hash = H160::from_slice(bytes).map_err(|err| err.to_string())?;
524            Ok(Address { format, hash })
525        }
526
527        pub fn from_input(network: NetworkType, input: &str) -> Result<Address, String> {
528            let (hrp, data, _variant) = bech32::decode(input).map_err(|err| err.to_string())?;
529            if NetworkType::from_prefix(&hrp)
530                .filter(|input_network| input_network == &network)
531                .is_none()
532            {
533                return Err(format!("Invalid hrp({}) for {}", hrp, network));
534            }
535            let data = convert_bits(&data, 5, 8, false).unwrap();
536            if data.len() != 25 {
537                return Err(format!("Invalid input data length {}", data.len()));
538            }
539            let format = AddressFormat::from_bytes(&data[0..5])?;
540            let hash = H160::from_slice(&data[5..25]).map_err(|err| err.to_string())?;
541            Ok(Address { format, hash })
542        }
543
544        pub fn display_with_prefix(&self, network: NetworkType) -> String {
545            let hrp = network.to_prefix();
546            let mut data = [0; 25];
547            let format_data = self.format.to_bytes().expect("Invalid address format");
548            data[0..5].copy_from_slice(&format_data[0..5]);
549            data[5..25].copy_from_slice(self.hash.as_bytes());
550            bech32::encode(hrp, data.to_base32(), bech32::Variant::Bech32)
551                .unwrap_or_else(|_| panic!("Encode address failed: hash={:?}", self.hash))
552        }
553
554        #[allow(clippy::inherent_to_string)]
555        #[deprecated(
556            since = "0.25.0",
557            note = "Name conflicts with the inherent to_string method. Use display_with_prefix instead."
558        )]
559        pub fn to_string(&self, network: NetworkType) -> String {
560            self.display_with_prefix(network)
561        }
562    }
563}
564
565#[cfg(test)]
566mod test {
567    use super::*;
568    use ckb_types::{h160, h256};
569
570    #[test]
571    fn test_short_address() {
572        let payload =
573            AddressPayload::from_pubkey_hash(h160!("0xb39bbc0b3673c7d36450bc14cfcdad2d559c6c64"));
574        let address = Address::new(NetworkType::Mainnet, payload, false);
575        assert_eq!(
576            address.to_string(),
577            "ckb1qyqt8xaupvm8837nv3gtc9x0ekkj64vud3jqfwyw5v"
578        );
579        assert_eq!(
580            address,
581            Address::from_str("ckb1qyqt8xaupvm8837nv3gtc9x0ekkj64vud3jqfwyw5v").unwrap()
582        );
583
584        let payload =
585            AddressPayload::from_pubkey_hash(h160!("0xb39bbc0b3673c7d36450bc14cfcdad2d559c6c64"));
586        let address = Address::new(NetworkType::Mainnet, payload.clone(), false);
587        let address_new = Address::new(NetworkType::Mainnet, payload, true);
588        assert_eq!(
589            address.to_string(),
590            "ckb1qyqt8xaupvm8837nv3gtc9x0ekkj64vud3jqfwyw5v"
591        );
592        assert_eq!(
593            address_new.to_string(),
594            "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdnnw7qkdnnclfkg59uzn8umtfd2kwxceqxwquc4"
595        );
596
597        let index = CodeHashIndex::Multisig;
598        let payload =
599            AddressPayload::new_short(index, h160!("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a"));
600        let address = Address::new(NetworkType::Mainnet, payload, false);
601        assert_eq!(
602            address.to_string(),
603            "ckb1qyq5lv479ewscx3ms620sv34pgeuz6zagaaqklhtgg"
604        );
605        assert_eq!(
606            address,
607            Address::from_str("ckb1qyq5lv479ewscx3ms620sv34pgeuz6zagaaqklhtgg").unwrap()
608        );
609    }
610
611    #[test]
612    fn test_old_full_address() {
613        let hash_type = ScriptHashType::Type;
614        let code_hash = Byte32::from_slice(
615            h256!("0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8").as_bytes(),
616        )
617        .unwrap();
618        let args = Bytes::from(h160!("0xb39bbc0b3673c7d36450bc14cfcdad2d559c6c64").as_bytes());
619        let payload = AddressPayload::new_full(hash_type, code_hash, args);
620        let address = Address::new(NetworkType::Mainnet, payload, false);
621        assert_eq!(address.to_string(), "ckb1qjda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xw3vumhs9nvu786dj9p0q5elx66t24n3kxgj53qks");
622        assert_eq!(address, Address::from_str("ckb1qjda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xw3vumhs9nvu786dj9p0q5elx66t24n3kxgj53qks").unwrap());
623    }
624
625    #[test]
626    fn test_new_full_address() {
627        let code_hash = Byte32::from_slice(
628            h256!("0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8").as_bytes(),
629        )
630        .unwrap();
631        let args = Bytes::from(h160!("0xb39bbc0b3673c7d36450bc14cfcdad2d559c6c64").as_bytes());
632
633        let payload =
634            AddressPayload::new_full(ScriptHashType::Type, code_hash.clone(), args.clone());
635        let address = Address::new(NetworkType::Mainnet, payload, true);
636        assert_eq!(address.to_string(), "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdnnw7qkdnnclfkg59uzn8umtfd2kwxceqxwquc4");
637        assert_eq!(address, Address::from_str("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdnnw7qkdnnclfkg59uzn8umtfd2kwxceqxwquc4").unwrap());
638
639        let payload =
640            AddressPayload::new_full(ScriptHashType::Data, code_hash.clone(), args.clone());
641        let address = Address::new(NetworkType::Mainnet, payload, true);
642        assert_eq!(address.to_string(), "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq9nnw7qkdnnclfkg59uzn8umtfd2kwxceqvguktl");
643        assert_eq!(address, Address::from_str("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq9nnw7qkdnnclfkg59uzn8umtfd2kwxceqvguktl").unwrap());
644
645        let payload = AddressPayload::new_full(ScriptHashType::Data1, code_hash, args);
646        let address = Address::new(NetworkType::Mainnet, payload, true);
647        assert_eq!(address.to_string(), "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq4nnw7qkdnnclfkg59uzn8umtfd2kwxceqcydzyt");
648        assert_eq!(address, Address::from_str("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq4nnw7qkdnnclfkg59uzn8umtfd2kwxceqcydzyt").unwrap());
649    }
650
651    #[test]
652    fn test_parse_display_address() {
653        let addr_str = "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqgvf0k9sc40s3azmpfvhyuudhahpsj72tsr8cx3d";
654        let addr = Address::from_str(addr_str).unwrap();
655        assert_eq!(addr.to_string(), addr_str);
656        assert_eq!(
657            addr.payload().code_hash(None).as_slice(),
658            h256!("0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8").as_bytes()
659        );
660        assert_eq!(addr.payload().hash_type(), ScriptHashType::Type);
661        assert_eq!(
662            addr.payload().args().as_ref(),
663            hex::decode("0c4bec5862af847a2d852cb939c6dfb70c25e52e").unwrap()
664        );
665    }
666
667    #[test]
668    fn test_invalid_short_address() {
669        // INVALID bech32 encoding
670        {
671            let mut data = vec![0u8; 22];
672            data[0] = 0x01;
673            data[1] = CodeHashIndex::Sighash as u8;
674            data[2..]
675                .copy_from_slice(h160!("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a").as_bytes());
676            let variant = bech32::Variant::Bech32m;
677            let addr = bech32::encode("ckb", data.to_base32(), variant).unwrap();
678            let expected_addr = "ckb1qyqylv479ewscx3ms620sv34pgeuz6zagaaqh0knz7";
679            assert_eq!(addr, expected_addr);
680            assert_eq!(
681                Address::from_str(expected_addr),
682                Err("short address must use bech32 encoding".to_string())
683            );
684        }
685        // INVALID data length
686        {
687            let mut data = vec![0u8; 23];
688            data[0] = 0x01;
689            data[1] = CodeHashIndex::Sighash as u8;
690            data[2..].copy_from_slice(
691                &hex::decode("4fb2be2e5d0c1a3b8694f832350a33c1685d477a33").unwrap(),
692            );
693            let variant = bech32::Variant::Bech32;
694            let addr = bech32::encode("ckb", data.to_base32(), variant).unwrap();
695            let expected_addr = "ckb1qyqylv479ewscx3ms620sv34pgeuz6zagaarxdzvx03";
696            assert_eq!(addr, expected_addr);
697            assert_eq!(
698                Address::from_str(expected_addr),
699                Err("Invalid input data length 23".to_string())
700            );
701        }
702        // INVALID code hash index
703        {
704            let mut data = vec![0u8; 22];
705            data[0] = 0x01;
706            data[1] = 17;
707            data[2..]
708                .copy_from_slice(h160!("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a").as_bytes());
709            let variant = bech32::Variant::Bech32;
710            let addr = bech32::encode("ckb", data.to_base32(), variant).unwrap();
711            let expected_addr = "ckb1qyg5lv479ewscx3ms620sv34pgeuz6zagaaqajch0c";
712            assert_eq!(addr, expected_addr);
713            assert_eq!(
714                Address::from_str(expected_addr),
715                Err("Invalid code hash index value: 17".to_string())
716            );
717        }
718    }
719
720    #[test]
721    fn test_invalid_old_full_address() {
722        // INVALID bech32 encoding
723        {
724            let args = hex::decode("4fb2be2e5d0c1a3b86").unwrap();
725            let mut data = vec![0u8; 33 + args.len()];
726            data[0] = AddressType::FullData as u8;
727            data[1..33].copy_from_slice(
728                h256!("0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")
729                    .as_bytes(),
730            );
731            data[33..].copy_from_slice(args.as_ref());
732            let variant = bech32::Variant::Bech32m;
733            let addr = bech32::encode("ckb", data.to_base32(), variant).unwrap();
734            let expected_addr =
735                "ckb1q2da0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsnajhch96rq68wrqn2tmhm";
736            assert_eq!(addr, expected_addr);
737            assert_eq!(
738                Address::from_str(expected_addr),
739                Err("non-ckb2021 format full address must use bech32 encoding".to_string())
740            );
741        }
742    }
743
744    #[test]
745    fn test_invalid_new_address() {
746        // INVALID bech32 encoding
747        for (hash_type, expected_addr) in [
748            (
749                ScriptHashType::Type,
750                "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq20k2lzuhgvrgacv4tmr88",
751            ),
752            (
753                ScriptHashType::Data,
754                "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqz0k2lzuhgvrgacvhcym08",
755            ),
756            (
757                ScriptHashType::Data1,
758                "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqj0k2lzuhgvrgacvnhnzl8",
759            ),
760        ] {
761            let code_hash =
762                h256!("0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8");
763            let args = hex::decode("4fb2be2e5d0c1a3b86").unwrap();
764            let mut data = vec![0u8; 34 + args.len()];
765            data[0] = 0x00;
766            data[1..33].copy_from_slice(code_hash.as_bytes());
767            data[33] = hash_type as u8;
768            data[34..].copy_from_slice(args.as_ref());
769            let variant = bech32::Variant::Bech32;
770            let addr = bech32::encode("ckb", data.to_base32(), variant).unwrap();
771            assert_eq!(addr, expected_addr);
772            assert_eq!(
773                Address::from_str(expected_addr),
774                Err("ckb2021 format full address must use bech32m encoding".to_string())
775            );
776        }
777    }
778
779    #[test]
780    fn test_address_debug() {
781        let payload = AddressPayload::Full {
782            hash_type: ScriptHashType::Data1,
783            code_hash: h256!("0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")
784                .pack(),
785            args: Bytes::from("abcd"),
786        };
787        let address = Address::new(NetworkType::Mainnet, payload.clone(), true);
788        assert_eq!(format!("{:?}", payload), "AddressPayload { hash_type: \"data1\", code_hash: Byte32(0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8), args: b\"abcd\" }");
789        assert_eq!(format!("{:?}", address), "Address { network: Mainnet, hash_type: \"data1\", code_hash: Byte32(0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8), args: b\"abcd\", is_new: true }");
790    }
791}