From c389d725c2dc8b35b86676e2284b1bf70ced9a57 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 18 Sep 2024 15:38:14 +0000 Subject: [PATCH] checksum: add new error-correction fields to the Checksum trait --- src/lib.rs | 6 +++++- src/primitives/checksum.rs | 35 +++++++++++++++++++++++++++++++---- src/primitives/mod.rs | 17 +++++++++++++++++ 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index afc8cd990..2cc18a993 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,7 +95,7 @@ //! //! ``` //! # #[cfg(feature = "alloc")] { -//! use bech32::Checksum; +//! use bech32::{Checksum, Fe32, Fe1024}; //! //! /// The codex32 checksum algorithm, defined in BIP-93. //! #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -103,6 +103,10 @@ //! //! impl Checksum for Codex32 { //! type MidstateRepr = u128; +//! type CorrectionField = bech32::primitives::gf32_ext::Fe32Ext<2>; +//! const ROOT_GENERATOR: Self::CorrectionField = Fe1024::new([Fe32::_9, Fe32::_9]); +//! const ROOT_EXPONENTS: core::ops::RangeInclusive = 77..=84; +//! //! const CHECKSUM_LENGTH: usize = 13; //! const CODE_LENGTH: usize = 93; //! // Copied from BIP-93 diff --git a/src/primitives/checksum.rs b/src/primitives/checksum.rs index 3e5f82f7d..50bebb75c 100644 --- a/src/primitives/checksum.rs +++ b/src/primitives/checksum.rs @@ -23,6 +23,10 @@ use crate::Fe32; /// /// For users, this can be treated as a marker trait; none of the associated data /// are end-user relevant. +/// +/// For developers, implementations of this trait can be computed by starting with +/// a couple of values and using the [`PrintImpl`] object to generate the rest. +/// See the documentation for that type and its unit tests for examples. pub trait Checksum { /// An unsigned integer type capable of holding a packed version of the generator /// polynomial (without its leading 1) and target residue (which will have the @@ -37,6 +41,16 @@ pub trait Checksum { /// be pretty efficient no matter what. type MidstateRepr: PackedFe32; + /// The extension field in which error correction happens. + type CorrectionField: super::ExtensionField; + + /// The generator of the consecutive roots of the generator polynomial. + const ROOT_GENERATOR: Self::CorrectionField; + + /// The consecutive powers of [`Self::ROOT_GENERATOR`] which are roots + /// of the generator polynomial. + const ROOT_EXPONENTS: core::ops::RangeInclusive; + /// The length of the code. /// /// The length of the code is how long a coded message can be (including the @@ -224,7 +238,7 @@ impl<'a, ExtField> PrintImpl<'a, ExtField> { #[cfg(feature = "alloc")] impl<'a, ExtField> fmt::Display for PrintImpl<'a, ExtField> where - ExtField: super::ExtensionField + From, + ExtField: super::Bech32Field + super::ExtensionField, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Generator polynomial as a polynomial over GF1024 @@ -234,7 +248,7 @@ where v.extend(self.generator.iter().cloned().map(ExtField::from)); Polynomial::new(v) }; - let (_gen, length, _exponents) = gen_poly.bch_generator_primitive_element(); + let (gen, length, exponents) = gen_poly.bch_generator_primitive_element(); write!(f, "// Code block generated by Checksum::print_impl polynomial ")?; for fe in self.generator { @@ -251,6 +265,18 @@ where " type MidstateRepr = {}; // checksum packs into {} bits", self.midstate_repr, self.bit_len )?; + f.write_str("\n")?; + writeln!(f, " type CorrectionField = {};", core::any::type_name::())?; + f.write_str(" const ROOT_GENERATOR: Self::CorrectionField = ")?; + gen.format_as_rust_code(f)?; + f.write_str(";\n")?; + writeln!( + f, + " const ROOT_EXPONENTS: core::ops::RangeInclusive = {}..={};", + exponents.start(), + exponents.end() + )?; + f.write_str("\n")?; writeln!(f, " const CODE_LENGTH: usize = {};", length)?; writeln!(f, " const CHECKSUM_LENGTH: usize = {};", gen_poly.degree())?; writeln!(f, " const GENERATOR_SH: [{}; 5] = [", self.midstate_repr)?; @@ -263,9 +289,10 @@ where writeln!(f, " ];")?; writeln!( f, - " const TARGET_RESIDUE: {} = {:?};", + " const TARGET_RESIDUE: {} = 0x{:0width$x};", self.midstate_repr, - u128::pack(self.target.iter().copied().map(From::from)) + u128::pack(self.target.iter().copied().map(From::from)), + width = self.hex_width, )?; f.write_str("}") } diff --git a/src/primitives/mod.rs b/src/primitives/mod.rs index bd3e0a08b..fcc82023e 100644 --- a/src/primitives/mod.rs +++ b/src/primitives/mod.rs @@ -20,6 +20,8 @@ pub use field::{Bech32Field, ExtensionField, Field}; #[cfg(feature = "alloc")] use polynomial::Polynomial; +use crate::{Fe1024, Fe32}; + /// The "null checksum" used on bech32 strings for which we want to do no checksum checking. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum NoChecksum {} @@ -38,6 +40,11 @@ pub enum Bech32m {} impl Checksum for NoChecksum { type MidstateRepr = PackedNull; + + type CorrectionField = Fe32; // needs to be a `Field` + const ROOT_GENERATOR: Self::CorrectionField = Fe32::P; + const ROOT_EXPONENTS: core::ops::RangeInclusive = 0..=0; + const CODE_LENGTH: usize = usize::MAX; const CHECKSUM_LENGTH: usize = 0; const GENERATOR_SH: [PackedNull; 5] = [PackedNull; 5]; @@ -49,6 +56,11 @@ const GEN: [u32; 5] = [0x3b6a_57b2, 0x2650_8e6d, 0x1ea1_19fa, 0x3d42_33dd, 0x2a1 impl Checksum for Bech32 { type MidstateRepr = u32; + + type CorrectionField = Fe1024; + const ROOT_GENERATOR: Self::CorrectionField = Fe1024::new([Fe32::P, Fe32::X]); + const ROOT_EXPONENTS: core::ops::RangeInclusive = 997..=999; + const CODE_LENGTH: usize = 1023; const CHECKSUM_LENGTH: usize = 6; const GENERATOR_SH: [u32; 5] = GEN; @@ -57,6 +69,11 @@ impl Checksum for Bech32 { // Same as Bech32 except TARGET_RESIDUE is different impl Checksum for Bech32m { type MidstateRepr = u32; + + type CorrectionField = Fe1024; + const ROOT_GENERATOR: Self::CorrectionField = Fe1024::new([Fe32::P, Fe32::X]); + const ROOT_EXPONENTS: core::ops::RangeInclusive = 997..=999; + const CODE_LENGTH: usize = 1023; const CHECKSUM_LENGTH: usize = 6; const GENERATOR_SH: [u32; 5] = GEN;