bitcoin_bech32/
lib.rs

1// Copyright (c) 2017 Clark Moody
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE.
20
21//! Encoding and decoding Bech32 Bitcoin Segwit Addresses
22//!
23//! Encoding and decoding for Bitcoin Segregated Witness addresses. Bech32 is an
24//! encoding scheme described in [BIP-0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki),
25//! and segregated witness addresses encoded by Bech32 simply combine a coin-specific
26//! human-readable part with the data of the witness program as the Bech32 data
27//! payload.
28//!
29//! # Examples
30//!
31//! ```rust
32//! use bitcoin_bech32::{WitnessProgram, u5};
33//! use bitcoin_bech32::constants::Network;
34//!
35//! let witness_program = WitnessProgram::new(
36//!     u5::try_from_u8(0).unwrap(),
37//!     vec![
38//!         0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62,
39//!         0x21, 0xb2, 0xa1, 0x87, 0x90, 0x5e, 0x52, 0x66,
40//!         0x36, 0x2b, 0x99, 0xd5, 0xe9, 0x1c, 0x6c, 0xe2,
41//!         0x4d, 0x16, 0x5d, 0xab, 0x93, 0xe8, 0x64, 0x33],
42//!     Network::Testnet
43//! ).unwrap();
44//!
45//! let address = witness_program.to_address();
46//! assert_eq!(address,
47//!     "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy".to_string());
48//!
49//! let decoded = WitnessProgram::from_address(&address).unwrap();
50//! assert_eq!(decoded, witness_program);
51//! ```
52
53// Allow trait objects without dyn on nightly and make 1.22 ignore the unknown lint
54#![allow(unknown_lints)]
55#![allow(bare_trait_objects)]
56#![deny(missing_docs)]
57#![deny(non_upper_case_globals)]
58#![deny(non_camel_case_types)]
59#![deny(non_snake_case)]
60#![deny(unused_mut)]
61#![cfg_attr(feature = "strict", deny(warnings))]
62#![cfg_attr(not(feature = "std"), no_std)]
63
64#[cfg_attr(not(feature = "std"), macro_use)]
65#[cfg(not(feature = "std"))]
66extern crate alloc;
67
68#[cfg(not(feature = "std"))]
69pub(crate) mod imports {
70    pub use alloc::string::String;
71    pub use alloc::string::ToString;
72    pub use alloc::vec::Vec;
73    pub use core::fmt;
74    pub use core::str::FromStr;
75}
76#[cfg(feature = "std")]
77pub(crate) mod imports {
78    pub use std::str::FromStr;
79    pub use std::string::ToString;
80    pub use std::{error, fmt};
81}
82
83use imports::*;
84
85extern crate bech32;
86pub use bech32::u5;
87use bech32::{decode, encode, FromBase32, ToBase32, Variant};
88
89pub mod constants;
90use constants::Network;
91
92/// Witness version and program data
93#[derive(PartialEq, Eq, Debug, Clone, PartialOrd, Ord, Hash)]
94pub struct WitnessProgram {
95    /// Witness program version
96    version: u5,
97    /// Witness program content
98    program: Vec<u8>,
99    /// Cryptocurrency network
100    network: Network,
101    /// Cached bech32 representation of the witness program
102    bech32: String,
103}
104
105impl WitnessProgram {
106    /// Construct a new WitnessProgram given the constituent version, witness program and network version
107    pub fn new(version: u5, program: Vec<u8>, network: Network) -> Result<WitnessProgram, Error> {
108        // Compute bech32
109        let hrp = constants::hrp(&network);
110        let mut b32_data: Vec<u5> = vec![version];
111        let p5 = program.to_base32();
112        b32_data.extend_from_slice(&p5);
113        let variant = program_version_to_variant(version).ok_or(Error::InvalidScriptVersion)?;
114        let bech32 = encode(&hrp, b32_data, variant)?;
115
116        // Create return object
117        let ret = WitnessProgram {
118            version,
119            program,
120            network,
121            bech32,
122        };
123
124        // Verify that the program is valid
125        ret.validate()?;
126        Ok(ret)
127    }
128
129    /// Converts a Witness Program to a SegWit Address
130    pub fn to_address(&self) -> String {
131        self.to_string()
132    }
133
134    /// Decodes a segwit address into a Witness Program
135    ///
136    /// Verifies that the `address` contains a known human-readable part
137    /// `hrp` and decodes as proper Bech32-encoded string. Allowed values of
138    /// the human-readable part correspond to the defined types in `constants`
139    pub fn from_address(address: &str) -> Result<WitnessProgram, Error> {
140        WitnessProgram::from_str(address)
141    }
142
143    /// Converts a `WitnessProgram` to a script public key
144    ///
145    /// The format for the output is
146    /// `[version, program length, <program>]`
147    pub fn to_scriptpubkey(&self) -> Vec<u8> {
148        let mut pubkey: Vec<u8> = Vec::new();
149        let mut v: u8 = self.version.into();
150        if v > 0 {
151            v += 0x50;
152        }
153        pubkey.push(v);
154        pubkey.push(self.program.len() as u8);
155        pubkey.extend_from_slice(&self.program);
156        pubkey
157    }
158
159    /// Extracts a WitnessProgram out of a provided script public key
160    pub fn from_scriptpubkey(pubkey: &[u8], network: Network) -> Result<WitnessProgram, Error> {
161        // We need a version byte and a program length byte, with a program at
162        // least 2 bytes long.
163        if pubkey.len() < 4 {
164            return Err(Error::ScriptPubkeyTooShort);
165        }
166        let proglen: usize = pubkey[1] as usize;
167        // Check that program length byte is consistent with pubkey length
168        if pubkey.len() != 2 + proglen {
169            return Err(Error::ScriptPubkeyInvalidLength);
170        }
171        // Process script version
172        let mut v: u8 = pubkey[0];
173        if v > 0x50 {
174            v -= 0x50;
175        }
176
177        let v = u5::try_from_u8(v).expect("range is already guaranteed by code above");
178        let program = &pubkey[2..];
179
180        WitnessProgram::new(v, program.to_vec(), network)
181    }
182
183    /// Validates the WitnessProgram against version and length constraints
184    pub fn validate(&self) -> Result<(), Error> {
185        if self.version.to_u8() > 16 {
186            // Invalid script version
187            return Err(Error::InvalidScriptVersion);
188        }
189        if self.program.len() < 2 || self.program.len() > 40 {
190            return Err(Error::InvalidLength);
191        }
192        // Check proper script length
193        if self.version.to_u8() == 0 && self.program.len() != 20 && self.program.len() != 32 {
194            return Err(Error::InvalidVersionLength);
195        }
196        Ok(())
197    }
198
199    /// Witness program version
200    pub fn version(&self) -> u5 {
201        self.version
202    }
203
204    /// Witness program serialized as 8-bit bytes
205    pub fn program(&self) -> &[u8] {
206        &self.program
207    }
208
209    /// Which network this witness program is intended to be run on
210    pub fn network(&self) -> Network {
211        self.network
212    }
213}
214
215impl ToString for WitnessProgram {
216    fn to_string(&self) -> String {
217        self.bech32.to_string()
218    }
219}
220
221impl FromStr for WitnessProgram {
222    type Err = Error;
223
224    fn from_str(s: &str) -> Result<WitnessProgram, Error> {
225        let (hrp, data, variant) = decode(s)?;
226        let network_classified = match constants::classify(&hrp) {
227            Some(nc) => nc,
228            None => return Err(Error::InvalidHumanReadablePart),
229        };
230        if data.is_empty() || data.len() > 65 {
231            return Err(Error::Bech32(bech32::Error::InvalidLength));
232        }
233        // Get the script version and program (converted from 5-bit to 8-bit)
234        let (version, program) = {
235            let (v, p5) = data.split_at(1);
236            let program = Vec::from_base32(p5)?;
237            (v[0], program)
238        };
239        let wp = WitnessProgram {
240            version,
241            program,
242            network: network_classified,
243            bech32: s.to_string(),
244        };
245        wp.validate()?;
246        if let Some(version_variant) = program_version_to_variant(version) {
247            if version_variant != variant {
248                return Err(Error::InvalidEncoding);
249            }
250        }
251        Ok(wp)
252    }
253}
254
255fn program_version_to_variant(version: u5) -> Option<Variant> {
256    match version.to_u8() {
257        0 => Some(Variant::Bech32),
258        1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 => {
259            Some(Variant::Bech32m)
260        }
261        _ => None,
262    }
263}
264
265/// Error types for witness programs
266///
267/// BIP141 specifies Segregated Witness and defines valid program lengths
268/// for Version 0 scripts. Script version is also limited to values 0-16.
269#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
270pub enum Error {
271    /// Some Bech32 conversion error
272    Bech32(bech32::Error),
273    /// The human-readable part is invalid (must be "bc" or "tb")
274    InvalidHumanReadablePart,
275    /// scriptpubkeys does not have enough data
276    ScriptPubkeyTooShort,
277    /// The provided length byte does not match the data
278    ScriptPubkeyInvalidLength,
279    /// Denotes that the WitnessProgram is too long or too short
280    ///
281    /// Programs must be between 2 and 40 bytes
282    InvalidLength,
283    /// Given the program version, the length is invalid
284    ///
285    /// Version 0 scripts must be either 20 or 32 bytes
286    InvalidVersionLength,
287    /// Script version must be 0 to 16 inclusive
288    InvalidScriptVersion,
289    /// Improper encoding used for address
290    ///
291    /// Witness version 0 addresses must use Bech32 encoding, and all other
292    /// versions must use Bech32m
293    InvalidEncoding,
294}
295
296impl From<bech32::Error> for Error {
297    fn from(e: bech32::Error) -> Error {
298        Error::Bech32(e)
299    }
300}
301
302impl fmt::Display for Error {
303    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
304        match *self {
305            Error::Bech32(ref e) => write!(f, "{}", e),
306            Error::InvalidHumanReadablePart => write!(f, "invalid human-readable part"),
307            Error::ScriptPubkeyTooShort => write!(f, "scriptpubkey too short"),
308            Error::ScriptPubkeyInvalidLength => write!(f, "scriptpubkey length mismatch"),
309            Error::InvalidLength => write!(f, "invalid length"),
310            Error::InvalidVersionLength => write!(f, "program length incompatible with version"),
311            Error::InvalidScriptVersion => write!(f, "invalid script version"),
312            Error::InvalidEncoding => write!(f, "invalid Bech32 encoding"),
313        }
314    }
315}
316
317#[cfg(feature = "std")]
318impl error::Error for Error {
319    fn description(&self) -> &str {
320        match *self {
321            Error::Bech32(_) => "Bech32 error",
322            Error::InvalidHumanReadablePart => "invalid human-readable part",
323            Error::ScriptPubkeyTooShort => "scriptpubkey too short",
324            Error::ScriptPubkeyInvalidLength => "scriptpubkey length mismatch",
325            Error::InvalidLength => "invalid length",
326            Error::InvalidVersionLength => "program length incompatible with version",
327            Error::InvalidScriptVersion => "invalid script version",
328            Error::InvalidEncoding => "invalid Bech32 encoding",
329        }
330    }
331
332    fn cause(&self) -> Option<&std::error::Error> {
333        match *self {
334            Error::Bech32(ref e) => Some(e),
335            _ => None,
336        }
337    }
338}
339
340#[cfg(test)]
341mod tests {
342    use bech32;
343    use constants::Network;
344    use *;
345
346    #[test]
347    fn valid_address() {
348        let pairs: Vec<(&str, Vec<u8>, Network)> = vec![
349            (
350                "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4",
351                vec![
352                    0x00, 0x14, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c,
353                    0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
354                ],
355                Network::Bitcoin,
356            ),
357            (
358                "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",
359                vec![
360                    0x00, 0x20, 0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, 0x04, 0xbd, 0x19,
361                    0x20, 0x33, 0x56, 0xda, 0x13, 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1,
362                    0xb8, 0xc6, 0x32, 0x96, 0x04, 0x90, 0x32, 0x62,
363                ],
364                Network::Testnet,
365            ),
366            (
367                "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y",
368                vec![
369                    0x51, 0x28, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c,
370                    0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6, 0x75, 0x1e, 0x76, 0xe8,
371                    0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1,
372                    0x43, 0x3b, 0xd6,
373                ],
374                Network::Bitcoin,
375            ),
376            (
377                "BC1SW50QGDZ25J",
378                vec![0x60, 0x02, 0x75, 0x1e],
379                Network::Bitcoin,
380            ),
381            (
382                "bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs",
383                vec![
384                    0x52, 0x10, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c,
385                    0x45, 0xd1, 0xb3, 0xa3, 0x23,
386                ],
387                Network::Bitcoin,
388            ),
389            (
390                "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy",
391                vec![
392                    0x00, 0x20, 0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, 0x21, 0xb2, 0xa1,
393                    0x87, 0x90, 0x5e, 0x52, 0x66, 0x36, 0x2b, 0x99, 0xd5, 0xe9, 0x1c, 0x6c, 0xe2,
394                    0x4d, 0x16, 0x5d, 0xab, 0x93, 0xe8, 0x64, 0x33,
395                ],
396                Network::Testnet,
397            ),
398            (
399                "bcrt1qn3h68k2u0rr49skx05qw7veynpf4lfppd2demt",
400                vec![
401                    0x00, 0x14, 0x9c, 0x6f, 0xa3, 0xd9, 0x5c, 0x78, 0xc7, 0x52, 0xc2, 0xc6, 0x7d,
402                    0x00, 0xef, 0x33, 0x24, 0x98, 0x53, 0x5f, 0xa4, 0x21,
403                ],
404                Network::Regtest,
405            ),
406            (
407                "tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c",
408                vec![
409                    0x51, 0x20, 0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, 0x21, 0xb2, 0xa1,
410                    0x87, 0x90, 0x5e, 0x52, 0x66, 0x36, 0x2b, 0x99, 0xd5, 0xe9, 0x1c, 0x6c, 0xe2,
411                    0x4d, 0x16, 0x5d, 0xab, 0x93, 0xe8, 0x64, 0x33,
412                ],
413                Network::Testnet,
414            ),
415            (
416                "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0",
417                vec![
418                    0x51, 0x20, 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62,
419                    0x95, 0xce, 0x87, 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9,
420                    0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98,
421                ],
422                Network::Bitcoin,
423            ),
424        ];
425        for p in pairs {
426            let (address, scriptpubkey, network) = p;
427            let version = if scriptpubkey[0] == 0 {
428                0
429            } else {
430                scriptpubkey[0] - 0x50
431            };
432            let prog = match WitnessProgram::from_address(&address) {
433                Ok(prog) => prog,
434                Err(e) => panic!("{}, {:?}", address, e),
435            };
436
437            let pubkey = prog.to_scriptpubkey();
438            assert_eq!(pubkey, scriptpubkey);
439
440            assert_eq!(prog.network(), network);
441            assert_eq!(prog.version().to_u8(), version);
442            assert_eq!(prog.program(), &scriptpubkey[2..]); // skip version and length
443
444            match WitnessProgram::from_scriptpubkey(&scriptpubkey, prog.network) {
445                Ok(prog) => {
446                    assert_eq!(
447                        prog.to_string().to_lowercase(),
448                        prog.to_string().to_lowercase()
449                    );
450                    let enc_address = prog.to_address();
451                    assert_eq!(address.to_lowercase(), enc_address.to_lowercase());
452                }
453                Err(e) => panic!("{:?}, {:?}", scriptpubkey, e),
454            }
455        }
456    }
457
458    #[test]
459    fn invalid_address() {
460        let pairs: Vec<(&str, Error)> = vec![
461            // BIP-0173 Invalid Addresses
462            (
463                "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty",
464                Error::InvalidHumanReadablePart,
465            ),
466            (
467                "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5",
468                Error::Bech32(bech32::Error::InvalidChecksum),
469            ),
470            (
471                "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2",
472                Error::InvalidScriptVersion,
473            ),
474            ("bc1rw5uspcuh", Error::InvalidLength),
475            (
476                "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90",
477                Error::Bech32(bech32::Error::InvalidLength),
478            ),
479            (
480                "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7",
481                Error::Bech32(bech32::Error::MixedCase),
482            ),
483            (
484                "tb1pw508d6qejxtdg4y5r3zarqfsj6c3",
485                Error::Bech32(bech32::Error::InvalidPadding),
486            ),
487            (
488                "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du",
489                Error::Bech32(bech32::Error::InvalidPadding),
490            ),
491            (
492                "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv",
493                Error::Bech32(bech32::Error::InvalidPadding),
494            ),
495            // BIP-0350 Invalid Addresses
496            (
497                "tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut",
498                Error::InvalidHumanReadablePart,
499            ),
500            (
501                "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd",
502                Error::InvalidEncoding,
503            ),
504            (
505                "tb1z0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqglt7rf",
506                Error::InvalidEncoding,
507            ),
508            (
509                "BC1S0XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ54WELL",
510                Error::InvalidEncoding,
511            ),
512            (
513                "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh",
514                Error::InvalidEncoding,
515            ),
516            (
517                "tb1q0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq24jc47",
518                Error::InvalidEncoding,
519            ),
520            (
521                "bc1p38j9r5y49hruaue7wxjce0updqjuyyx0kh56v8s25huc6995vvpql3jow4",
522                Error::Bech32(bech32::Error::InvalidChar('o')),
523            ),
524            (
525                "BC130XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ7ZWS8R",
526                Error::InvalidScriptVersion,
527            ),
528            ("bc1pw5dgrnzv", Error::InvalidLength),
529            (
530                "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav253zgeav",
531                Error::Bech32(bech32::Error::InvalidLength),
532            ),
533            (
534                "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P",
535                Error::InvalidVersionLength,
536            ),
537            (
538                "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq47Zagq",
539                Error::Bech32(bech32::Error::MixedCase),
540            ),
541            (
542                "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v07qwwzcrf",
543                Error::Bech32(bech32::Error::InvalidPadding),
544            ),
545            (
546                "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vpggkg4j",
547                Error::Bech32(bech32::Error::InvalidPadding),
548            ),
549            ("bc1gmk9yu", Error::Bech32(bech32::Error::InvalidLength)),
550        ];
551        for p in pairs {
552            let (address, desired_error) = p;
553            match WitnessProgram::from_address(&address) {
554                Ok(_) => panic!("Should be invalid: {:?}", address),
555                Err(e) => assert_eq!(e, desired_error, "{}", address),
556            }
557        }
558    }
559}