altcoin_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 altcoin_bech32::{WitnessProgram, u5};
33//! use altcoin_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
63extern crate bech32;
64pub use bech32::u5;
65use bech32::{decode, encode, FromBase32, ToBase32, Variant};
66
67use std::str::FromStr;
68use std::string::ToString;
69use std::{error, fmt};
70
71pub mod constants;
72use constants::Network;
73
74/// Witness version and program data
75#[derive(PartialEq, Eq, Debug, Clone, PartialOrd, Ord, Hash)]
76pub struct WitnessProgram {
77    /// Witness program version
78    version: u5,
79    /// Witness program content
80    program: Vec<u8>,
81    /// Cryptocurrency network
82    network: Network,
83    /// Cached bech32 representation of the witness program
84    bech32: String,
85}
86
87impl WitnessProgram {
88    /// Construct a new WitnessProgram given the constituent version, witness program and network version
89    pub fn new(version: u5, program: Vec<u8>, network: Network) -> Result<WitnessProgram, Error> {
90        // Compute bech32
91        let hrp = constants::hrp(&network);
92        let mut b32_data: Vec<u5> = vec![version];
93        let p5 = program.to_base32();
94        b32_data.extend_from_slice(&p5);
95        let variant = program_version_to_variant(version).ok_or(Error::InvalidScriptVersion)?;
96        let bech32 = encode(&hrp, b32_data, variant)?;
97
98        // Create return object
99        let ret = WitnessProgram {
100            version,
101            program,
102            network,
103            bech32,
104        };
105
106        // Verify that the program is valid
107        ret.validate()?;
108        Ok(ret)
109    }
110
111    /// Converts a Witness Program to a SegWit Address
112    pub fn to_address(&self) -> String {
113        self.to_string()
114    }
115
116    /// Decodes a segwit address into a Witness Program
117    ///
118    /// Verifies that the `address` contains a known human-readable part
119    /// `hrp` and decodes as proper Bech32-encoded string. Allowed values of
120    /// the human-readable part correspond to the defined types in `constants`
121    pub fn from_address(address: &str) -> Result<WitnessProgram, Error> {
122        WitnessProgram::from_str(address)
123    }
124
125    /// Converts a `WitnessProgram` to a script public key
126    ///
127    /// The format for the output is
128    /// `[version, program length, <program>]`
129    pub fn to_scriptpubkey(&self) -> Vec<u8> {
130        let mut pubkey: Vec<u8> = Vec::new();
131        let mut v: u8 = self.version.into();
132        if v > 0 {
133            v += 0x50;
134        }
135        pubkey.push(v);
136        pubkey.push(self.program.len() as u8);
137        pubkey.extend_from_slice(&self.program);
138        pubkey
139    }
140
141    /// Extracts a WitnessProgram out of a provided script public key
142    pub fn from_scriptpubkey(pubkey: &[u8], network: Network) -> Result<WitnessProgram, Error> {
143        // We need a version byte and a program length byte, with a program at
144        // least 2 bytes long.
145        if pubkey.len() < 4 {
146            return Err(Error::ScriptPubkeyTooShort);
147        }
148        let proglen: usize = pubkey[1] as usize;
149        // Check that program length byte is consistent with pubkey length
150        if pubkey.len() != 2 + proglen {
151            return Err(Error::ScriptPubkeyInvalidLength);
152        }
153        // Process script version
154        let mut v: u8 = pubkey[0];
155        if v > 0x50 {
156            v -= 0x50;
157        }
158
159        let v = u5::try_from_u8(v).expect("range is already guaranteed by code above");
160        let program = &pubkey[2..];
161
162        WitnessProgram::new(v, program.to_vec(), network)
163    }
164
165    /// Validates the WitnessProgram against version and length constraints
166    pub fn validate(&self) -> Result<(), Error> {
167        if self.version.to_u8() > 16 {
168            // Invalid script version
169            return Err(Error::InvalidScriptVersion);
170        }
171        if self.program.len() < 2 || self.program.len() > 40 {
172            return Err(Error::InvalidLength);
173        }
174        // Check proper script length
175        if self.version.to_u8() == 0 && self.program.len() != 20 && self.program.len() != 32 {
176            return Err(Error::InvalidVersionLength);
177        }
178        Ok(())
179    }
180
181    /// Witness program version
182    pub fn version(&self) -> u5 {
183        self.version
184    }
185
186    /// Witness program serialized as 8-bit bytes
187    pub fn program(&self) -> &[u8] {
188        &self.program
189    }
190
191    /// Which network this witness program is intended to be run on
192    pub fn network(&self) -> Network {
193        self.network
194    }
195}
196
197impl ToString for WitnessProgram {
198    fn to_string(&self) -> String {
199        self.bech32.to_string()
200    }
201}
202
203impl FromStr for WitnessProgram {
204    type Err = Error;
205
206    fn from_str(s: &str) -> Result<WitnessProgram, Error> {
207        let (hrp, data, variant) = decode(s)?;
208        let network_classified = match constants::classify(&hrp) {
209            Some(nc) => nc,
210            None => return Err(Error::InvalidHumanReadablePart),
211        };
212        if data.is_empty() || data.len() > 65 {
213            return Err(Error::Bech32(bech32::Error::InvalidLength));
214        }
215        // Get the script version and program (converted from 5-bit to 8-bit)
216        let (version, program) = {
217            let (v, p5) = data.split_at(1);
218            let program = Vec::from_base32(p5)?;
219            (v[0], program)
220        };
221        let wp = WitnessProgram {
222            version,
223            program,
224            network: network_classified,
225            bech32: s.to_string(),
226        };
227        wp.validate()?;
228        if let Some(version_variant) = program_version_to_variant(version) {
229            if version_variant != variant {
230                return Err(Error::InvalidEncoding);
231            }
232        }
233        Ok(wp)
234    }
235}
236
237fn program_version_to_variant(version: u5) -> Option<Variant> {
238    match version.to_u8() {
239        0 => Some(Variant::Bech32),
240        1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 => {
241            Some(Variant::Bech32m)
242        }
243        _ => None,
244    }
245}
246
247/// Error types for witness programs
248///
249/// BIP141 specifies Segregated Witness and defines valid program lengths
250/// for Version 0 scripts. Script version is also limited to values 0-16.
251#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
252pub enum Error {
253    /// Some Bech32 conversion error
254    Bech32(bech32::Error),
255    /// The human-readable part is invalid (must be "bc" or "tb")
256    InvalidHumanReadablePart,
257    /// scriptpubkeys does not have enough data
258    ScriptPubkeyTooShort,
259    /// The provided length byte does not match the data
260    ScriptPubkeyInvalidLength,
261    /// Denotes that the WitnessProgram is too long or too short
262    ///
263    /// Programs must be between 2 and 40 bytes
264    InvalidLength,
265    /// Given the program version, the length is invalid
266    ///
267    /// Version 0 scripts must be either 20 or 32 bytes
268    InvalidVersionLength,
269    /// Script version must be 0 to 16 inclusive
270    InvalidScriptVersion,
271    /// Improper encoding used for address
272    ///
273    /// Witness version 0 addresses must use Bech32 encoding, and all other
274    /// versions must use Bech32m
275    InvalidEncoding,
276}
277
278impl From<bech32::Error> for Error {
279    fn from(e: bech32::Error) -> Error {
280        Error::Bech32(e)
281    }
282}
283
284impl fmt::Display for Error {
285    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
286        match *self {
287            Error::Bech32(ref e) => write!(f, "{}", e),
288            Error::InvalidHumanReadablePart => write!(f, "invalid human-readable part"),
289            Error::ScriptPubkeyTooShort => write!(f, "scriptpubkey too short"),
290            Error::ScriptPubkeyInvalidLength => write!(f, "scriptpubkey length mismatch"),
291            Error::InvalidLength => write!(f, "invalid length"),
292            Error::InvalidVersionLength => write!(f, "program length incompatible with version"),
293            Error::InvalidScriptVersion => write!(f, "invalid script version"),
294            Error::InvalidEncoding => write!(f, "invalid Bech32 encoding"),
295        }
296    }
297}
298
299impl error::Error for Error {
300    fn description(&self) -> &str {
301        match *self {
302            Error::Bech32(_) => "Bech32 error",
303            Error::InvalidHumanReadablePart => "invalid human-readable part",
304            Error::ScriptPubkeyTooShort => "scriptpubkey too short",
305            Error::ScriptPubkeyInvalidLength => "scriptpubkey length mismatch",
306            Error::InvalidLength => "invalid length",
307            Error::InvalidVersionLength => "program length incompatible with version",
308            Error::InvalidScriptVersion => "invalid script version",
309            Error::InvalidEncoding => "invalid Bech32 encoding",
310        }
311    }
312
313    fn cause(&self) -> Option<&std::error::Error> {
314        match *self {
315            Error::Bech32(ref e) => Some(e),
316            _ => None,
317        }
318    }
319}
320
321#[cfg(test)]
322mod tests {
323    use bech32;
324    use constants::Network;
325    use *;
326
327    #[test]
328    fn valid_address() {
329        let pairs: Vec<(&str, Vec<u8>, Network)> = vec![
330            (
331                "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4",
332                vec![
333                    0x00, 0x14, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c,
334                    0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
335                ],
336                Network::Bitcoin,
337            ),
338            (
339                "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",
340                vec![
341                    0x00, 0x20, 0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, 0x04, 0xbd, 0x19,
342                    0x20, 0x33, 0x56, 0xda, 0x13, 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1,
343                    0xb8, 0xc6, 0x32, 0x96, 0x04, 0x90, 0x32, 0x62,
344                ],
345                Network::Testnet,
346            ),
347            (
348                "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y",
349                vec![
350                    0x51, 0x28, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c,
351                    0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6, 0x75, 0x1e, 0x76, 0xe8,
352                    0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1,
353                    0x43, 0x3b, 0xd6,
354                ],
355                Network::Bitcoin,
356            ),
357            (
358                "BC1SW50QGDZ25J",
359                vec![0x60, 0x02, 0x75, 0x1e],
360                Network::Bitcoin,
361            ),
362            (
363                "bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs",
364                vec![
365                    0x52, 0x10, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c,
366                    0x45, 0xd1, 0xb3, 0xa3, 0x23,
367                ],
368                Network::Bitcoin,
369            ),
370            (
371                "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy",
372                vec![
373                    0x00, 0x20, 0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, 0x21, 0xb2, 0xa1,
374                    0x87, 0x90, 0x5e, 0x52, 0x66, 0x36, 0x2b, 0x99, 0xd5, 0xe9, 0x1c, 0x6c, 0xe2,
375                    0x4d, 0x16, 0x5d, 0xab, 0x93, 0xe8, 0x64, 0x33,
376                ],
377                Network::Testnet,
378            ),
379            (
380                "bcrt1qn3h68k2u0rr49skx05qw7veynpf4lfppd2demt",
381                vec![
382                    0x00, 0x14, 0x9c, 0x6f, 0xa3, 0xd9, 0x5c, 0x78, 0xc7, 0x52, 0xc2, 0xc6, 0x7d,
383                    0x00, 0xef, 0x33, 0x24, 0x98, 0x53, 0x5f, 0xa4, 0x21,
384                ],
385                Network::Regtest,
386            ),
387            (
388                "MONA1Q4KPN6PSTHGD5UR894AUHJJ2G02WLGMP8KE08NE",
389                vec![
390                    0x00, 0x14, 0xad, 0x83, 0x3d, 0x06, 0x0b, 0xba, 0x1b, 0x4e, 0x0c, 0xe5, 0xaf,
391                    0x79, 0x79, 0x49, 0x48, 0x7a, 0x9d, 0xf4, 0x6c, 0x27,
392                ],
393                Network::Monacoin,
394            ),
395            (
396                "tmona1qfj8lu0rafk2mpvk7jj62q8eerjpex3xlcadtupkrkhh5a73htmhs68e55m",
397                vec![
398                    0x00, 0x20, 0x4c, 0x8f, 0xfe, 0x3c, 0x7d, 0x4d, 0x95, 0xb0, 0xb2, 0xde, 0x94,
399                    0xb4, 0xa0, 0x1f, 0x39, 0x1c, 0x83, 0x93, 0x44, 0xdf, 0xc7, 0x5a, 0xbe, 0x06,
400                    0xc3, 0xb5, 0xef, 0x4e, 0xfa, 0x37, 0x5e, 0xef,
401                ],
402                Network::MonacoinTestnet,
403            ),
404            (
405                "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",
406                vec![
407                    0x00, 0x20, 0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, 0x04, 0xbd, 0x19,
408                    0x20, 0x33, 0x56, 0xda, 0x13, 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1,
409                    0xb8, 0xc6, 0x32, 0x96, 0x04, 0x90, 0x32, 0x62,
410                ],
411                Network::Testnet,
412            ),
413            (
414                "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y",
415                vec![
416                    0x51, 0x28, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c,
417                    0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6, 0x75, 0x1e, 0x76, 0xe8,
418                    0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1,
419                    0x43, 0x3b, 0xd6,
420                ],
421                Network::Bitcoin,
422            ),
423            (
424                "BC1SW50QGDZ25J",
425                vec![0x60, 0x02, 0x75, 0x1e],
426                Network::Bitcoin,
427            ),
428            (
429                "tmona1q0p29rfu7ap3duzqj5t9e0jzgqzwdtd97pa5rhuz4r38t5a6dknyqxmyyaz",
430                vec![
431                    0x00, 0x20, 0x78, 0x54, 0x51, 0xa7, 0x9e, 0xe8, 0x62, 0xde, 0x08, 0x12, 0xa2,
432                    0xcb, 0x97, 0xc8, 0x48, 0x00, 0x9c, 0xd5, 0xb4, 0xbe, 0x0f, 0x68, 0x3b, 0xf0,
433                    0x55, 0x1c, 0x4e, 0xba, 0x77, 0x4d, 0xb4, 0xc8,
434                ],
435                Network::MonacoinTestnet,
436            ),
437            (
438                "tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c",
439                vec![
440                    0x51, 0x20, 0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, 0x21, 0xb2, 0xa1,
441                    0x87, 0x90, 0x5e, 0x52, 0x66, 0x36, 0x2b, 0x99, 0xd5, 0xe9, 0x1c, 0x6c, 0xe2,
442                    0x4d, 0x16, 0x5d, 0xab, 0x93, 0xe8, 0x64, 0x33,
443                ],
444                Network::Testnet,
445            ),
446            (
447                "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0",
448                vec![
449                    0x51, 0x20, 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62,
450                    0x95, 0xce, 0x87, 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9,
451                    0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98,
452                ],
453                Network::Bitcoin,
454            ),
455        ];
456        for p in pairs {
457            let (address, scriptpubkey, network) = p;
458            let version = if scriptpubkey[0] == 0 {
459                0
460            } else {
461                scriptpubkey[0] - 0x50
462            };
463            let prog = match WitnessProgram::from_address(&address) {
464                Ok(prog) => prog,
465                Err(e) => panic!("{}, {:?}", address, e),
466            };
467
468            let pubkey = prog.to_scriptpubkey();
469            assert_eq!(pubkey, scriptpubkey);
470
471            assert_eq!(prog.network(), network);
472            assert_eq!(prog.version().to_u8(), version);
473            assert_eq!(prog.program(), &scriptpubkey[2..]); // skip version and length
474
475            match WitnessProgram::from_scriptpubkey(&scriptpubkey, prog.network) {
476                Ok(prog) => {
477                    assert_eq!(
478                        prog.to_string().to_lowercase(),
479                        prog.to_string().to_lowercase()
480                    );
481                    let enc_address = prog.to_address();
482                    assert_eq!(address.to_lowercase(), enc_address.to_lowercase());
483                }
484                Err(e) => panic!("{:?}, {:?}", scriptpubkey, e),
485            }
486        }
487    }
488
489    #[test]
490    fn invalid_address() {
491        let pairs: Vec<(&str, Error)> = vec![
492            // BIP-0173 Invalid Addresses
493            (
494                "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty",
495                Error::InvalidHumanReadablePart,
496            ),
497            (
498                "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5",
499                Error::Bech32(bech32::Error::InvalidChecksum),
500            ),
501            (
502                "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2",
503                Error::InvalidScriptVersion,
504            ),
505            ("bc1rw5uspcuh", Error::InvalidLength),
506            (
507                "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90",
508                Error::Bech32(bech32::Error::InvalidLength),
509            ),
510            (
511                "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7",
512                Error::Bech32(bech32::Error::MixedCase),
513            ),
514            (
515                "tb1pw508d6qejxtdg4y5r3zarqfsj6c3",
516                Error::Bech32(bech32::Error::InvalidPadding),
517            ),
518            (
519                "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du",
520                Error::Bech32(bech32::Error::InvalidPadding),
521            ),
522            (
523                "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv",
524                Error::Bech32(bech32::Error::InvalidPadding),
525            ),
526            // BIP-0350 Invalid Addresses
527            (
528                "tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut",
529                Error::InvalidHumanReadablePart,
530            ),
531            (
532                "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd",
533                Error::InvalidEncoding,
534            ),
535            (
536                "tb1z0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqglt7rf",
537                Error::InvalidEncoding,
538            ),
539            (
540                "BC1S0XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ54WELL",
541                Error::InvalidEncoding,
542            ),
543            (
544                "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh",
545                Error::InvalidEncoding,
546            ),
547            (
548                "tb1q0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq24jc47",
549                Error::InvalidEncoding,
550            ),
551            (
552                "bc1p38j9r5y49hruaue7wxjce0updqjuyyx0kh56v8s25huc6995vvpql3jow4",
553                Error::Bech32(bech32::Error::InvalidChar('o')),
554            ),
555            (
556                "BC130XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ7ZWS8R",
557                Error::InvalidScriptVersion,
558            ),
559            ("bc1pw5dgrnzv", Error::InvalidLength),
560            (
561                "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav253zgeav",
562                Error::Bech32(bech32::Error::InvalidLength),
563            ),
564            (
565                "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P",
566                Error::InvalidVersionLength,
567            ),
568            (
569                "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq47Zagq",
570                Error::Bech32(bech32::Error::MixedCase),
571            ),
572            (
573                "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v07qwwzcrf",
574                Error::Bech32(bech32::Error::InvalidPadding),
575            ),
576            (
577                "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vpggkg4j",
578                Error::Bech32(bech32::Error::InvalidPadding),
579            ),
580            ("bc1gmk9yu", Error::Bech32(bech32::Error::InvalidLength)),
581        ];
582        for p in pairs {
583            let (address, desired_error) = p;
584            match WitnessProgram::from_address(&address) {
585                Ok(_) => panic!("Should be invalid: {:?}", address),
586                Err(e) => assert_eq!(e, desired_error, "{}", address),
587            }
588        }
589    }
590}