-
Notifications
You must be signed in to change notification settings - Fork 378
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
Own implementation of Bech32::u5 type, upgrade of bech32 dependency #3201
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3201 +/- ##
==========================================
+ Coverage 89.80% 91.36% +1.56%
==========================================
Files 121 123 +2
Lines 100045 113913 +13868
Branches 100045 113913 +13868
==========================================
+ Hits 89845 104078 +14233
+ Misses 7533 7289 -244
+ Partials 2667 2546 -121 ☔ View full report in Codecov by Sentry. |
ae1b49d
to
dd02b21
Compare
Also, do you think this could possibly be split up into two commits? First the introduction of the new type, and then switching to it? Or would it cause too much havoc with check_commits and needing to add and then remove allow_unused? |
It is possible to split up into two commits, it helps review, but check_commits will probably complain, and it should be squashed before merging. Or did you have in mind 2 separate PRs? Also, what would be the benefit? Since the addition is in a new file, it is quite easy to separate during review. |
Actually, it's not really possible to separate the external upgrade and the new own implementation:
|
ee27f1a
to
a4e40fc
Compare
I'm a little confused about the motivation for this PR. Upstream you complained that One is to implement diff --git a/lightning-invoice/src/ser.rs b/lightning-invoice/src/ser.rs
index 7317b5cc3..4e9bce317 100644
--- a/lightning-invoice/src/ser.rs
+++ b/lightning-invoice/src/ser.rs
@@ -177,6 +177,22 @@ impl Display for SiPrefix {
}
}
+fn encode_int_be_base32_fixed(int: u64, size: usize) -> Vec<u5> {
+ let base = 32u64;
+
+ let mut out_vec = Vec::<u5>::new();
+
+ let mut rem_int = int;
+ for _ in 0..size {
+ out_vec.push(u5::try_from_u8((rem_int % base) as u8).expect("always <32"));
+ rem_int /= base;
+ }
+ assert_eq!(rem_int, 0, "input {} did not fit into {} u5 words (remainder {})", int, size, rem_int);
+
+ out_vec.reverse();
+ out_vec
+}
+
fn encode_int_be_base32(int: u64) -> Vec<u5> {
let base = 32u64;
@@ -250,10 +266,7 @@ impl ToBase32 for RawDataPart {
impl ToBase32 for PositiveTimestamp {
fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
// FIXME: use writer for int encoding
- writer.write(
- &try_stretch(encode_int_be_base32(self.as_unix_timestamp()), 7)
- .expect("Can't be longer due than 7 u5s due to timestamp bounds")
- )
+ writer.write(&encode_int_be_base32_fixed(self.as_unix_timestamp() , 7))
}
}
@@ -415,10 +428,12 @@ impl ToBase32 for TaggedField {
assert!(len < 1024, "Every tagged field data can be at most 1023 bytes long.");
writer.write_u5(u5::try_from_u8(tag).expect("invalid tag, not in 0..32"))?;
- writer.write(&try_stretch(
- encode_int_be_base32(len as u64),
- 2
- ).expect("Can't be longer than 2, see assert above."))?;
+ writer.write(&encode_int_be_base32_fixed(len as u64, 2))?;
+
+ writer.write(&[
+ u5::try_from_u8((len / 32) as u8).unwrap(),
+ u5::try_from_u8((len % 32) as u8).unwrap(),
+ ])?;
payload.write_base32(writer)
} But of course a proper fix would stop using Then for diff --git a/lightning-invoice/src/lib.rs b/lightning-invoice/src/lib.rs
index 063ae8f40..e6fc5b1bc 100644
--- a/lightning-invoice/src/lib.rs
+++ b/lightning-invoice/src/lib.rs
@@ -419,7 +419,7 @@ impl From<Currency> for Network {
/// Tagged field which may have an unknown tag
///
/// This is not exported to bindings users as we don't currently support TaggedField
-#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub enum RawTaggedField {
/// Parsed tagged field with known tag
KnownSemantics(TaggedField),
@@ -427,6 +427,23 @@ pub enum RawTaggedField {
UnknownSemantics(Vec<u5>),
}
+impl PartialOrd for RawTaggedField {
+ fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for RawTaggedField {
+ fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+ match (self, other) {
+ (RawTaggedField::KnownSemantics(..), RawTaggedField::UnknownSemantics(..)) => core::cmp::Ordering::Less,
+ (RawTaggedField::UnknownSemantics(..), RawTaggedField::KnownSemantics(..)) => core::cmp::Ordering::Greater,
+ (RawTaggedField::KnownSemantics(ref a), RawTaggedField::KnownSemantics(ref b)) => a.cmp(b),
+ (RawTaggedField::UnknownSemantics(ref a), RawTaggedField::UnknownSemantics(ref b)) => a.iter().map(|a| a.as_u8()).cmp(b.iter().map(|b| b.as_u8())),
+ }
+ }
+}
+
/// Tagged field with known tag
///
/// For descriptions of the enum values please refer to the enclosed type's docs. And both of these changes could be done in commits independent of and prior to any Also, even if you did want to create your own |
Thanks for looking into this in detail! You are right that the Default requirement could worked around, but I think the Default trait makes sense for a numerical value, and it doesn't degrade code quality. For Ord: correct, it is needed for the tagged values in lightning invoices, for the unparsed parts ( If my goal was to keep using
You are right, and I had a version like that. However, after weighing the conversions and adaptations needed vs. the code/logic to be duplicated, I arrived at the conclusion that it's easier to essentially duplicate some logic in the way suitable for the LDK project. Lightning invoice parsing is a basic operation for LDK, and I think it makes sense to have a well separated, minimal dependency module within LDK for lightning parsing, I'd like to move into that direction. In general I try to achieve high code reuse, but in this case maybe it's more practical to go towards more self-sufficient solution with no external dependency. I have written up my concerns in the issue #3195 . But I'm listening to your opinion as well. I may revisit the version without code duplication and check the shortcoming again ( #3181 ) |
My suggestion does not convert to And no, it is not "simpler" to implement a nonsensical ordering on a field element when there is a standard notion of "ordered field" which does not apply to the field in question.
You intend to write your own bech32 checksum encoding and verification code? I will switch to the other issue to avoid splitting the conversation but this is a dramatic failure of the bech32 API if it is unable to do basic checksumming. |
Own implementation of
u5
type and conversions.Upgrade of bech32 dependency, from
0.9.1
to0.11.0
.Fixes #3176 and #3195 . Obsoletes #3181 .
Changes summary:
lightning::util::bech32::u5
type, for holding a 32-bit value, taken over frombech32 v0.9.1
(This type is no longer available in bech32` crate)u8
), Bech32 character encoding/decoding (from/tochar
), ...bech32
is still used directly in a few places; it has been upgraded to0.11.0
.More details in #3195 .
Potential further improvements:
util/bech32.rs
, by calling into external bech32 implementationbech32/mod.rs
, by calling into external crate, or better, by own implementation (simple string parsing / concatenation)bech32/mod.rs
bech32-ligthning
).TODO:
fuzz
target