Skip to content

Commit

Permalink
checksum: add new error-correction fields to the Checksum trait
Browse files Browse the repository at this point in the history
  • Loading branch information
apoelstra committed Sep 18, 2024
1 parent 14e4f7b commit c389d72
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 5 deletions.
6 changes: 5 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,18 @@
//!
//! ```
//! # #[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)]
//! pub enum Codex32 {}
//!
//! 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<usize> = 77..=84;
//!
//! const CHECKSUM_LENGTH: usize = 13;
//! const CODE_LENGTH: usize = 93;
//! // Copied from BIP-93
Expand Down
35 changes: 31 additions & 4 deletions src/primitives/checksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<BaseField = Fe32>;

/// 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<usize>;

/// The length of the code.
///
/// The length of the code is how long a coded message can be (including the
Expand Down Expand Up @@ -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<Fe32>,
ExtField: super::Bech32Field + super::ExtensionField<BaseField = Fe32>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Generator polynomial as a polynomial over GF1024
Expand All @@ -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 {
Expand All @@ -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::<ExtField>())?;
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<usize> = {}..={};",
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)?;
Expand All @@ -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("}")
}
Expand Down
17 changes: 17 additions & 0 deletions src/primitives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Expand All @@ -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<usize> = 0..=0;

const CODE_LENGTH: usize = usize::MAX;
const CHECKSUM_LENGTH: usize = 0;
const GENERATOR_SH: [PackedNull; 5] = [PackedNull; 5];
Expand All @@ -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<usize> = 997..=999;

const CODE_LENGTH: usize = 1023;
const CHECKSUM_LENGTH: usize = 6;
const GENERATOR_SH: [u32; 5] = GEN;
Expand All @@ -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<usize> = 997..=999;

const CODE_LENGTH: usize = 1023;
const CHECKSUM_LENGTH: usize = 6;
const GENERATOR_SH: [u32; 5] = GEN;
Expand Down

0 comments on commit c389d72

Please sign in to comment.