Skip to content

Commit cb743f7

Browse files
committed
taproot
1 parent b5c1ef7 commit cb743f7

1 file changed

Lines changed: 83 additions & 20 deletions

File tree

logic/src/bech32.rs

Lines changed: 83 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
// Bech32 character set
1+
// Bech32 character set (unchanged)
22
const BECH32_ALPHABET: &[u8; 32] = b"qpzry9x8gf2tvdw0s3jn54khce6mua7l";
33

4-
// Generator polynomial for checksum
4+
// Generator polynomial for checksum (unchanged)
55
const BECH32_CONST: u32 = 1;
6+
const BECH32M_CONST: u32 = 0x2bc830a3; // NEW: Bech32m constant
67
const 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)
916
fn 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)
4350
fn 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
140153
pub 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)]
161214
mod 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

Comments
 (0)