invoice/
address.rs

1// Modern, minimalistic & standard-compliant cold wallet library.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Written in 2020-2024 by
6//     Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
7//
8// Copyright (C) 2020-2024 LNP/BP Standards Association. All rights reserved.
9// Copyright (C) 2020-2024 Dr Maxim Orlovsky. All rights reserved.
10//
11// Licensed under the Apache License, Version 2.0 (the "License");
12// you may not use this file except in compliance with the License.
13// You may obtain a copy of the License at
14//
15//     http://www.apache.org/licenses/LICENSE-2.0
16//
17// Unless required by applicable law or agreed to in writing, software
18// distributed under the License is distributed on an "AS IS" BASIS,
19// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20// See the License for the specific language governing permissions and
21// limitations under the License.
22
23//! Address-related types for detailed payload analysis and memory-efficient
24//! processing.
25
26use std::fmt::{self, Debug, Display, Formatter};
27use std::str::FromStr;
28
29use bc::{
30    InvalidPubkey, OutputPk, PubkeyHash, ScriptHash, ScriptPubkey, WPubkeyHash, WScriptHash,
31    WitnessVer,
32};
33use bech32::u5;
34
35use crate::base58;
36
37/// Mainnet (bitcoin) pubkey address prefix.
38pub const PUBKEY_ADDRESS_PREFIX_MAIN: u8 = 0; // 0x00
39/// Mainnet (bitcoin) script address prefix.
40pub const SCRIPT_ADDRESS_PREFIX_MAIN: u8 = 5; // 0x05
41/// Test (tesnet, signet, regtest) pubkey address prefix.
42pub const PUBKEY_ADDRESS_PREFIX_TEST: u8 = 111; // 0x6f
43/// Test (tesnet, signet, regtest) script address prefix.
44pub const SCRIPT_ADDRESS_PREFIX_TEST: u8 = 196; // 0xc4
45
46/// Errors creating address from scriptPubkey.
47#[derive(Clone, Eq, PartialEq, Debug, Display, Error)]
48#[display(doc_comments)]
49pub enum AddressError {
50    /// scriptPubkey contains invalid BIP340 output pubkey.
51    InvalidTaprootKey,
52    /// scriptPubkey can't be represented with any known address standard.
53    UnsupportedScriptPubkey,
54}
55
56/// Errors parsing address strings.
57#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
58#[display(doc_comments)]
59pub enum AddressParseError {
60    /// wrong Base58 encoding of address data - {0}
61    #[from]
62    Base58(base58::Error),
63
64    /// wrong Bech32 encoding of address data - {0}
65    #[from]
66    Bech32(bech32::Error),
67
68    /// proprietary address has an invalid version code {0:#04x}.
69    InvalidAddressVersion(u8),
70
71    /// segwit address has an invalid witness version {0:#04x}.
72    InvalidWitnessVersion(u8),
73
74    /// unsupported future taproot version in address `{1}` detected by a length of {0}.
75    FutureTaprootVersion(usize, String),
76
77    /// address has an unsupported future witness version {0}.
78    FutureWitnessVersion(WitnessVer),
79
80    /// address has an invalid Bech32 variant {0:?}.
81    InvalidBech32Variant(bech32::Variant),
82
83    /// unrecognized address format in '{0}'.
84    UnrecognizableFormat(String),
85
86    /// wrong BIP340 public key
87    #[from(InvalidPubkey<32>)]
88    WrongPublicKeyData,
89
90    /// unrecognized address format string; must be one of `P2PKH`, `P2SH`,
91    /// `P2WPKH`, `P2WSH`, `P2TR`
92    UnrecognizedAddressType,
93}
94
95#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
96pub struct Address {
97    /// Address payload (see [`AddressPayload`]).
98    pub payload: AddressPayload,
99
100    /// A type of the network used by the address
101    pub network: AddressNetwork,
102}
103
104impl Address {
105    pub fn new(payload: AddressPayload, network: AddressNetwork) -> Self {
106        Address { payload, network }
107    }
108
109    /// Constructs compatible address for a given `scriptPubkey`.
110    /// Returns `None` if the uncompressed key is provided or `scriptPubkey`
111    /// can't be represented as an address.
112    pub fn with(
113        script: &ScriptPubkey,
114        network: impl Into<AddressNetwork>,
115    ) -> Result<Self, AddressError> {
116        let payload = AddressPayload::from_script(script)?;
117        Ok(Address {
118            payload,
119            network: network.into(),
120        })
121    }
122
123    /// Returns script corresponding to the given address.
124    pub fn script_pubkey(self) -> ScriptPubkey { self.payload.script_pubkey() }
125
126    /// Returns if the address is testnet-, signet- or regtest-specific.
127    pub fn is_testnet(self) -> bool { self.network != AddressNetwork::Mainnet }
128
129    /// Detects address type.
130    pub fn address_type(self) -> AddressType { self.payload.address_type() }
131}
132
133impl Display for Address {
134    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
135        let (version, variant, prog) = match self.payload {
136            AddressPayload::Pkh(PubkeyHash(hash)) | AddressPayload::Sh(ScriptHash(hash)) => {
137                let mut prefixed = [0; 21];
138                prefixed[0] = match (self.payload, self.network) {
139                    (AddressPayload::Pkh(_), AddressNetwork::Mainnet) => PUBKEY_ADDRESS_PREFIX_MAIN,
140                    (AddressPayload::Sh(_), AddressNetwork::Mainnet) => SCRIPT_ADDRESS_PREFIX_MAIN,
141                    (AddressPayload::Pkh(_), _) => PUBKEY_ADDRESS_PREFIX_TEST,
142                    (AddressPayload::Sh(_), _) => SCRIPT_ADDRESS_PREFIX_TEST,
143                    _ => unreachable!(),
144                };
145                prefixed[1..].copy_from_slice(hash.as_ref());
146                return base58::encode_check_to_fmt(f, &prefixed[..]);
147            }
148            AddressPayload::Wpkh(hash) => {
149                (WitnessVer::V0, bech32::Variant::Bech32, Box::new(hash) as Box<dyn AsRef<[u8]>>)
150            }
151            AddressPayload::Wsh(hash) => {
152                (WitnessVer::V0, bech32::Variant::Bech32, Box::new(hash) as Box<dyn AsRef<[u8]>>)
153            }
154            AddressPayload::Tr(pk) => (
155                WitnessVer::V1,
156                bech32::Variant::Bech32m,
157                Box::new(pk.to_byte_array()) as Box<dyn AsRef<[u8]>>,
158            ),
159        };
160
161        struct UpperWriter<W: fmt::Write>(W);
162        impl<W: fmt::Write> fmt::Write for UpperWriter<W> {
163            fn write_str(&mut self, s: &str) -> fmt::Result {
164                for c in s.chars() {
165                    self.0.write_char(c.to_ascii_uppercase())?;
166                }
167                Ok(())
168            }
169        }
170
171        let mut upper_writer;
172        let writer = if f.alternate() {
173            upper_writer = UpperWriter(f);
174            &mut upper_writer as &mut dyn fmt::Write
175        } else {
176            f as &mut dyn fmt::Write
177        };
178        let mut bech32_writer =
179            bech32::Bech32Writer::new(self.network.bech32_hrp(), variant, writer)?;
180        let ver_u5 = u5::try_from_u8(version.version_no()).expect("witness version <= 16");
181        bech32::WriteBase32::write_u5(&mut bech32_writer, ver_u5)?;
182        bech32::ToBase32::write_base32(&prog.as_ref(), &mut bech32_writer)
183    }
184}
185
186impl FromStr for Address {
187    type Err = AddressParseError;
188
189    fn from_str(s: &str) -> Result<Self, Self::Err> {
190        let parse_base58 = || -> Result<Self, Self::Err> {
191            if s.len() > 50 {
192                return Err(AddressParseError::Base58(base58::Error::InvalidLength(
193                    s.len() * 11 / 15,
194                )));
195            }
196            let data = base58::decode_check(s)?;
197            if data.len() != 21 {
198                return Err(AddressParseError::Base58(base58::Error::InvalidLength(data.len())));
199            }
200
201            let network = match data[0] {
202                PUBKEY_ADDRESS_PREFIX_MAIN | SCRIPT_ADDRESS_PREFIX_MAIN => AddressNetwork::Mainnet,
203                PUBKEY_ADDRESS_PREFIX_TEST | SCRIPT_ADDRESS_PREFIX_TEST => AddressNetwork::Testnet,
204                x => return Err(AddressParseError::InvalidAddressVersion(x)),
205            };
206
207            let mut hash = [0u8; 20];
208            hash.copy_from_slice(&data[1..]);
209            let payload = match data[0] {
210                PUBKEY_ADDRESS_PREFIX_MAIN | PUBKEY_ADDRESS_PREFIX_TEST => {
211                    AddressPayload::Pkh(PubkeyHash::from(hash))
212                }
213                SCRIPT_ADDRESS_PREFIX_MAIN | SCRIPT_ADDRESS_PREFIX_TEST => {
214                    AddressPayload::Sh(ScriptHash::from(hash))
215                }
216                _ => unreachable!(),
217            };
218
219            Ok(Address::new(payload, network))
220        };
221
222        let parse_bech32 = |hri: String,
223                            payload: Vec<bech32::u5>,
224                            variant: bech32::Variant|
225         -> Result<Self, Self::Err> {
226            let network = match hri.as_str() {
227                "bc" | "BC" => AddressNetwork::Mainnet,
228                "tb" | "TB" => AddressNetwork::Testnet,
229                "bcrt" | "BCRT" => AddressNetwork::Regtest,
230                _ => return parse_base58(),
231            };
232            let (v, p5) = payload.split_at(1);
233            let wv = v[0].to_u8();
234            let version = WitnessVer::from_version_no(wv).map_err(|err| {
235                eprintln!("{err}");
236                AddressParseError::InvalidWitnessVersion(wv)
237            })?;
238            let program: Vec<u8> = bech32::FromBase32::from_base32(p5)?;
239            let payload = match (version, variant) {
240                (WitnessVer::V0, bech32::Variant::Bech32) if program.len() == 20 => {
241                    let mut hash = [0u8; 20];
242                    hash.copy_from_slice(&program);
243                    AddressPayload::Wpkh(hash.into())
244                }
245                (WitnessVer::V0, bech32::Variant::Bech32) if program.len() == 32 => {
246                    let mut hash = [0u8; 32];
247                    hash.copy_from_slice(&program);
248                    AddressPayload::Wsh(hash.into())
249                }
250                (WitnessVer::V1, bech32::Variant::Bech32m) if program.len() == 32 => {
251                    let mut key = [0u8; 32];
252                    key.copy_from_slice(&program);
253                    let pk = OutputPk::from_byte_array(key)?;
254                    AddressPayload::Tr(pk)
255                }
256
257                (WitnessVer::V1, bech32::Variant::Bech32m) => {
258                    return Err(AddressParseError::FutureTaprootVersion(
259                        program.len(),
260                        s.to_owned(),
261                    ));
262                }
263
264                (WitnessVer::V0 | WitnessVer::V1, wrong) => {
265                    return Err(AddressParseError::InvalidBech32Variant(wrong));
266                }
267
268                (future, _) => return Err(AddressParseError::FutureWitnessVersion(future)),
269            };
270            Ok(Address::new(payload, network))
271        };
272
273        match bech32::decode(s) {
274            Ok((hri, payload, variant)) => parse_bech32(hri, payload, variant),
275            Err(_) => {
276                parse_base58().map_err(|_| AddressParseError::UnrecognizableFormat(s.to_owned()))
277            }
278        }
279    }
280}
281
282/// Internal address content. Consists of serialized hashes or x-only key value.
283#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
284#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
285#[cfg_attr(
286    feature = "strict_encoding",
287    derive(StrictType, StrictDumb, StrictEncode, StrictDecode),
288    // This type should not be included in any library
289    strict_type(lib = "_", tags = custom, dumb = Self::Pkh(strict_dumb!()))
290)]
291pub enum AddressPayload {
292    /// P2PKH payload.
293    #[from]
294    #[cfg_attr(feature = "strict_encoding", strict_type(tag = 1))]
295    Pkh(PubkeyHash),
296
297    /// P2SH and SegWit nested (proprietary) P2WPKH/WSH-in-P2SH payloads.
298    #[from]
299    #[cfg_attr(feature = "strict_encoding", strict_type(tag = 2))]
300    Sh(ScriptHash),
301
302    /// P2WPKH payload.
303    #[from]
304    #[cfg_attr(feature = "strict_encoding", strict_type(tag = 0x10))]
305    Wpkh(WPubkeyHash),
306
307    /// P2WSH payload.
308    #[from]
309    #[cfg_attr(feature = "strict_encoding", strict_type(tag = 0x11))]
310    Wsh(WScriptHash),
311
312    /// P2TR payload.
313    #[from]
314    #[cfg_attr(feature = "strict_encoding", strict_type(tag = 0x20))]
315    Tr(OutputPk),
316}
317
318impl AddressPayload {
319    /// Constructs [`Address`] from the payload.
320    pub fn into_address(self, network: AddressNetwork) -> Address {
321        Address {
322            payload: self,
323            network,
324        }
325    }
326
327    /// Constructs payload from a given `scriptPubkey`. Fails on future
328    /// (post-taproot) witness types with `None`.
329    pub fn from_script(script: &ScriptPubkey) -> Result<Self, AddressError> {
330        Ok(if script.is_p2pkh() {
331            let mut bytes = [0u8; 20];
332            bytes.copy_from_slice(&script[3..23]);
333            AddressPayload::Pkh(PubkeyHash::from(bytes))
334        } else if script.is_p2sh() {
335            let mut bytes = [0u8; 20];
336            bytes.copy_from_slice(&script[2..22]);
337            AddressPayload::Sh(ScriptHash::from(bytes))
338        } else if script.is_p2wpkh() {
339            let mut bytes = [0u8; 20];
340            bytes.copy_from_slice(&script[2..]);
341            AddressPayload::Wpkh(WPubkeyHash::from(bytes))
342        } else if script.is_p2wsh() {
343            let mut bytes = [0u8; 32];
344            bytes.copy_from_slice(&script[2..]);
345            AddressPayload::Wsh(WScriptHash::from(bytes))
346        } else if script.is_p2tr() {
347            let mut bytes = [0u8; 32];
348            bytes.copy_from_slice(&script[2..]);
349            AddressPayload::Tr(
350                OutputPk::from_byte_array(bytes).map_err(|_| AddressError::InvalidTaprootKey)?,
351            )
352        } else {
353            return Err(AddressError::UnsupportedScriptPubkey);
354        })
355    }
356
357    /// Returns script corresponding to the given address.
358    pub fn script_pubkey(self) -> ScriptPubkey {
359        match self {
360            AddressPayload::Pkh(hash) => ScriptPubkey::p2pkh(hash),
361            AddressPayload::Sh(hash) => ScriptPubkey::p2sh(hash),
362            AddressPayload::Wpkh(hash) => ScriptPubkey::p2wpkh(hash),
363            AddressPayload::Wsh(hash) => ScriptPubkey::p2wsh(hash),
364            AddressPayload::Tr(output_key) => ScriptPubkey::p2tr_tweaked(output_key),
365        }
366    }
367
368    /// Detects address type.
369    pub fn address_type(self) -> AddressType {
370        match self {
371            AddressPayload::Pkh(_) => AddressType::P2pkh,
372            AddressPayload::Sh(_) => AddressType::P2sh,
373            AddressPayload::Wpkh(_) => AddressType::P2wpkh,
374            AddressPayload::Wsh(_) => AddressType::P2wsh,
375            AddressPayload::Tr(_) => AddressType::P2tr,
376        }
377    }
378}
379
380impl From<AddressPayload> for ScriptPubkey {
381    fn from(ap: AddressPayload) -> Self { ap.script_pubkey() }
382}
383
384/// Address type
385#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
386pub enum AddressType {
387    /// Pay-to-public key hash
388    #[display("P2PKH")]
389    P2pkh,
390
391    /// Pay-to-script hash
392    #[display("P2SH")]
393    P2sh,
394
395    /// Pay-to-witness public key hash
396    #[display("P2WPKH")]
397    P2wpkh,
398
399    /// Pay-to-witness script hash
400    #[display("P2WSH")]
401    P2wsh,
402
403    /// Pay-to-taproot
404    #[display("P2TR")]
405    P2tr,
406}
407
408impl AddressType {
409    /// Returns witness version used by the address format.
410    /// Returns `None` for pre-SegWit address formats.
411    pub fn witness_version(self) -> Option<WitnessVer> {
412        match self {
413            AddressType::P2pkh => None,
414            AddressType::P2sh => None,
415            AddressType::P2wpkh | AddressType::P2wsh => Some(WitnessVer::V0),
416            AddressType::P2tr => Some(WitnessVer::V1),
417        }
418    }
419}
420
421impl FromStr for AddressType {
422    type Err = AddressParseError;
423
424    fn from_str(s: &str) -> Result<Self, Self::Err> {
425        #[allow(clippy::match_str_case_mismatch)]
426        Ok(match s.to_uppercase().as_str() {
427            "P2PKH" => AddressType::P2pkh,
428            "P2SH" => AddressType::P2sh,
429            "P2WPKH" => AddressType::P2wpkh,
430            "P2WSH" => AddressType::P2wsh,
431            "P2TR" => AddressType::P2tr,
432            _ => return Err(AddressParseError::UnrecognizedAddressType),
433        })
434    }
435}
436
437/// Bitcoin network used by the address
438#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
439pub enum AddressNetwork {
440    /// Bitcoin mainnet
441    Mainnet,
442
443    /// Bitcoin testnet and signet
444    Testnet,
445
446    /// Bitcoin regtest networks
447    Regtest,
448}
449
450impl AddressNetwork {
451    /// Detects whether the network is a kind of test network (testnet, signet,
452    /// regtest).
453    pub fn is_testnet(self) -> bool { self != Self::Mainnet }
454
455    pub fn bech32_hrp(self) -> &'static str {
456        match self {
457            AddressNetwork::Mainnet => "bc",
458            AddressNetwork::Testnet => "tb",
459            AddressNetwork::Regtest => "bcrt",
460        }
461    }
462}
463
464#[cfg(feature = "serde")]
465mod _serde {
466    use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
467
468    use super::*;
469
470    impl Serialize for Address {
471        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
472        where S: Serializer {
473            serializer.serialize_str(&self.to_string())
474        }
475    }
476
477    impl<'de> Deserialize<'de> for Address {
478        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
479        where D: Deserializer<'de> {
480            let s = String::deserialize(deserializer)?;
481            Address::from_str(&s).map_err(|err| {
482                de::Error::custom(format!(
483                    "invalid xpub specification string representation; {err}"
484                ))
485            })
486        }
487    }
488}
489
490#[cfg(test)]
491mod test {
492    use super::*;
493
494    #[test]
495    fn display_from_str() {
496        let b32 = "tb1p5kgdjdf99vfa2xwufd2cx2qru468z79s2arn3jf5feg95d9m62gqzpnjjk";
497        assert_eq!(Address::from_str(b32).unwrap().to_string(), b32);
498    }
499}