1- // Bech32 character set
1+ // Bech32 character set (unchanged)
22const BECH32_ALPHABET : & [ u8 ; 32 ] = b"qpzry9x8gf2tvdw0s3jn54khce6mua7l" ;
33
4- // Generator polynomial for checksum
4+ // Generator polynomial for checksum (unchanged)
55const BECH32_CONST : u32 = 1 ;
6+ const BECH32M_CONST : u32 = 0x2bc830a3 ; // NEW: Bech32m constant
67const GENERATORS : [ u32 ; 5 ] = [ 0x3b6a57b2 , 0x26508e6d , 0x1ea119fa , 0x3d4233dd , 0x2a1462b3 ] ;
78
8- /// Convert 8-bit data to 5-bit groups
9+ #[ derive( Clone , Copy ) ]
10+ pub enum Bech32Variant {
11+ Bech32 ,
12+ Bech32m ,
13+ }
14+
15+ /// Convert 8-bit data to 5-bit groups (unchanged)
916fn convert_bits ( data : & [ u8 ] , from_bits : u8 , to_bits : u8 , pad : bool ) -> [ u8 ; 64 ] {
1017 let mut result = [ 0u8 ; 64 ] ;
1118 let mut result_len = 0 ;
@@ -39,7 +46,7 @@ fn convert_bits(data: &[u8], from_bits: u8, to_bits: u8, pad: bool) -> [u8; 64]
3946 result
4047}
4148
42- /// Polymod function for checksum calculation
49+ /// Polymod function for checksum calculation (unchanged)
4350fn polymod ( values : & [ u8 ] ) -> u32 {
4451 let mut chk = 1u32 ;
4552 for & value in values {
@@ -52,9 +59,9 @@ fn polymod(values: &[u8]) -> u32 {
5259 chk
5360}
5461
55- /// Calculate Bech32 checksum
56- fn calculate_checksum ( hrp : & [ u8 ] , data : & [ u8 ] ) -> [ u8 ; 6 ] {
57- let mut values = [ 0u8 ; 256 ] ; // Max reasonable size
62+ /// Calculate checksum (MODIFIED to support both variants)
63+ fn calculate_checksum ( hrp : & [ u8 ] , data : & [ u8 ] , variant : Bech32Variant ) -> [ u8 ; 6 ] {
64+ let mut values = [ 0u8 ; 256 ] ;
5865 let mut values_len = 0 ;
5966
6067 // Add HRP
@@ -97,17 +104,23 @@ fn calculate_checksum(hrp: &[u8], data: &[u8]) -> [u8; 6] {
97104 values_len += 1 ;
98105 }
99106
100- let mod_val = polymod ( & values[ ..values_len] ) ^ BECH32_CONST ;
107+ // Use appropriate constant based on variant
108+ let const_val = match variant {
109+ Bech32Variant :: Bech32 => BECH32_CONST ,
110+ Bech32Variant :: Bech32m => BECH32M_CONST ,
111+ } ;
112+
113+ let mod_val = polymod ( & values[ ..values_len] ) ^ const_val;
101114 let mut checksum = [ 0u8 ; 6 ] ;
102115 for i in 0 ..6 {
103116 checksum[ i] = ( ( mod_val >> ( 5 * ( 5 - i) ) ) & 31 ) as u8 ;
104117 }
105118 checksum
106119}
107120
108- /// Encode data as Bech32
109- pub fn bech32_encode ( hrp : & [ u8 ] , data : & [ u8 ] , output : & mut [ u8 ] ) -> usize {
110- let checksum = calculate_checksum ( hrp, data) ;
121+ /// Encode data as Bech32/Bech32m (MODIFIED)
122+ pub fn bech32_encode ( hrp : & [ u8 ] , data : & [ u8 ] , variant : Bech32Variant , output : & mut [ u8 ] ) -> usize {
123+ let checksum = calculate_checksum ( hrp, data, variant ) ;
111124
112125 let mut pos = 0 ;
113126
@@ -136,38 +149,88 @@ pub fn bech32_encode(hrp: &[u8], data: &[u8], output: &mut [u8]) -> usize {
136149 pos
137150}
138151
139- /// Encode Bitcoin P2WPKH address (bc1q...)
152+ /// Encode Bitcoin P2WPKH address (bc1q...) - Segwit v0
140153pub fn encode_p2wpkh_address ( pubkey_hash : & [ u8 ; 20 ] , mainnet : bool , output : & mut [ u8 ] ) -> usize {
141154 let hrp = if mainnet { b"bc" } else { b"tb" } ;
142155
143- // Create witness program: version (0) + pubkey_hash converted to 5-bit
144156 let mut witness_program = [ 0u8 ; 64 ] ;
145- witness_program[ 0 ] = 0 ; // Version 0 for P2WPKH
157+ witness_program[ 0 ] = 0 ; // Version 0
146158
147159 let converted = convert_bits ( pubkey_hash, 8 , 5 , true ) ;
148-
149- // Find actual length of converted data
150160 let converted_len = 32 ;
151161
152- // Copy converted data
153162 for i in 0 ..converted_len {
154163 witness_program[ i + 1 ] = converted[ i] ;
155164 }
156165
157- bech32_encode ( hrp, & witness_program[ ..converted_len + 1 ] , output)
166+ // Segwit v0 uses Bech32
167+ bech32_encode ( hrp, & witness_program[ ..converted_len + 1 ] , Bech32Variant :: Bech32 , output)
168+ }
169+
170+ /// NEW: Encode Bitcoin P2TR address (bc1p...) - Segwit v1 (Taproot)
171+ pub fn encode_p2tr_address ( pubkey : & [ u8 ; 32 ] , mainnet : bool , output : & mut [ u8 ] ) -> usize {
172+ let hrp = if mainnet { b"bc" } else { b"tb" } ;
173+
174+ let mut witness_program = [ 0u8 ; 64 ] ;
175+ witness_program[ 0 ] = 1 ; // Version 1 for Taproot
176+
177+ let converted = convert_bits ( pubkey, 8 , 5 , true ) ;
178+ let converted_len = 52 ; // 32 bytes -> 52 5-bit groups (with padding)
179+
180+ for i in 0 ..converted_len {
181+ witness_program[ i + 1 ] = converted[ i] ;
182+ }
183+
184+ // Segwit v1+ uses Bech32m
185+ bech32_encode ( hrp, & witness_program[ ..converted_len + 1 ] , Bech32Variant :: Bech32m , output)
186+ }
187+
188+ /// NEW: Generic witness program encoder
189+ pub fn encode_witness_program ( version : u8 , program : & [ u8 ] , mainnet : bool , output : & mut [ u8 ] ) -> usize {
190+ let hrp = if mainnet { b"bc" } else { b"tb" } ;
191+
192+ let mut witness_program = [ 0u8 ; 64 ] ;
193+ witness_program[ 0 ] = version;
194+
195+ let converted = convert_bits ( program, 8 , 5 , true ) ;
196+ // Calculate actual converted length based on program length
197+ let converted_len = ( program. len ( ) * 8 + 4 ) / 5 ;
198+
199+ for i in 0 ..converted_len {
200+ witness_program[ i + 1 ] = converted[ i] ;
201+ }
202+
203+ // Use Bech32 for v0, Bech32m for v1+
204+ let variant = if version == 0 {
205+ Bech32Variant :: Bech32
206+ } else {
207+ Bech32Variant :: Bech32m
208+ } ;
209+
210+ bech32_encode ( hrp, & witness_program[ ..converted_len + 1 ] , variant, output)
158211}
159212
160213#[ cfg( test) ]
161214mod test {
162215 use super :: * ;
163216
164217 #[ test]
165- fn should_encode_correctly ( ) {
218+ fn should_encode_p2wpkh_correctly ( ) {
166219 let public_key_hash: [ u8 ; 20 ] = hex:: decode ( "46047c8a3d8edb134c3f1a3e7d65b0fd7421f127" ) . unwrap ( ) . try_into ( ) . unwrap ( ) ;
167220 let mut encoded_public_key = [ 0u8 ; 64 ] ;
168221 let encoded_len = encode_p2wpkh_address ( & public_key_hash, true , & mut encoded_public_key) ;
169222 let encoded_public_key = & encoded_public_key[ 0 ..encoded_len] ;
170223 let expected = b"bc1qgcz8ez3a3md3xnplrgl86edsl46zruf8mwx56m" ;
171224 assert_eq ! ( * encoded_public_key, * expected) ;
172225 }
173- }
226+
227+ #[ test]
228+ fn should_encode_p2tr_correctly ( ) {
229+ let public_key: [ u8 ; 32 ] = hex:: decode ( "46047c8a3d8edb134c3f1a3e7d65b0fd7421f127ff3355433344445553111444" ) . unwrap ( ) . try_into ( ) . unwrap ( ) ;
230+ let mut encoded_public_key = [ 0u8 ; 64 ] ;
231+ let encoded_len = encode_p2tr_address ( & public_key, true , & mut encoded_public_key) ;
232+ let encoded_public_key = & encoded_public_key[ 0 ..encoded_len] ;
233+ let expected = b"bc1pgcz8ez3a3md3xnplrgl86edsl46zruf8lue42seng3z925c3z3zqc5wf5u" ;
234+ assert_eq ! ( * encoded_public_key, * expected) ;
235+ }
236+ }
0 commit comments