Skip to content

Commit a4e40fc

Browse files
committed
Own implementation of Bech32::u5 type, upgrade of bech32 dependency
1 parent 78c0eaa commit a4e40fc

20 files changed

+713
-95
lines changed

fuzz/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ stdin_fuzz = []
2121
lightning = { path = "../lightning", features = ["regex", "hashbrown", "_test_utils"] }
2222
lightning-invoice = { path = "../lightning-invoice" }
2323
lightning-rapid-gossip-sync = { path = "../lightning-rapid-gossip-sync" }
24-
bech32 = "0.9.1"
24+
bech32 = "0.11.0"
2525
bitcoin = { version = "0.31.2", features = ["secp-lowmemory"] }
2626
hex = { package = "hex-conservative", version = "0.1.1", default-features = false }
2727

fuzz/src/bolt11_deser.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
// licenses.
99

1010
use crate::utils::test_logger;
11-
use bech32::{u5, FromBase32, ToBase32};
1211
use bitcoin::secp256k1::{Secp256k1, SecretKey};
1312
use lightning_invoice::{
1413
Bolt11Invoice, RawBolt11Invoice, RawDataPart, RawHrp, RawTaggedField, TaggedField,
1514
};
15+
use lightning::util::bech32::{u5, FromBase32, ToBase32};
1616
use std::str::FromStr;
1717

1818
#[inline]

fuzz/src/chanmon_consistency.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ use lightning::routing::router::{InFlightHtlcs, Path, Route, RouteHop, RoutePara
6464
use lightning::sign::{
6565
EntropySource, InMemorySigner, KeyMaterial, NodeSigner, Recipient, SignerProvider,
6666
};
67+
use lightning::util::bech32::u5;
6768
use lightning::util::config::UserConfig;
6869
use lightning::util::errors::APIError;
6970
use lightning::util::hash_tables::*;
@@ -79,7 +80,6 @@ use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
7980
use bitcoin::secp256k1::schnorr;
8081
use bitcoin::secp256k1::{self, Message, PublicKey, Scalar, Secp256k1, SecretKey};
8182

82-
use bech32::u5;
8383
use std::cmp::{self, Ordering};
8484
use std::io::Cursor;
8585
use std::mem;

fuzz/src/full_stack.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ use lightning::routing::utxo::UtxoLookup;
6161
use lightning::sign::{
6262
EntropySource, InMemorySigner, KeyMaterial, NodeSigner, Recipient, SignerProvider,
6363
};
64+
use lightning::util::bech32::u5;
6465
use lightning::util::config::{ChannelConfig, UserConfig};
6566
use lightning::util::errors::APIError;
6667
use lightning::util::hash_tables::*;
@@ -76,7 +77,6 @@ use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
7677
use bitcoin::secp256k1::schnorr;
7778
use bitcoin::secp256k1::{self, Message, PublicKey, Scalar, Secp256k1, SecretKey};
7879

79-
use bech32::u5;
8080
use std::cell::RefCell;
8181
use std::cmp;
8282
use std::convert::TryInto;

fuzz/src/onion_message.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// Imports that need to be added manually
2-
use bech32::u5;
32
use bitcoin::blockdata::script::ScriptBuf;
43
use bitcoin::secp256k1::ecdh::SharedSecret;
54
use bitcoin::secp256k1::ecdsa::RecoverableSignature;
@@ -23,6 +22,7 @@ use lightning::onion_message::messenger::{
2322
use lightning::onion_message::offers::{OffersMessage, OffersMessageHandler};
2423
use lightning::onion_message::packet::OnionMessageContents;
2524
use lightning::sign::{EntropySource, KeyMaterial, NodeSigner, Recipient, SignerProvider};
25+
use lightning::util::bech32::u5;
2626
use lightning::util::logger::Logger;
2727
use lightning::util::ser::{Readable, Writeable, Writer};
2828
use lightning::util::test_channel_signer::TestChannelSigner;

lightning-invoice/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ no-std = ["lightning/no-std"]
2020
std = ["bitcoin/std", "lightning/std", "bech32/std"]
2121

2222
[dependencies]
23-
bech32 = { version = "0.9.1", default-features = false }
23+
bech32 = { version = "0.11.0", default-features = false }
2424
lightning = { version = "0.0.123-beta", path = "../lightning", default-features = false }
2525
secp256k1 = { version = "0.28.0", default-features = false, features = ["recovery", "alloc"] }
2626
serde = { version = "1.0.118", optional = true }

lightning-invoice/src/de.rs

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ use core::num::ParseIntError;
88
use core::str;
99
use core::str::FromStr;
1010

11-
use bech32::{u5, FromBase32};
11+
use bech32::Bech32;
12+
use bech32::primitives::decode::{CheckedHrpstring, CheckedHrpstringError};
1213

1314
use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion};
1415
use bitcoin::hashes::Hash;
1516
use bitcoin::hashes::sha256;
1617
use crate::prelude::*;
18+
use lightning::util::bech32::{Bech32Error, FromBase32, u5};
1719
use lightning::ln::types::PaymentSecret;
1820
use lightning::routing::gossip::RoutingFees;
1921
use lightning::routing::router::{RouteHint, RouteHintHop};
@@ -270,31 +272,32 @@ impl FromStr for SignedRawBolt11Invoice {
270272
type Err = Bolt11ParseError;
271273

272274
fn from_str(s: &str) -> Result<Self, Self::Err> {
273-
let (hrp, data, var) = bech32::decode(s)?;
274275

275-
if var == bech32::Variant::Bech32m {
276-
// Consider Bech32m addresses to be "Invalid Checksum", since that is what we'd get if
277-
// we didn't support Bech32m (which lightning does not use).
278-
return Err(Bolt11ParseError::Bech32Error(bech32::Error::InvalidChecksum));
279-
}
276+
let parsed = CheckedHrpstring::new::<Bech32>(s)?;
277+
let hrp = parsed.hrp();
278+
// access the parse data part as chars, covert to u5
279+
let data: Vec<_> = parsed.data_part_ascii_no_checksum().iter()
280+
.map(|ch| u5::try_from_char(char::from(*ch)).expect("value should be < 32"))
281+
.collect();
280282

281-
if data.len() < 104 {
283+
const MIN_LEN: usize = 104;
284+
if data.len() < MIN_LEN {
282285
return Err(Bolt11ParseError::TooShortDataPart);
283286
}
284287

285-
let raw_hrp: RawHrp = hrp.parse()?;
286-
let data_part = RawDataPart::from_base32(&data[..data.len()-104])?;
288+
let raw_hrp: RawHrp = hrp.to_string().to_lowercase().parse()?;
289+
let data_part = RawDataPart::from_base32(&data[..data.len()-MIN_LEN])?;
287290

288291
Ok(SignedRawBolt11Invoice {
289292
raw_invoice: RawBolt11Invoice {
290293
hrp: raw_hrp,
291294
data: data_part,
292295
},
293296
hash: RawBolt11Invoice::hash_from_parts(
294-
hrp.as_bytes(),
295-
&data[..data.len()-104]
297+
hrp.to_string().as_bytes(),
298+
&data[..data.len()-MIN_LEN]
296299
),
297-
signature: Bolt11InvoiceSignature::from_base32(&data[data.len()-104..])?,
300+
signature: Bolt11InvoiceSignature::from_base32(&data[data.len()-MIN_LEN..])?,
298301
})
299302
}
300303
}
@@ -389,7 +392,7 @@ macro_rules! define_parse_int_be { ($name: ident, $ty: ty) => {
389392
digits.iter().fold(Some(Default::default()), |acc, b|
390393
acc
391394
.and_then(|x| x.checked_mul(32))
392-
.and_then(|x| x.checked_add((Into::<u8>::into(*b)).into()))
395+
.and_then(|x| x.checked_add((*b).as_u8().into()))
393396
)
394397
}
395398
} }
@@ -424,7 +427,7 @@ fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, Bolt11ParseErr
424427
Ok(field) => {
425428
parts.push(RawTaggedField::KnownSemantics(field))
426429
},
427-
Err(Bolt11ParseError::Skip)|Err(Bolt11ParseError::Bech32Error(bech32::Error::InvalidLength)) => {
430+
Err(Bolt11ParseError::Skip)|Err(Bolt11ParseError::Bech32Error(_)) => {
428431
parts.push(RawTaggedField::UnknownSemantics(field.into()))
429432
},
430433
Err(e) => {return Err(e)}
@@ -444,7 +447,7 @@ impl FromBase32 for TaggedField {
444447
let tag = field[0];
445448
let field_data = &field[3..];
446449

447-
match tag.to_u8() {
450+
match tag.as_u8() {
448451
constants::TAG_PAYMENT_HASH =>
449452
Ok(TaggedField::PaymentHash(Sha256::from_base32(field_data)?)),
450453
constants::TAG_DESCRIPTION =>
@@ -550,7 +553,7 @@ impl FromBase32 for Fallback {
550553
return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
551554
}
552555

553-
let version = field_data[0].to_u8();
556+
let version = field_data[0].as_u8();
554557
let bytes = Vec::<u8>::from_base32(&field_data[1..])?;
555558

556559
match version {
@@ -629,6 +632,9 @@ impl Display for Bolt11ParseError {
629632
Bolt11ParseError::Bech32Error(ref e) => {
630633
write!(f, "Invalid bech32: {}", e)
631634
}
635+
Bolt11ParseError::Bech32ExternalError(ref e) => {
636+
write!(f, "Invalid bech32: {}", e)
637+
}
632638
Bolt11ParseError::ParseAmountError(ref e) => {
633639
write!(f, "Invalid amount in hrp ({})", e)
634640
}
@@ -703,10 +709,17 @@ from_error!(Bolt11ParseError::MalformedSignature, secp256k1::Error);
703709
from_error!(Bolt11ParseError::ParseAmountError, ParseIntError);
704710
from_error!(Bolt11ParseError::DescriptionDecodeError, str::Utf8Error);
705711

706-
impl From<bech32::Error> for Bolt11ParseError {
707-
fn from(e: bech32::Error) -> Self {
712+
impl From<CheckedHrpstringError> for Bolt11ParseError {
713+
fn from(e: CheckedHrpstringError) -> Self {
714+
match e {
715+
_ => Bolt11ParseError::Bech32ExternalError(e)
716+
}
717+
}
718+
}
719+
720+
impl From<Bech32Error> for Bolt11ParseError {
721+
fn from(e: Bech32Error) -> Self {
708722
match e {
709-
bech32::Error::InvalidPadding => Bolt11ParseError::PaddingError,
710723
_ => Bolt11ParseError::Bech32Error(e)
711724
}
712725
}
@@ -726,9 +739,9 @@ impl From<crate::Bolt11SemanticError> for ParseOrSemanticError {
726739

727740
#[cfg(test)]
728741
mod test {
742+
use super::{u5, FromBase32};
729743
use crate::de::Bolt11ParseError;
730744
use secp256k1::PublicKey;
731-
use bech32::u5;
732745
use bitcoin::hashes::sha256;
733746
use std::str::FromStr;
734747

@@ -779,7 +792,6 @@ mod test {
779792
#[test]
780793
fn test_parse_sha256_hash() {
781794
use crate::Sha256;
782-
use bech32::FromBase32;
783795

784796
let input = from_bech32(
785797
"qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq".as_bytes()
@@ -802,7 +814,6 @@ mod test {
802814
#[test]
803815
fn test_parse_description() {
804816
use crate::Description;
805-
use bech32::FromBase32;
806817

807818
let input = from_bech32("xysxxatsyp3k7enxv4js".as_bytes());
808819
let expected = Ok(Description::new("1 cup coffee".to_owned()).unwrap());
@@ -812,7 +823,6 @@ mod test {
812823
#[test]
813824
fn test_parse_payee_pub_key() {
814825
use crate::PayeePubKey;
815-
use bech32::FromBase32;
816826

817827
let input = from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66".as_bytes());
818828
let pk_bytes = [
@@ -836,7 +846,6 @@ mod test {
836846
#[test]
837847
fn test_parse_expiry_time() {
838848
use crate::ExpiryTime;
839-
use bech32::FromBase32;
840849

841850
let input = from_bech32("pu".as_bytes());
842851
let expected = Ok(ExpiryTime::from_seconds(60));
@@ -849,7 +858,6 @@ mod test {
849858
#[test]
850859
fn test_parse_min_final_cltv_expiry_delta() {
851860
use crate::MinFinalCltvExpiryDelta;
852-
use bech32::FromBase32;
853861

854862
let input = from_bech32("pr".as_bytes());
855863
let expected = Ok(MinFinalCltvExpiryDelta(35));
@@ -860,7 +868,6 @@ mod test {
860868
#[test]
861869
fn test_parse_fallback() {
862870
use crate::Fallback;
863-
use bech32::FromBase32;
864871
use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion};
865872
use bitcoin::hashes::Hash;
866873

@@ -921,7 +928,6 @@ mod test {
921928
use lightning::routing::gossip::RoutingFees;
922929
use lightning::routing::router::{RouteHint, RouteHintHop};
923930
use crate::PrivateRoute;
924-
use bech32::FromBase32;
925931

926932
let input = from_bech32(
927933
"q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
@@ -967,7 +973,7 @@ mod test {
967973
assert_eq!(PrivateRoute::from_base32(&input), Ok(PrivateRoute(RouteHint(expected))));
968974

969975
assert_eq!(
970-
PrivateRoute::from_base32(&[u5::try_from_u8(0).unwrap(); 40][..]),
976+
PrivateRoute::from_base32(&[u5::ZERO; 40][..]),
971977
Err(Bolt11ParseError::UnexpectedEndOfTaggedFields)
972978
);
973979
}

lightning-invoice/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,11 @@ extern crate serde;
4141
#[cfg(feature = "std")]
4242
use std::time::SystemTime;
4343

44-
use bech32::u5;
44+
use bech32::primitives::decode::CheckedHrpstringError;
4545
use bitcoin::{Address, Network, PubkeyHash, ScriptHash, WitnessProgram, WitnessVersion};
4646
use bitcoin::address::Payload;
4747
use bitcoin::hashes::{Hash, sha256};
48+
use lightning::util::bech32::{Bech32Error, u5, ToBase32};
4849
use lightning::ln::features::Bolt11InvoiceFeatures;
4950
use lightning::util::invoice::construct_invoice_preimage;
5051

@@ -90,7 +91,8 @@ use crate::prelude::*;
9091
#[allow(missing_docs)]
9192
#[derive(PartialEq, Eq, Debug, Clone)]
9293
pub enum Bolt11ParseError {
93-
Bech32Error(bech32::Error),
94+
Bech32Error(Bech32Error),
95+
Bech32ExternalError(CheckedHrpstringError),
9496
ParseAmountError(ParseIntError),
9597
MalformedSignature(secp256k1::Error),
9698
BadPrefix,
@@ -979,8 +981,6 @@ impl RawBolt11Invoice {
979981

980982
/// Calculate the hash of the encoded `RawBolt11Invoice` which should be signed.
981983
pub fn signable_hash(&self) -> [u8; 32] {
982-
use bech32::ToBase32;
983-
984984
RawBolt11Invoice::hash_from_parts(
985985
self.hrp.to_string().as_bytes(),
986986
&self.data.to_base32()

lightning-invoice/src/ser.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
use lightning::util::bech32::{Base32Len, ToBase32, u5, WriteBase32};
2+
use bech32::{Bech32, Fe32, Fe32IterExt, Hrp};
13
use core::fmt;
24
use core::fmt::{Display, Formatter};
3-
use bech32::{ToBase32, u5, WriteBase32, Base32Len};
45
use crate::prelude::*;
56

67
use super::{Bolt11Invoice, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiryDelta, Fallback, PayeePubKey, Bolt11InvoiceSignature, PositiveTimestamp,
@@ -118,7 +119,10 @@ impl Display for SignedRawBolt11Invoice {
118119
let mut data = self.raw_invoice.data.to_base32();
119120
data.extend_from_slice(&self.signature.to_base32());
120121

121-
bech32::encode_to_fmt(f, &hrp, data, bech32::Variant::Bech32).expect("HRP is valid")?;
122+
// TODO(bech32): support with_checksum() in own u5 implementation
123+
let bech32 = data.iter().map(|u| Fe32::try_from(u.as_u8()).expect("<31"))
124+
.with_checksum::<Bech32>(&Hrp::parse(&hrp).expect("not a valid hrp string")).chars().collect::<String>();
125+
f.write_str(&bech32)?;
122126

123127
Ok(())
124128
}
@@ -468,8 +472,6 @@ impl ToBase32 for Bolt11InvoiceSignature {
468472

469473
#[cfg(test)]
470474
mod test {
471-
use bech32::CheckBase32;
472-
473475
#[test]
474476
fn test_currency_code() {
475477
use crate::Currency;
@@ -497,9 +499,10 @@ mod test {
497499
#[test]
498500
fn test_encode_int_be_base32() {
499501
use crate::ser::encode_int_be_base32;
502+
use lightning::util::bech32::u5;
500503

501504
let input: u64 = 33764;
502-
let expected_out = CheckBase32::check_base32(&[1, 0, 31, 4]).unwrap();
505+
let expected_out = [1u8, 0, 31, 4].iter().map(|v| u5::try_from_u8(*v).unwrap()).collect::<Vec<u5>>();
503506

504507
assert_eq!(expected_out, encode_int_be_base32(input));
505508
}

lightning-invoice/src/utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
use crate::{Bolt11Invoice, CreationError, Currency, InvoiceBuilder, SignOrCreationError};
44

55
use crate::{prelude::*, Description, Bolt11InvoiceDescription, Sha256};
6-
use bech32::ToBase32;
76
use bitcoin::hashes::Hash;
7+
use lightning::util::bech32::ToBase32;
88
use lightning::chain;
99
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
1010
use lightning::sign::{Recipient, NodeSigner, SignerProvider, EntropySource};

lightning-invoice/tests/ser_de.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ extern crate lightning_invoice;
44
extern crate secp256k1;
55
extern crate hex;
66

7+
use bech32::primitives::decode::{CharError, CheckedHrpstringError, ChecksumError, UncheckedHrpstringError};
78
use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion};
89
use bitcoin::hashes::hex::FromHex;
910
use bitcoin::hashes::{sha256, Hash};
@@ -419,13 +420,13 @@ fn test_bolt_invalid_invoices() {
419420
), Err(ParseOrSemanticError::SemanticError(Bolt11SemanticError::InvalidFeatures)));
420421
assert_eq!(Bolt11Invoice::from_str(
421422
"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrnt"
422-
), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32Error(bech32::Error::InvalidChecksum))));
423+
), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32ExternalError(CheckedHrpstringError::Checksum(ChecksumError::InvalidResidue)))));
423424
assert_eq!(Bolt11Invoice::from_str(
424425
"pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny"
425-
), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32Error(bech32::Error::MissingSeparator))));
426+
), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32ExternalError(CheckedHrpstringError::Parse(UncheckedHrpstringError::Char(CharError::MissingSeparator))))));
426427
assert_eq!(Bolt11Invoice::from_str(
427428
"LNBC2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny"
428-
), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32Error(bech32::Error::MixedCase))));
429+
), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32ExternalError(CheckedHrpstringError::Parse(UncheckedHrpstringError::Char(CharError::MixedCase))))));
429430
assert_eq!(Bolt11Invoice::from_str(
430431
"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgqwgt7mcn5yqw3yx0w94pswkpq6j9uh6xfqqqtsk4tnarugeektd4hg5975x9am52rz4qskukxdmjemg92vvqz8nvmsye63r5ykel43pgz7zq0g2"
431432
), Err(ParseOrSemanticError::SemanticError(Bolt11SemanticError::InvalidSignature)));

lightning/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ grind_signatures = []
4040
default = ["std", "grind_signatures"]
4141

4242
[dependencies]
43-
bech32 = { version = "0.9.1", default-features = false }
43+
bech32 = { version = "0.11.0", default-features = false }
4444
bitcoin = { version = "0.31.2", default-features = false, features = ["secp-recovery"] }
4545

4646
hashbrown = { version = "0.13", optional = true, default-features = false }

0 commit comments

Comments
 (0)