|
10 | 10 | //! Parsing and formatting for bech32 message encoding. |
11 | 11 |
|
12 | 12 | use bitcoin::bech32; |
13 | | -use bitcoin::bech32::{FromBase32, ToBase32}; |
14 | 13 | use bitcoin::secp256k1; |
15 | 14 | use core::convert::TryFrom; |
16 | | -use core::fmt; |
17 | 15 | use crate::io; |
18 | 16 | use crate::ln::msgs::DecodeError; |
19 | 17 | use crate::util::ser::SeekReadable; |
20 | 18 |
|
21 | 19 | use crate::prelude::*; |
22 | 20 |
|
23 | | -/// Indicates a message can be encoded using bech32. |
24 | | -pub(super) trait Bech32Encode: AsRef<[u8]> + TryFrom<Vec<u8>, Error=ParseError> { |
25 | | - /// Human readable part of the message's bech32 encoding. |
26 | | - const BECH32_HRP: &'static str; |
27 | | - |
28 | | - /// Parses a bech32-encoded message into a TLV stream. |
29 | | - fn from_bech32_str(s: &str) -> Result<Self, ParseError> { |
30 | | - // Offer encoding may be split by '+' followed by optional whitespace. |
31 | | - let encoded = match s.split('+').skip(1).next() { |
32 | | - Some(_) => { |
33 | | - for chunk in s.split('+') { |
34 | | - let chunk = chunk.trim_start(); |
35 | | - if chunk.is_empty() || chunk.contains(char::is_whitespace) { |
36 | | - return Err(ParseError::InvalidContinuation); |
| 21 | +#[cfg(not(fuzzing))] |
| 22 | +pub(super) use sealed::Bech32Encode; |
| 23 | + |
| 24 | +#[cfg(fuzzing)] |
| 25 | +pub use sealed::Bech32Encode; |
| 26 | + |
| 27 | +mod sealed { |
| 28 | + use bitcoin::bech32; |
| 29 | + use bitcoin::bech32::{FromBase32, ToBase32}; |
| 30 | + use core::convert::TryFrom; |
| 31 | + use core::fmt; |
| 32 | + use super::ParseError; |
| 33 | + |
| 34 | + use crate::prelude::*; |
| 35 | + |
| 36 | + /// Indicates a message can be encoded using bech32. |
| 37 | + pub trait Bech32Encode: AsRef<[u8]> + TryFrom<Vec<u8>, Error=ParseError> { |
| 38 | + /// Human readable part of the message's bech32 encoding. |
| 39 | + const BECH32_HRP: &'static str; |
| 40 | + |
| 41 | + /// Parses a bech32-encoded message into a TLV stream. |
| 42 | + fn from_bech32_str(s: &str) -> Result<Self, ParseError> { |
| 43 | + // Offer encoding may be split by '+' followed by optional whitespace. |
| 44 | + let encoded = match s.split('+').skip(1).next() { |
| 45 | + Some(_) => { |
| 46 | + for chunk in s.split('+') { |
| 47 | + let chunk = chunk.trim_start(); |
| 48 | + if chunk.is_empty() || chunk.contains(char::is_whitespace) { |
| 49 | + return Err(ParseError::InvalidContinuation); |
| 50 | + } |
37 | 51 | } |
38 | | - } |
39 | 52 |
|
40 | | - let s = s.chars().filter(|c| *c != '+' && !c.is_whitespace()).collect::<String>(); |
41 | | - Bech32String::Owned(s) |
42 | | - }, |
43 | | - None => Bech32String::Borrowed(s), |
44 | | - }; |
| 53 | + let s: String = s.chars().filter(|c| *c != '+' && !c.is_whitespace()).collect(); |
| 54 | + Bech32String::Owned(s) |
| 55 | + }, |
| 56 | + None => Bech32String::Borrowed(s), |
| 57 | + }; |
45 | 58 |
|
46 | | - let (hrp, data) = bech32::decode_without_checksum(encoded.as_ref())?; |
| 59 | + let (hrp, data) = bech32::decode_without_checksum(encoded.as_ref())?; |
47 | 60 |
|
48 | | - if hrp != Self::BECH32_HRP { |
49 | | - return Err(ParseError::InvalidBech32Hrp); |
50 | | - } |
| 61 | + if hrp != Self::BECH32_HRP { |
| 62 | + return Err(ParseError::InvalidBech32Hrp); |
| 63 | + } |
51 | 64 |
|
52 | | - let data = Vec::<u8>::from_base32(&data)?; |
53 | | - Self::try_from(data) |
54 | | - } |
| 65 | + let data = Vec::<u8>::from_base32(&data)?; |
| 66 | + Self::try_from(data) |
| 67 | + } |
55 | 68 |
|
56 | | - /// Formats the message using bech32-encoding. |
57 | | - fn fmt_bech32_str(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
58 | | - bech32::encode_without_checksum_to_fmt(f, Self::BECH32_HRP, self.as_ref().to_base32()) |
59 | | - .expect("HRP is invalid").unwrap(); |
| 69 | + /// Formats the message using bech32-encoding. |
| 70 | + fn fmt_bech32_str(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
| 71 | + bech32::encode_without_checksum_to_fmt(f, Self::BECH32_HRP, self.as_ref().to_base32()) |
| 72 | + .expect("HRP is invalid").unwrap(); |
60 | 73 |
|
61 | | - Ok(()) |
| 74 | + Ok(()) |
| 75 | + } |
62 | 76 | } |
63 | | -} |
64 | 77 |
|
65 | | -// Used to avoid copying a bech32 string not containing the continuation character (+). |
66 | | -enum Bech32String<'a> { |
67 | | - Borrowed(&'a str), |
68 | | - Owned(String), |
69 | | -} |
| 78 | + // Used to avoid copying a bech32 string not containing the continuation character (+). |
| 79 | + enum Bech32String<'a> { |
| 80 | + Borrowed(&'a str), |
| 81 | + Owned(String), |
| 82 | + } |
70 | 83 |
|
71 | | -impl<'a> AsRef<str> for Bech32String<'a> { |
72 | | - fn as_ref(&self) -> &str { |
73 | | - match self { |
74 | | - Bech32String::Borrowed(s) => s, |
75 | | - Bech32String::Owned(s) => s, |
| 84 | + impl<'a> AsRef<str> for Bech32String<'a> { |
| 85 | + fn as_ref(&self) -> &str { |
| 86 | + match self { |
| 87 | + Bech32String::Borrowed(s) => s, |
| 88 | + Bech32String::Owned(s) => s, |
| 89 | + } |
76 | 90 | } |
77 | 91 | } |
78 | 92 | } |
|
0 commit comments