Skip to content

Commit 7a629c6

Browse files
committed
Merge #108: Add checksum module
10e32e9 Add checksum module (Tobin C. Harding) Pull request description: Add a module `checksum` that implements BCH codes. Use the new module in place of the current checksum logic within the `Bech32Writer`. Please note, this PR does not remove _all_ the checksum related logic from `lib.rs` - checksum verification is still present. Will attempt that if this merges. ACKs for top commit: apoelstra: ACK 10e32e9 Tree-SHA512: 8278419ff1d735511ec43711e2456d7246591265950319575d4ac803115b4c23bb3bde72bdeb7e345c1fc8e07987b2183c16259ad69a8ab99101bb9a9afe98bd
2 parents c915def + 10e32e9 commit 7a629c6

File tree

3 files changed

+305
-63
lines changed

3 files changed

+305
-63
lines changed

src/lib.rs

Lines changed: 51 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,11 @@ use alloc::{string::String, vec::Vec};
5151
use core::convert::{Infallible, TryFrom};
5252
use core::{fmt, mem};
5353

54+
pub use crate::primitives::checksum::Checksum;
55+
use crate::primitives::checksum::{self, PackedFe32};
5456
use crate::primitives::hrp;
5557
pub use crate::primitives::hrp::Hrp;
58+
pub use crate::primitives::{Bech32, Bech32m};
5659

5760
mod error;
5861
pub mod primitives;
@@ -101,50 +104,26 @@ const CHECKSUM_LENGTH: usize = 6;
101104

102105
/// Allocationless Bech32 writer that accumulates the checksum data internally and writes them out
103106
/// in the end.
104-
pub struct Bech32Writer<'a> {
107+
pub struct Bech32Writer<'a, Ck: Checksum> {
105108
formatter: &'a mut dyn fmt::Write,
106-
chk: u32,
107-
variant: Variant,
109+
engine: checksum::Engine<Ck>,
108110
}
109111

110-
impl<'a> Bech32Writer<'a> {
112+
impl<'a, Ck: Checksum> Bech32Writer<'a, Ck> {
111113
/// Creates a new writer that can write a bech32 string without allocating itself.
112114
///
113115
/// This is a rather low-level API and doesn't check the HRP or data length for standard
114116
/// compliance.
115-
pub fn new(
116-
hrp: Hrp,
117-
variant: Variant,
118-
fmt: &'a mut dyn fmt::Write,
119-
) -> Result<Bech32Writer<'a>, fmt::Error> {
120-
let mut writer = Bech32Writer { formatter: fmt, chk: 1, variant };
117+
fn new(hrp: Hrp, fmt: &'a mut dyn fmt::Write) -> Result<Bech32Writer<'a, Ck>, fmt::Error> {
118+
let mut engine = checksum::Engine::new();
119+
engine.input_hrp(&hrp);
121120

122121
for c in hrp.lowercase_char_iter() {
123-
writer.formatter.write_char(c)?;
124-
}
125-
writer.formatter.write_char(SEP)?;
126-
127-
// expand HRP
128-
for b in hrp.lowercase_byte_iter() {
129-
writer.polymod_step(u5(b >> 5));
122+
fmt.write_char(c)?;
130123
}
131-
writer.polymod_step(u5(0));
132-
for b in hrp.lowercase_byte_iter() {
133-
writer.polymod_step(u5(b & 0x1f));
134-
}
135-
136-
Ok(writer)
137-
}
138-
139-
fn polymod_step(&mut self, v: u5) {
140-
let b = (self.chk >> 25) as u8;
141-
self.chk = (self.chk & 0x01ff_ffff) << 5 ^ (u32::from(*v.as_ref()));
124+
fmt.write_char(SEP)?;
142125

143-
for (i, item) in GEN.iter().enumerate() {
144-
if (b >> i) & 1 == 1 {
145-
self.chk ^= item;
146-
}
147-
}
126+
Ok(Bech32Writer { formatter: fmt, engine })
148127
}
149128

150129
/// Writes out the checksum at the end.
@@ -158,31 +137,31 @@ impl<'a> Bech32Writer<'a> {
158137

159138
/// Calculates and writes a checksum to `self`.
160139
fn write_checksum(&mut self) -> fmt::Result {
161-
// Pad with 6 zeros
162-
for _ in 0..CHECKSUM_LENGTH {
163-
self.polymod_step(u5(0))
164-
}
140+
self.engine.input_target_residue();
165141

166-
let plm: u32 = self.chk ^ self.variant.constant();
142+
let mut checksum_remaining = self::CHECKSUM_LENGTH;
143+
while checksum_remaining > 0 {
144+
checksum_remaining -= 1;
167145

168-
for p in 0..CHECKSUM_LENGTH {
169-
self.formatter.write_char(u5(((plm >> (5 * (5 - p))) & 0x1f) as u8).to_char())?;
146+
let fe = u5::try_from(self.engine.residue().unpack(checksum_remaining))
147+
.expect("unpack returns valid field element");
148+
self.formatter.write_char(fe.to_char())?;
170149
}
171150

172151
Ok(())
173152
}
174153
}
175154

176-
impl<'a> WriteBase32 for Bech32Writer<'a> {
155+
impl<'a, Ck: Checksum> WriteBase32 for Bech32Writer<'a, Ck> {
177156
type Error = fmt::Error;
178157

179158
fn write_u5(&mut self, data: u5) -> fmt::Result {
180-
self.polymod_step(data);
159+
self.engine.input_fe(data);
181160
self.formatter.write_char(data.to_char())
182161
}
183162
}
184163

185-
impl<'a> Drop for Bech32Writer<'a> {
164+
impl<'a, Ck: Checksum> Drop for Bech32Writer<'a, Ck> {
186165
fn drop(&mut self) {
187166
self.write_checksum().expect("Unhandled error writing the checksum on drop.")
188167
}
@@ -441,14 +420,31 @@ pub fn encode_to_fmt_anycase<T: AsRef<[u5]>>(
441420
data: T,
442421
variant: Variant,
443422
) -> Result<fmt::Result, Error> {
444-
match Bech32Writer::new(hrp, variant, fmt) {
445-
Ok(mut writer) => {
446-
Ok(writer.write(data.as_ref()).and_then(|_| {
447-
// Finalize manually to avoid panic on drop if write fails
448-
writer.finalize()
449-
}))
423+
match variant {
424+
Variant::Bech32 => {
425+
let res = Bech32Writer::<Bech32>::new(hrp, fmt);
426+
match res {
427+
Ok(mut writer) => {
428+
Ok(writer.write(data.as_ref()).and_then(|_| {
429+
// Finalize manually to avoid panic on drop if write fails
430+
writer.finalize()
431+
}))
432+
}
433+
Err(e) => Ok(Err(e)),
434+
}
435+
}
436+
Variant::Bech32m => {
437+
let res = Bech32Writer::<Bech32m>::new(hrp, fmt);
438+
match res {
439+
Ok(mut writer) => {
440+
Ok(writer.write(data.as_ref()).and_then(|_| {
441+
// Finalize manually to avoid panic on drop if write fails
442+
writer.finalize()
443+
}))
444+
}
445+
Err(e) => Ok(Err(e)),
446+
}
450447
}
451-
Err(e) => Ok(Err(e)),
452448
}
453449
}
454450

@@ -502,13 +498,6 @@ impl Variant {
502498
_ => None,
503499
}
504500
}
505-
506-
fn constant(self) -> u32 {
507-
match self {
508-
Variant::Bech32 => BECH32_CONST,
509-
Variant::Bech32m => BECH32M_CONST,
510-
}
511-
}
512501
}
513502

514503
/// Encodes a bech32 payload to string.
@@ -1118,7 +1107,7 @@ mod tests {
11181107

11191108
let mut written_str = String::new();
11201109
{
1121-
let mut writer = Bech32Writer::new(hrp, Variant::Bech32, &mut written_str).unwrap();
1110+
let mut writer = Bech32Writer::<Bech32>::new(hrp, &mut written_str).unwrap();
11221111
writer.write(&data).unwrap();
11231112
writer.finalize().unwrap();
11241113
}
@@ -1136,7 +1125,7 @@ mod tests {
11361125

11371126
let mut written_str = String::new();
11381127
{
1139-
let mut writer = Bech32Writer::new(hrp, Variant::Bech32, &mut written_str).unwrap();
1128+
let mut writer = Bech32Writer::<Bech32>::new(hrp, &mut written_str).unwrap();
11401129
writer.write(&data).unwrap();
11411130
}
11421131

@@ -1153,7 +1142,7 @@ mod tests {
11531142

11541143
let mut written_str = String::new();
11551144
{
1156-
let mut writer = Bech32Writer::new(hrp, Variant::Bech32, &mut written_str).unwrap();
1145+
let mut writer = Bech32Writer::<Bech32>::new(hrp, &mut written_str).unwrap();
11571146
writer.write(&data).unwrap();
11581147
}
11591148

@@ -1258,9 +1247,8 @@ mod tests {
12581247
let data: Vec<u8> = FromBase32::from_base32(&data[1..]).expect("failed to convert u5s");
12591248

12601249
let mut writer = String::new();
1261-
let mut bech32_writer =
1262-
Bech32Writer::new(Hrp::parse("BC").unwrap(), Variant::Bech32, &mut writer)
1263-
.expect("failed to write hrp");
1250+
let mut bech32_writer = Bech32Writer::<Bech32>::new(Hrp::parse("BC").unwrap(), &mut writer)
1251+
.expect("failed to write hrp");
12641252
let version = u5::try_from(0).unwrap();
12651253

12661254
WriteBase32::write_u5(&mut bech32_writer, version).expect("failed to write version");

0 commit comments

Comments
 (0)