ckb_client/
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::{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 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, 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    /// Get the code hash of an address
139    ///
140    /// # Panics
141    ///
142    /// When current addres is short format anyone-can-pay address, and the
143    /// network type is not `Mainnet` or `Testnet` this function will panic.
144    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        // Serialize pubkey as compressed format
168        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            // payload = 0x00 | code_hash | hash_type | args
182            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                // payload = 0x01 | code_hash_index | args
194                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                    // short address always use bech32
200                    (data, bech32::Variant::Bech32)
201                }
202                AddressPayload::Full {
203                    code_hash, args, ..
204                } => {
205                    // payload = 0x02/0x04 | code_hash | args
206                    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            // NOTE: anoney-can-pay script args can larger than 20 bytes, here
271            // args.len() != 20 is not a short format address, see RFC21 for
272            // more details.
273            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    /// The network type of current address
302    pub fn network(&self) -> NetworkType {
303        self.network
304    }
305    /// The address payload
306    pub fn payload(&self) -> &AddressPayload {
307        &self.payload
308    }
309    /// If true the address is ckb2021 format
310    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            // payload = 0x01 | code_hash_index | args
364            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            // payload = 0x02/0x04 | code_hash | args
381            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            // payload = 0x00 | code_hash | hash_type | args
409            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    // \x01 is the P2PH version
444    const P2PH_MARK: &[u8] = b"\x01P2PH";
445
446    #[derive(Hash, Eq, PartialEq, Debug, Clone, Copy, Serialize, Deserialize, Default)]
447    pub enum AddressFormat {
448        // SECP256K1 algorithm	PK
449        #[allow(dead_code)]
450        Sp2k,
451        // SECP256R1 algorithm	PK
452        #[allow(dead_code)]
453        Sp2r,
454        // SECP256K1 + blake160	blake160(pk)
455        #[default]
456        P2ph,
457        // Alias of SP2K	PK
458        #[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            // Serialize pubkey as compressed format
507            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}