Skip to content

Commit 56a01de

Browse files
committed
Expose Bech32Encode trait for fuzzing
In order to fuzz test Bech32Encode parsing independent of the underlying message deserialization, the trait needs to be exposed. Conditionally expose it only for fuzzing.
1 parent 16168d4 commit 56a01de

File tree

1 file changed

+59
-45
lines changed

1 file changed

+59
-45
lines changed

lightning/src/offers/parse.rs

Lines changed: 59 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,69 +10,83 @@
1010
//! Parsing and formatting for bech32 message encoding.
1111
1212
use bitcoin::bech32;
13-
use bitcoin::bech32::{FromBase32, ToBase32};
1413
use bitcoin::secp256k1;
1514
use core::convert::TryFrom;
16-
use core::fmt;
1715
use crate::io;
1816
use crate::ln::msgs::DecodeError;
1917
use crate::util::ser::SeekReadable;
2018

2119
use crate::prelude::*;
2220

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+
}
3751
}
38-
}
3952

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+
};
4558

46-
let (hrp, data) = bech32::decode_without_checksum(encoded.as_ref())?;
59+
let (hrp, data) = bech32::decode_without_checksum(encoded.as_ref())?;
4760

48-
if hrp != Self::BECH32_HRP {
49-
return Err(ParseError::InvalidBech32Hrp);
50-
}
61+
if hrp != Self::BECH32_HRP {
62+
return Err(ParseError::InvalidBech32Hrp);
63+
}
5164

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+
}
5568

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();
6073

61-
Ok(())
74+
Ok(())
75+
}
6276
}
63-
}
6477

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+
}
7083

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+
}
7690
}
7791
}
7892
}

0 commit comments

Comments
 (0)