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 = 0x00,
27 Short = 0x01,
29 FullData = 0x02,
31 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 Sighash = 0x00,
52 Multisig = 0x01,
54 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 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 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 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 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 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 (data, bech32::Variant::Bech32)
198 }
199 AddressPayload::Full {
200 code_hash, args, ..
201 } => {
202 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 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 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 pub fn network(&self) -> NetworkType {
311 self.network
312 }
313 pub fn payload(&self) -> &AddressPayload {
315 &self.payload
316 }
317 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 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 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 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 const P2PH_MARK: &[u8] = b"\x01P2PH";
454
455 #[derive(Hash, Eq, PartialEq, Debug, Clone, Copy, Serialize, Deserialize, Default)]
456 pub enum AddressFormat {
457 #[allow(dead_code)]
459 Sp2k,
460 #[allow(dead_code)]
462 Sp2r,
463 #[default]
465 P2ph,
466 #[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 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 {
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 {
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 {
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 {
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 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}