bech32/primitives/
segwit.rs

1// SPDX-License-Identifier: MIT
2
3//! Segregated Witness functionality - useful for enforcing parts of [BIP-173] and [BIP-350].
4//!
5//! [BIP-173]: <https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki>
6//! [BIP-350]: <https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki>
7
8use core::fmt;
9
10use crate::primitives::gf32::Fe32;
11
12/// The maximum enforced string length of a segwit address.
13///
14/// The maximum length as specified in BIP-173, this is less than the 1023 character code length.
15/// This limit is based on empirical error-correcting properties. See ["Checksum design"] section.
16///
17/// ["Checksum design"]: <https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#user-content-Checksum_design>
18pub const MAX_STRING_LENGTH: usize = 90;
19
20/// The field element representing segwit version 0.
21pub const VERSION_0: Fe32 = Fe32::Q;
22/// The field element representing segwit version 1 (taproot).
23pub const VERSION_1: Fe32 = Fe32::P;
24
25/// Returns true if given field element represents a valid segwit version.
26pub fn is_valid_witness_version(witness_version: Fe32) -> bool {
27    validate_witness_version(witness_version).is_ok()
28}
29
30/// Returns true if `length` represents a valid witness program length for `witness_version`.
31pub fn is_valid_witness_program_length(length: usize, witness_version: Fe32) -> bool {
32    validate_witness_program_length(length, witness_version).is_ok()
33}
34
35/// Checks that the given field element represents a valid segwit witness version.
36pub fn validate_witness_version(witness_version: Fe32) -> Result<(), InvalidWitnessVersionError> {
37    if witness_version.to_u8() > 16 {
38        Err(InvalidWitnessVersionError(witness_version))
39    } else {
40        Ok(())
41    }
42}
43
44/// Validates the segwit witness program `length` rules for witness `version`.
45pub fn validate_witness_program_length(
46    length: usize,
47    version: Fe32,
48) -> Result<(), WitnessLengthError> {
49    use WitnessLengthError::*;
50
51    if length < 2 {
52        return Err(TooShort);
53    }
54    if length > 40 {
55        return Err(TooLong);
56    }
57    if version == VERSION_0 && length != 20 && length != 32 {
58        return Err(InvalidSegwitV0);
59    }
60    Ok(())
61}
62
63/// Field element does not represent a valid witness version.
64#[derive(Debug, Clone, PartialEq, Eq)]
65#[non_exhaustive]
66pub struct InvalidWitnessVersionError(pub Fe32);
67
68#[rustfmt::skip]
69impl fmt::Display for InvalidWitnessVersionError {
70    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71        write!(f, "invalid segwit witness version: {} (bech32 character: '{}')", self.0.to_u8(), self.0)
72    }
73}
74
75#[cfg(feature = "std")]
76impl std::error::Error for InvalidWitnessVersionError {
77    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
78}
79
80/// Witness program invalid because of incorrect length.
81#[derive(Debug, Clone, PartialEq, Eq)]
82#[non_exhaustive]
83pub enum WitnessLengthError {
84    /// The witness data is too short.
85    TooShort,
86    /// The witness data is too long.
87    TooLong,
88    /// The segwit v0 witness is not 20 or 32 bytes long.
89    InvalidSegwitV0,
90}
91
92impl fmt::Display for WitnessLengthError {
93    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94        use WitnessLengthError::*;
95
96        match *self {
97            TooShort => write!(f, "witness program is less than 2 bytes long"),
98            TooLong => write!(f, "witness program is more than 40 bytes long"),
99            InvalidSegwitV0 => write!(f, "the segwit v0 witness is not 20 or 32 bytes long"),
100        }
101    }
102}
103
104#[cfg(feature = "std")]
105impl std::error::Error for WitnessLengthError {
106    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
107        use WitnessLengthError::*;
108
109        match *self {
110            TooShort | TooLong | InvalidSegwitV0 => None,
111        }
112    }
113}