Skip to content

Own implementation of Bech32::u5 type, upgrade of bech32 dependency #3201

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ stdin_fuzz = []
lightning = { path = "../lightning", features = ["regex", "hashbrown", "_test_utils"] }
lightning-invoice = { path = "../lightning-invoice" }
lightning-rapid-gossip-sync = { path = "../lightning-rapid-gossip-sync" }
bech32 = "0.9.1"
bech32 = "0.11.0"
bitcoin = { version = "0.31.2", features = ["secp-lowmemory"] }
hex = { package = "hex-conservative", version = "0.1.1", default-features = false }

Expand Down
2 changes: 1 addition & 1 deletion fuzz/src/bolt11_deser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
// licenses.

use crate::utils::test_logger;
use bech32::{u5, FromBase32, ToBase32};
use bitcoin::secp256k1::{Secp256k1, SecretKey};
use lightning::util::bech32::{u5, FromBase32, ToBase32};
use lightning_invoice::{
Bolt11Invoice, RawBolt11Invoice, RawDataPart, RawHrp, RawTaggedField, TaggedField,
};
Expand Down
2 changes: 1 addition & 1 deletion fuzz/src/chanmon_consistency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ use lightning::routing::router::{InFlightHtlcs, Path, Route, RouteHop, RoutePara
use lightning::sign::{
EntropySource, InMemorySigner, KeyMaterial, NodeSigner, Recipient, SignerProvider,
};
use lightning::util::bech32::u5;
use lightning::util::config::UserConfig;
use lightning::util::errors::APIError;
use lightning::util::hash_tables::*;
Expand All @@ -79,7 +80,6 @@ use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
use bitcoin::secp256k1::schnorr;
use bitcoin::secp256k1::{self, Message, PublicKey, Scalar, Secp256k1, SecretKey};

use bech32::u5;
use std::cmp::{self, Ordering};
use std::io::Cursor;
use std::mem;
Expand Down
2 changes: 1 addition & 1 deletion fuzz/src/full_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ use lightning::routing::utxo::UtxoLookup;
use lightning::sign::{
EntropySource, InMemorySigner, KeyMaterial, NodeSigner, Recipient, SignerProvider,
};
use lightning::util::bech32::u5;
use lightning::util::config::{ChannelConfig, UserConfig};
use lightning::util::errors::APIError;
use lightning::util::hash_tables::*;
Expand All @@ -76,7 +77,6 @@ use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
use bitcoin::secp256k1::schnorr;
use bitcoin::secp256k1::{self, Message, PublicKey, Scalar, Secp256k1, SecretKey};

use bech32::u5;
use std::cell::RefCell;
use std::cmp;
use std::convert::TryInto;
Expand Down
2 changes: 1 addition & 1 deletion fuzz/src/onion_message.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// Imports that need to be added manually
use bech32::u5;
use bitcoin::blockdata::script::ScriptBuf;
use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::RecoverableSignature;
Expand All @@ -23,6 +22,7 @@ use lightning::onion_message::messenger::{
use lightning::onion_message::offers::{OffersMessage, OffersMessageHandler};
use lightning::onion_message::packet::OnionMessageContents;
use lightning::sign::{EntropySource, KeyMaterial, NodeSigner, Recipient, SignerProvider};
use lightning::util::bech32::u5;
use lightning::util::logger::Logger;
use lightning::util::ser::{Readable, Writeable, Writer};
use lightning::util::test_channel_signer::TestChannelSigner;
Expand Down
2 changes: 1 addition & 1 deletion lightning-invoice/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ no-std = ["lightning/no-std"]
std = ["bitcoin/std", "lightning/std", "bech32/std"]

[dependencies]
bech32 = { version = "0.9.1", default-features = false }
bech32 = { version = "0.11.0", default-features = false }
lightning = { version = "0.0.123-beta", path = "../lightning", default-features = false }
secp256k1 = { version = "0.28.0", default-features = false, features = ["recovery", "alloc"] }
serde = { version = "1.0.118", optional = true }
Expand Down
64 changes: 35 additions & 29 deletions lightning-invoice/src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ use core::num::ParseIntError;
use core::str;
use core::str::FromStr;

use bech32::{u5, FromBase32};
use bech32::Bech32;
use bech32::primitives::decode::{CheckedHrpstring, CheckedHrpstringError};

use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion};
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256;
use crate::prelude::*;
use lightning::util::bech32::{Bech32Error, FromBase32, u5};
use lightning::ln::types::PaymentSecret;
use lightning::routing::gossip::RoutingFees;
use lightning::routing::router::{RouteHint, RouteHintHop};
Expand Down Expand Up @@ -270,31 +272,32 @@ impl FromStr for SignedRawBolt11Invoice {
type Err = Bolt11ParseError;

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

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

if data.len() < 104 {
const MIN_LEN: usize = 104;
if data.len() < MIN_LEN {
return Err(Bolt11ParseError::TooShortDataPart);
}

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

Ok(SignedRawBolt11Invoice {
raw_invoice: RawBolt11Invoice {
hrp: raw_hrp,
data: data_part,
},
hash: RawBolt11Invoice::hash_from_parts(
hrp.as_bytes(),
&data[..data.len()-104]
hrp.to_string().as_bytes(),
&data[..data.len()-MIN_LEN]
),
signature: Bolt11InvoiceSignature::from_base32(&data[data.len()-104..])?,
signature: Bolt11InvoiceSignature::from_base32(&data[data.len()-MIN_LEN..])?,
})
}
}
Expand Down Expand Up @@ -389,7 +392,7 @@ macro_rules! define_parse_int_be { ($name: ident, $ty: ty) => {
digits.iter().fold(Some(Default::default()), |acc, b|
acc
.and_then(|x| x.checked_mul(32))
.and_then(|x| x.checked_add((Into::<u8>::into(*b)).into()))
.and_then(|x| x.checked_add((*b).as_u8().into()))
)
}
} }
Expand Down Expand Up @@ -424,7 +427,7 @@ fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, Bolt11ParseErr
Ok(field) => {
parts.push(RawTaggedField::KnownSemantics(field))
},
Err(Bolt11ParseError::Skip)|Err(Bolt11ParseError::Bech32Error(bech32::Error::InvalidLength)) => {
Err(Bolt11ParseError::Skip)|Err(Bolt11ParseError::Bech32Error(_)) => {
parts.push(RawTaggedField::UnknownSemantics(field.into()))
},
Err(e) => {return Err(e)}
Expand All @@ -444,7 +447,7 @@ impl FromBase32 for TaggedField {
let tag = field[0];
let field_data = &field[3..];

match tag.to_u8() {
match tag.as_u8() {
constants::TAG_PAYMENT_HASH =>
Ok(TaggedField::PaymentHash(Sha256::from_base32(field_data)?)),
constants::TAG_DESCRIPTION =>
Expand Down Expand Up @@ -550,7 +553,7 @@ impl FromBase32 for Fallback {
return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
}

let version = field_data[0].to_u8();
let version = field_data[0].as_u8();
let bytes = Vec::<u8>::from_base32(&field_data[1..])?;

match version {
Expand Down Expand Up @@ -629,6 +632,9 @@ impl Display for Bolt11ParseError {
Bolt11ParseError::Bech32Error(ref e) => {
write!(f, "Invalid bech32: {}", e)
}
Bolt11ParseError::Bech32ExternalError(ref e) => {
write!(f, "Invalid bech32: {}", e)
}
Bolt11ParseError::ParseAmountError(ref e) => {
write!(f, "Invalid amount in hrp ({})", e)
}
Expand Down Expand Up @@ -703,10 +709,17 @@ from_error!(Bolt11ParseError::MalformedSignature, secp256k1::Error);
from_error!(Bolt11ParseError::ParseAmountError, ParseIntError);
from_error!(Bolt11ParseError::DescriptionDecodeError, str::Utf8Error);

impl From<bech32::Error> for Bolt11ParseError {
fn from(e: bech32::Error) -> Self {
impl From<CheckedHrpstringError> for Bolt11ParseError {
fn from(e: CheckedHrpstringError) -> Self {
match e {
_ => Bolt11ParseError::Bech32ExternalError(e)
}
}
}

impl From<Bech32Error> for Bolt11ParseError {
fn from(e: Bech32Error) -> Self {
match e {
bech32::Error::InvalidPadding => Bolt11ParseError::PaddingError,
_ => Bolt11ParseError::Bech32Error(e)
}
}
Expand All @@ -726,9 +739,9 @@ impl From<crate::Bolt11SemanticError> for ParseOrSemanticError {

#[cfg(test)]
mod test {
use super::{u5, FromBase32};
use crate::de::Bolt11ParseError;
use secp256k1::PublicKey;
use bech32::u5;
use bitcoin::hashes::sha256;
use std::str::FromStr;

Expand Down Expand Up @@ -779,7 +792,6 @@ mod test {
#[test]
fn test_parse_sha256_hash() {
use crate::Sha256;
use bech32::FromBase32;

let input = from_bech32(
"qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq".as_bytes()
Expand All @@ -802,7 +814,6 @@ mod test {
#[test]
fn test_parse_description() {
use crate::Description;
use bech32::FromBase32;

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

let input = from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66".as_bytes());
let pk_bytes = [
Expand All @@ -836,7 +846,6 @@ mod test {
#[test]
fn test_parse_expiry_time() {
use crate::ExpiryTime;
use bech32::FromBase32;

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

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

Expand Down Expand Up @@ -921,7 +928,6 @@ mod test {
use lightning::routing::gossip::RoutingFees;
use lightning::routing::router::{RouteHint, RouteHintHop};
use crate::PrivateRoute;
use bech32::FromBase32;

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

assert_eq!(
PrivateRoute::from_base32(&[u5::try_from_u8(0).unwrap(); 40][..]),
PrivateRoute::from_base32(&[u5::ZERO; 40][..]),
Err(Bolt11ParseError::UnexpectedEndOfTaggedFields)
);
}
Expand Down
8 changes: 4 additions & 4 deletions lightning-invoice/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ extern crate serde;
#[cfg(feature = "std")]
use std::time::SystemTime;

use bech32::u5;
use bech32::primitives::decode::CheckedHrpstringError;
use bitcoin::{Address, Network, PubkeyHash, ScriptHash, WitnessProgram, WitnessVersion};
use bitcoin::address::Payload;
use bitcoin::hashes::{Hash, sha256};
use lightning::util::bech32::{Bech32Error, u5, ToBase32};
use lightning::ln::features::Bolt11InvoiceFeatures;
use lightning::util::invoice::construct_invoice_preimage;

Expand Down Expand Up @@ -90,7 +91,8 @@ use crate::prelude::*;
#[allow(missing_docs)]
#[derive(PartialEq, Eq, Debug, Clone)]
pub enum Bolt11ParseError {
Bech32Error(bech32::Error),
Bech32Error(Bech32Error),
Bech32ExternalError(CheckedHrpstringError),
ParseAmountError(ParseIntError),
MalformedSignature(secp256k1::Error),
BadPrefix,
Expand Down Expand Up @@ -979,8 +981,6 @@ impl RawBolt11Invoice {

/// Calculate the hash of the encoded `RawBolt11Invoice` which should be signed.
pub fn signable_hash(&self) -> [u8; 32] {
use bech32::ToBase32;

RawBolt11Invoice::hash_from_parts(
self.hrp.to_string().as_bytes(),
&self.data.to_base32()
Expand Down
13 changes: 8 additions & 5 deletions lightning-invoice/src/ser.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use lightning::util::bech32::{Base32Len, ToBase32, u5, WriteBase32};
use bech32::{Bech32, Fe32, Fe32IterExt, Hrp};
use core::fmt;
use core::fmt::{Display, Formatter};
use bech32::{ToBase32, u5, WriteBase32, Base32Len};
use crate::prelude::*;

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

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

Ok(())
}
Expand Down Expand Up @@ -468,8 +472,6 @@ impl ToBase32 for Bolt11InvoiceSignature {

#[cfg(test)]
mod test {
use bech32::CheckBase32;

#[test]
fn test_currency_code() {
use crate::Currency;
Expand Down Expand Up @@ -497,9 +499,10 @@ mod test {
#[test]
fn test_encode_int_be_base32() {
use crate::ser::encode_int_be_base32;
use lightning::util::bech32::u5;

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

assert_eq!(expected_out, encode_int_be_base32(input));
}
Expand Down
2 changes: 1 addition & 1 deletion lightning-invoice/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
use crate::{Bolt11Invoice, CreationError, Currency, InvoiceBuilder, SignOrCreationError};

use crate::{prelude::*, Description, Bolt11InvoiceDescription, Sha256};
use bech32::ToBase32;
use bitcoin::hashes::Hash;
use lightning::util::bech32::ToBase32;
use lightning::chain;
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
use lightning::sign::{Recipient, NodeSigner, SignerProvider, EntropySource};
Expand Down
7 changes: 4 additions & 3 deletions lightning-invoice/tests/ser_de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ extern crate lightning_invoice;
extern crate secp256k1;
extern crate hex;

use bech32::primitives::decode::{CharError, CheckedHrpstringError, ChecksumError, UncheckedHrpstringError};
use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion};
use bitcoin::hashes::hex::FromHex;
use bitcoin::hashes::{sha256, Hash};
Expand Down Expand Up @@ -419,13 +420,13 @@ fn test_bolt_invalid_invoices() {
), Err(ParseOrSemanticError::SemanticError(Bolt11SemanticError::InvalidFeatures)));
assert_eq!(Bolt11Invoice::from_str(
"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrnt"
), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32Error(bech32::Error::InvalidChecksum))));
), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32ExternalError(CheckedHrpstringError::Checksum(ChecksumError::InvalidResidue)))));
assert_eq!(Bolt11Invoice::from_str(
"pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny"
), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32Error(bech32::Error::MissingSeparator))));
), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32ExternalError(CheckedHrpstringError::Parse(UncheckedHrpstringError::Char(CharError::MissingSeparator))))));
assert_eq!(Bolt11Invoice::from_str(
"LNBC2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny"
), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32Error(bech32::Error::MixedCase))));
), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32ExternalError(CheckedHrpstringError::Parse(UncheckedHrpstringError::Char(CharError::MixedCase))))));
assert_eq!(Bolt11Invoice::from_str(
"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgqwgt7mcn5yqw3yx0w94pswkpq6j9uh6xfqqqtsk4tnarugeektd4hg5975x9am52rz4qskukxdmjemg92vvqz8nvmsye63r5ykel43pgz7zq0g2"
), Err(ParseOrSemanticError::SemanticError(Bolt11SemanticError::InvalidSignature)));
Expand Down
2 changes: 1 addition & 1 deletion lightning/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ grind_signatures = []
default = ["std", "grind_signatures"]

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

hashbrown = { version = "0.13", optional = true, default-features = false }
Expand Down
Loading
Loading