|
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