77//!
88//! [BIP-380]: <https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki>
99
10+ use core:: convert:: TryFrom ;
1011use core:: fmt;
1112use core:: iter:: FromIterator ;
1213
14+ use bech32:: primitives:: checksum:: PackedFe32 ;
15+ use bech32:: { Checksum , Fe32 } ;
16+
1317pub use crate :: expression:: VALID_CHARS ;
1418use crate :: prelude:: * ;
1519use crate :: Error ;
1620
17- const CHECKSUM_CHARSET : & [ u8 ] = b"qpzry9x8gf2tvdw0s3jn54khce6mua7l" ;
18-
1921const CHECKSUM_LENGTH : usize = 8 ;
2022
21- fn poly_mod ( mut c : u64 , val : u64 ) -> u64 {
22- let c0 = c >> 35 ;
23-
24- c = ( ( c & 0x7ffffffff ) << 5 ) ^ val;
25- if c0 & 1 > 0 {
26- c ^= 0xf5dee51989
27- } ;
28- if c0 & 2 > 0 {
29- c ^= 0xa9fdca3312
30- } ;
31- if c0 & 4 > 0 {
32- c ^= 0x1bab10e32d
33- } ;
34- if c0 & 8 > 0 {
35- c ^= 0x3706b1677a
36- } ;
37- if c0 & 16 > 0 {
38- c ^= 0x644d626ffd
39- } ;
40-
41- c
42- }
43-
4423/// Compute the checksum of a descriptor.
4524///
4625/// Note that this function does not check if the descriptor string is
@@ -78,7 +57,7 @@ pub(super) fn verify_checksum(s: &str) -> Result<&str, Error> {
7857
7958/// An engine to compute a checksum from a string.
8059pub struct Engine {
81- c : u64 ,
60+ inner : bech32 :: primitives :: checksum :: Engine < DescriptorChecksum > ,
8261 cls : u64 ,
8362 clscount : u64 ,
8463}
@@ -89,7 +68,9 @@ impl Default for Engine {
8968
9069impl Engine {
9170 /// Constructs an engine with no input.
92- pub fn new ( ) -> Self { Engine { c : 1 , cls : 0 , clscount : 0 } }
71+ pub fn new ( ) -> Self {
72+ Engine { inner : bech32:: primitives:: checksum:: Engine :: new ( ) , cls : 0 , clscount : 0 }
73+ }
9374
9475 /// Inputs some data into the checksum engine.
9576 ///
@@ -105,11 +86,15 @@ impl Engine {
10586 . ok_or_else ( || {
10687 Error :: BadDescriptor ( format ! ( "Invalid character in checksum: '{}'" , ch) )
10788 } ) ? as u64 ;
108- self . c = poly_mod ( self . c , pos & 31 ) ;
89+
90+ let fe = Fe32 :: try_from ( pos & 31 ) . expect ( "pos is valid because of the mask" ) ;
91+ self . inner . input_fe ( fe) ;
92+
10993 self . cls = self . cls * 3 + ( pos >> 5 ) ;
11094 self . clscount += 1 ;
11195 if self . clscount == 3 {
112- self . c = poly_mod ( self . c , self . cls ) ;
96+ let fe = Fe32 :: try_from ( self . cls ) . expect ( "cls is valid" ) ;
97+ self . inner . input_fe ( fe) ;
11398 self . cls = 0 ;
11499 self . clscount = 0 ;
115100 }
@@ -121,14 +106,19 @@ impl Engine {
121106 /// engine without allocating, to get a string use [`Self::checksum`].
122107 pub fn checksum_chars ( & mut self ) -> [ char ; CHECKSUM_LENGTH ] {
123108 if self . clscount > 0 {
124- self . c = poly_mod ( self . c , self . cls ) ;
109+ let fe = Fe32 :: try_from ( self . cls ) . expect ( "cls is valid" ) ;
110+ self . inner . input_fe ( fe) ;
125111 }
126- ( 0 ..CHECKSUM_LENGTH ) . for_each ( |_| self . c = poly_mod ( self . c , 0 ) ) ;
127- self . c ^= 1 ;
112+ self . inner . input_target_residue ( ) ;
128113
129114 let mut chars = [ 0 as char ; CHECKSUM_LENGTH ] ;
115+ let mut checksum_remaining = CHECKSUM_LENGTH ;
116+
130117 for j in 0 ..CHECKSUM_LENGTH {
131- chars[ j] = CHECKSUM_CHARSET [ ( ( self . c >> ( 5 * ( 7 - j) ) ) & 31 ) as usize ] as char ;
118+ checksum_remaining -= 1 ;
119+ let unpacked = self . inner . residue ( ) . unpack ( checksum_remaining) ;
120+ let fe = Fe32 :: try_from ( unpacked) . expect ( "5 bits fits in an fe32" ) ;
121+ chars[ j] = fe. to_char ( ) ;
132122 }
133123 chars
134124 }
@@ -139,6 +129,23 @@ impl Engine {
139129 }
140130}
141131
132+ /// The Output Script Descriptor checksum algorithm, defined in [BIP-380].
133+ ///
134+ /// [BIP-380]: <https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki>
135+ #[ derive( Copy , Clone , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
136+ enum DescriptorChecksum { }
137+
138+ /// Generator coefficients, taken from BIP-380.
139+ #[ rustfmt:: skip]
140+ const GEN : [ u64 ; 5 ] = [ 0xf5dee51989 , 0xa9fdca3312 , 0x1bab10e32d , 0x3706b1677a , 0x644d626ffd ] ;
141+
142+ impl Checksum for DescriptorChecksum {
143+ type MidstateRepr = u64 ; // We need 40 bits (8 * 5).
144+ const CHECKSUM_LENGTH : usize = CHECKSUM_LENGTH ;
145+ const GENERATOR_SH : [ u64 ; 5 ] = GEN ;
146+ const TARGET_RESIDUE : u64 = 1 ;
147+ }
148+
142149/// A wrapper around a `fmt::Formatter` which provides checksumming ability.
143150pub struct Formatter < ' f , ' a > {
144151 fmt : & ' f mut fmt:: Formatter < ' a > ,
0 commit comments