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::{Deserialize, Serialize};
15
16use crate::constant::{
17 ACP_TYPE_HASH_AGGRON, ACP_TYPE_HASH_LINA, MULTISIG_TYPE_HASH, SIGHASH_TYPE_HASH,
18};
19use crate::network_type::NetworkType;
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, true) => AddressType::Full,
109 (ScriptHashType::Type, true) => AddressType::Full,
110 (ScriptHashType::Data1, _) => AddressType::Full,
111 (ScriptHashType::Data2, _) => AddressType::Full,
112 (ScriptHashType::Data, false) => AddressType::FullData,
113 (ScriptHashType::Type, false) => AddressType::FullType,
114 },
115 }
116 }
117
118 pub fn is_short(&self) -> bool {
119 matches!(self, AddressPayload::Short { .. })
120 }
121 pub fn is_short_acp(&self) -> bool {
122 matches!(
123 self,
124 AddressPayload::Short {
125 index: CodeHashIndex::Acp,
126 ..
127 }
128 )
129 }
130
131 pub fn hash_type(&self) -> ScriptHashType {
132 match self {
133 AddressPayload::Short { .. } => ScriptHashType::Type,
134 AddressPayload::Full { hash_type, .. } => *hash_type,
135 }
136 }
137
138 pub fn code_hash(&self, network: Option<NetworkType>) -> Byte32 {
145 match self {
146 AddressPayload::Short { index, .. } => match index {
147 CodeHashIndex::Sighash => SIGHASH_TYPE_HASH.clone().pack(),
148 CodeHashIndex::Multisig => MULTISIG_TYPE_HASH.clone().pack(),
149 CodeHashIndex::Acp => match network {
150 Some(NetworkType::Mainnet) => ACP_TYPE_HASH_LINA.clone().pack(),
151 Some(NetworkType::Testnet) => ACP_TYPE_HASH_AGGRON.clone().pack(),
152 _ => panic!("network type must be `mainnet` or `testnet` when handle short format anyone-can-pay address"),
153 }
154 },
155 AddressPayload::Full { code_hash, .. } => code_hash.clone(),
156 }
157 }
158
159 pub fn args(&self) -> Bytes {
160 match self {
161 AddressPayload::Short { hash, .. } => Bytes::from(hash.as_bytes().to_vec()),
162 AddressPayload::Full { args, .. } => args.clone(),
163 }
164 }
165
166 pub fn from_pubkey(pubkey: &secp256k1::PublicKey) -> AddressPayload {
167 let hash = H160::from_slice(&blake2b_256(&pubkey.serialize()[..])[0..20])
169 .expect("Generate hash(H160) from pubkey failed");
170 AddressPayload::from_pubkey_hash(hash)
171 }
172
173 pub fn from_pubkey_hash(hash: H160) -> AddressPayload {
174 let index = CodeHashIndex::Sighash;
175 AddressPayload::Short { index, hash }
176 }
177
178 pub fn display_with_network(&self, network: NetworkType, is_new: bool) -> String {
179 let hrp = network.to_prefix();
180 let (data, variant) = if is_new {
181 let code_hash = self.code_hash(Some(network));
183 let hash_type = self.hash_type();
184 let args = self.args();
185 let mut data = vec![0u8; 34 + args.len()];
186 data[0] = 0x00;
187 data[1..33].copy_from_slice(code_hash.as_slice());
188 data[33] = hash_type as u8;
189 data[34..].copy_from_slice(args.as_ref());
190 (data, bech32::Variant::Bech32m)
191 } else {
192 match self {
193 AddressPayload::Short { index, hash } => {
195 let mut data = vec![0u8; 22];
196 data[0] = 0x01;
197 data[1] = (*index) as u8;
198 data[2..].copy_from_slice(hash.as_bytes());
199 (data, bech32::Variant::Bech32)
201 }
202 AddressPayload::Full {
203 code_hash, args, ..
204 } => {
205 let mut data = vec![0u8; 33 + args.len()];
207 data[0] = self.ty(false) as u8;
208 data[1..33].copy_from_slice(code_hash.as_slice());
209 data[33..].copy_from_slice(args.as_ref());
210 (data, bech32::Variant::Bech32)
211 }
212 }
213 };
214 bech32::encode(hrp, data.to_base32(), variant)
215 .unwrap_or_else(|_| panic!("Encode address failed: payload={:?}", self))
216 }
217}
218
219impl fmt::Debug for AddressPayload {
220 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
221 let hash_type = match self.hash_type() {
222 ScriptHashType::Type => "type",
223 ScriptHashType::Data => "data",
224 ScriptHashType::Data1 => "data1",
225 ScriptHashType::Data2 => "data2",
226 };
227 f.debug_struct("AddressPayload")
228 .field("hash_type", &hash_type)
229 .field("code_hash", &self.code_hash(None))
230 .field("args", &self.args())
231 .finish()
232 }
233}
234
235impl From<&AddressPayload> for Script {
236 fn from(payload: &AddressPayload) -> Script {
237 Script::new_builder()
238 .hash_type(payload.hash_type().into())
239 .code_hash(payload.code_hash(None))
240 .args(payload.args().pack())
241 .build()
242 }
243}
244
245impl From<Script> for AddressPayload {
246 #[allow(clippy::fallible_impl_from)]
247 fn from(lock: Script) -> AddressPayload {
248 let hash_type: ScriptHashType = lock.hash_type().try_into().expect("Invalid hash_type");
249 let code_hash = lock.code_hash();
250 let code_hash_h256: H256 = code_hash.unpack();
251 let args = lock.args().raw_data();
252 if hash_type == ScriptHashType::Type
253 && code_hash_h256 == SIGHASH_TYPE_HASH
254 && args.len() == 20
255 {
256 let index = CodeHashIndex::Sighash;
257 let hash = H160::from_slice(args.as_ref()).unwrap();
258 AddressPayload::Short { index, hash }
259 } else if hash_type == ScriptHashType::Type
260 && code_hash_h256 == MULTISIG_TYPE_HASH
261 && args.len() == 20
262 {
263 let index = CodeHashIndex::Multisig;
264 let hash = H160::from_slice(args.as_ref()).unwrap();
265 AddressPayload::Short { index, hash }
266 } else if hash_type == ScriptHashType::Type
267 && (code_hash_h256 == ACP_TYPE_HASH_LINA || code_hash_h256 == ACP_TYPE_HASH_AGGRON)
268 && args.len() == 20
269 {
270 let index = CodeHashIndex::Acp;
274 let hash = H160::from_slice(args.as_ref()).unwrap();
275 AddressPayload::Short { index, hash }
276 } else {
277 AddressPayload::Full {
278 hash_type,
279 code_hash,
280 args,
281 }
282 }
283 }
284}
285
286#[derive(Hash, Eq, PartialEq, Clone)]
287pub struct Address {
288 network: NetworkType,
289 payload: AddressPayload,
290 is_new: bool,
291}
292
293impl Address {
294 pub fn new(network: NetworkType, payload: AddressPayload, is_new: bool) -> Address {
295 Address {
296 network,
297 payload,
298 is_new,
299 }
300 }
301 pub fn network(&self) -> NetworkType {
303 self.network
304 }
305 pub fn payload(&self) -> &AddressPayload {
307 &self.payload
308 }
309 pub fn is_new(&self) -> bool {
311 self.is_new
312 }
313}
314
315impl fmt::Debug for Address {
316 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
317 let hash_type = match self.payload.hash_type() {
318 ScriptHashType::Type => "type",
319 ScriptHashType::Data => "data",
320 ScriptHashType::Data1 => "data1",
321 ScriptHashType::Data2 => "data2",
322 };
323 f.debug_struct("Address")
324 .field("network", &self.network)
325 .field("hash_type", &hash_type)
326 .field("code_hash", &self.payload.code_hash(Some(self.network)))
327 .field("args", &self.payload.args())
328 .field("is_new", &self.is_new)
329 .finish()
330 }
331}
332
333impl From<&Address> for Script {
334 fn from(addr: &Address) -> Script {
335 Script::new_builder()
336 .hash_type(addr.payload.hash_type().into())
337 .code_hash(addr.payload.code_hash(Some(addr.network)))
338 .args(addr.payload.args().pack())
339 .build()
340 }
341}
342
343impl fmt::Display for Address {
344 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
345 write!(
346 f,
347 "{}",
348 self.payload.display_with_network(self.network, self.is_new)
349 )
350 }
351}
352
353impl FromStr for Address {
354 type Err = String;
355
356 fn from_str(input: &str) -> Result<Self, Self::Err> {
357 let (hrp, data, variant) = bech32::decode(input).map_err(|err| err.to_string())?;
358 let network =
359 NetworkType::from_prefix(&hrp).ok_or_else(|| format!("Invalid hrp: {}", hrp))?;
360 let data = convert_bits(&data, 5, 8, false).unwrap();
361 let ty = AddressType::from_u8(data[0])?;
362 match ty {
363 AddressType::Short => {
365 if variant != Variant::Bech32 {
366 return Err("short address must use bech32 encoding".to_string());
367 }
368 if data.len() != 22 {
369 return Err(format!("Invalid input data length {}", data.len()));
370 }
371 let index = CodeHashIndex::from_u8(data[1])?;
372 let hash = H160::from_slice(&data[2..22]).unwrap();
373 let payload = AddressPayload::Short { index, hash };
374 Ok(Address {
375 network,
376 payload,
377 is_new: false,
378 })
379 }
380 AddressType::FullData | AddressType::FullType => {
382 if variant != Variant::Bech32 {
383 return Err(
384 "non-ckb2021 format full address must use bech32 encoding".to_string()
385 );
386 }
387 if data.len() < 33 {
388 return Err(format!("Insufficient data length: {}", data.len()));
389 }
390 let hash_type = if ty == AddressType::FullData {
391 ScriptHashType::Data
392 } else {
393 ScriptHashType::Type
394 };
395 let code_hash = Byte32::from_slice(&data[1..33]).unwrap();
396 let args = Bytes::from(data[33..].to_vec());
397 let payload = AddressPayload::Full {
398 hash_type,
399 code_hash,
400 args,
401 };
402 Ok(Address {
403 network,
404 payload,
405 is_new: false,
406 })
407 }
408 AddressType::Full => {
410 if variant != Variant::Bech32m {
411 return Err("ckb2021 format full address must use bech32m encoding".to_string());
412 }
413 if data.len() < 34 {
414 return Err(format!("Insufficient data length: {}", data.len()));
415 }
416 let code_hash = Byte32::from_slice(&data[1..33]).unwrap();
417 let hash_type =
418 ScriptHashType::try_from(data[33]).map_err(|err| err.to_string())?;
419 let args = Bytes::from(data[34..].to_vec());
420 let payload = AddressPayload::Full {
421 hash_type,
422 code_hash,
423 args,
424 };
425 Ok(Address {
426 network,
427 payload,
428 is_new: true,
429 })
430 }
431 }
432 }
433}
434
435mod old_addr {
436 use super::{
437 bech32, blake2b_256, convert_bits, Deserialize, NetworkType, Script, ScriptHashType,
438 Serialize, ToBase32, H160, H256,
439 };
440 use ckb_crypto::secp::Pubkey;
441 use ckb_types::prelude::*;
442
443 const P2PH_MARK: &[u8] = b"\x01P2PH";
445
446 #[derive(Hash, Eq, PartialEq, Debug, Clone, Copy, Serialize, Deserialize, Default)]
447 pub enum AddressFormat {
448 #[allow(dead_code)]
450 Sp2k,
451 #[allow(dead_code)]
453 Sp2r,
454 #[default]
456 P2ph,
457 #[allow(dead_code)]
459 P2pk,
460 }
461
462 impl AddressFormat {
463 pub fn from_bytes(format: &[u8]) -> Result<AddressFormat, String> {
464 match format {
465 P2PH_MARK => Ok(AddressFormat::P2ph),
466 _ => Err(format!("Unsupported address format data: {:?}", format)),
467 }
468 }
469
470 pub fn to_bytes(self) -> Result<Vec<u8>, String> {
471 match self {
472 AddressFormat::P2ph => Ok(P2PH_MARK.to_vec()),
473 _ => Err(format!("Unsupported address format: {:?}", self)),
474 }
475 }
476 }
477
478 #[derive(Hash, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)]
479 pub struct Address {
480 format: AddressFormat,
481 hash: H160,
482 }
483
484 impl Address {
485 pub fn new_default(hash: H160) -> Address {
486 let format = AddressFormat::P2ph;
487 Address { format, hash }
488 }
489
490 pub fn hash(&self) -> &H160 {
491 &self.hash
492 }
493
494 pub fn lock_script(&self, code_hash: H256) -> Script {
495 Script::new_builder()
496 .args(self.hash.as_bytes().pack())
497 .code_hash(code_hash.pack())
498 .hash_type(ScriptHashType::Data.into())
499 .build()
500 }
501
502 pub fn from_pubkey(format: AddressFormat, pubkey: &Pubkey) -> Result<Address, String> {
503 if format != AddressFormat::P2ph {
504 return Err("Only support P2PH for now".to_owned());
505 }
506 let hash = H160::from_slice(&blake2b_256(pubkey.serialize())[0..20])
508 .expect("Generate hash(H160) from pubkey failed");
509 Ok(Address { format, hash })
510 }
511
512 pub fn from_lock_arg(bytes: &[u8]) -> Result<Address, String> {
513 let format = AddressFormat::P2ph;
514 let hash = H160::from_slice(bytes).map_err(|err| err.to_string())?;
515 Ok(Address { format, hash })
516 }
517
518 pub fn from_input(network: NetworkType, input: &str) -> Result<Address, String> {
519 let (hrp, data, _variant) = bech32::decode(input).map_err(|err| err.to_string())?;
520 if NetworkType::from_prefix(&hrp)
521 .filter(|input_network| input_network == &network)
522 .is_none()
523 {
524 return Err(format!("Invalid hrp({}) for {}", hrp, network));
525 }
526 let data = convert_bits(&data, 5, 8, false).unwrap();
527 if data.len() != 25 {
528 return Err(format!("Invalid input data length {}", data.len()));
529 }
530 let format = AddressFormat::from_bytes(&data[0..5])?;
531 let hash = H160::from_slice(&data[5..25]).map_err(|err| err.to_string())?;
532 Ok(Address { format, hash })
533 }
534
535 pub fn display_with_prefix(&self, network: NetworkType) -> String {
536 let hrp = network.to_prefix();
537 let mut data = [0; 25];
538 let format_data = self.format.to_bytes().expect("Invalid address format");
539 data[0..5].copy_from_slice(&format_data[0..5]);
540 data[5..25].copy_from_slice(self.hash.as_bytes());
541 bech32::encode(hrp, data.to_base32(), bech32::Variant::Bech32)
542 .unwrap_or_else(|_| panic!("Encode address failed: hash={:?}", self.hash))
543 }
544
545 #[allow(clippy::inherent_to_string)]
546 #[deprecated(
547 since = "0.25.0",
548 note = "Name conflicts with the inherent to_string method. Use display_with_prefix instead."
549 )]
550 pub fn to_string(&self, network: NetworkType) -> String {
551 self.display_with_prefix(network)
552 }
553 }
554}