7
7
//!
8
8
//! [BIP-380]: <https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki>
9
9
10
+ use core:: convert:: TryFrom ;
10
11
use core:: fmt;
11
12
use core:: iter:: FromIterator ;
12
13
14
+ use bech32:: primitives:: checksum:: PackedFe32 ;
15
+ use bech32:: { Checksum , Fe32 } ;
16
+
13
17
pub use crate :: expression:: VALID_CHARS ;
14
18
use crate :: prelude:: * ;
15
19
use crate :: Error ;
16
20
17
- const CHECKSUM_CHARSET : & [ u8 ] = b"qpzry9x8gf2tvdw0s3jn54khce6mua7l" ;
18
-
19
21
const CHECKSUM_LENGTH : usize = 8 ;
20
22
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
-
44
23
/// Compute the checksum of a descriptor.
45
24
///
46
25
/// 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> {
78
57
79
58
/// An engine to compute a checksum from a string.
80
59
pub struct Engine {
81
- c : u64 ,
60
+ inner : bech32 :: primitives :: checksum :: Engine < DescriptorChecksum > ,
82
61
cls : u64 ,
83
62
clscount : u64 ,
84
63
}
@@ -89,7 +68,9 @@ impl Default for Engine {
89
68
90
69
impl Engine {
91
70
/// 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
+ }
93
74
94
75
/// Inputs some data into the checksum engine.
95
76
///
@@ -105,11 +86,15 @@ impl Engine {
105
86
. ok_or_else ( || {
106
87
Error :: BadDescriptor ( format ! ( "Invalid character in checksum: '{}'" , ch) )
107
88
} ) ? 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
+
109
93
self . cls = self . cls * 3 + ( pos >> 5 ) ;
110
94
self . clscount += 1 ;
111
95
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) ;
113
98
self . cls = 0 ;
114
99
self . clscount = 0 ;
115
100
}
@@ -121,14 +106,19 @@ impl Engine {
121
106
/// engine without allocating, to get a string use [`Self::checksum`].
122
107
pub fn checksum_chars ( & mut self ) -> [ char ; CHECKSUM_LENGTH ] {
123
108
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) ;
125
111
}
126
- ( 0 ..CHECKSUM_LENGTH ) . for_each ( |_| self . c = poly_mod ( self . c , 0 ) ) ;
127
- self . c ^= 1 ;
112
+ self . inner . input_target_residue ( ) ;
128
113
129
114
let mut chars = [ 0 as char ; CHECKSUM_LENGTH ] ;
115
+ let mut checksum_remaining = CHECKSUM_LENGTH ;
116
+
130
117
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 ( ) ;
132
122
}
133
123
chars
134
124
}
@@ -139,6 +129,23 @@ impl Engine {
139
129
}
140
130
}
141
131
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
+
142
149
/// A wrapper around a `fmt::Formatter` which provides checksumming ability.
143
150
pub struct Formatter < ' f , ' a > {
144
151
fmt : & ' f mut fmt:: Formatter < ' a > ,
0 commit comments