1use crate::error::Result;
7use crate::opcodes::*;
8use crate::types::*;
9use blvm_spec_lock::spec_locked;
10
11pub use crate::types::Witness;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum WitnessVersion {
20 SegWitV0 = 0,
22 TaprootV1 = 1,
24}
25
26#[spec_locked("11.1.2", "ValidateSegWitWitnessStructure")]
31pub fn validate_segwit_witness_structure(witness: &Witness) -> Result<bool> {
32 const MAX_WITNESS_ELEMENT_SIZE: usize = 520;
36 for element in witness {
37 if element.len() > MAX_WITNESS_ELEMENT_SIZE {
38 return Ok(false);
39 }
40 }
41 Ok(true)
42}
43
44#[spec_locked("11.2.4", "ValidateTaprootWitnessStructure")]
50pub fn validate_taproot_witness_structure(witness: &Witness, is_script_path: bool) -> Result<bool> {
51 if witness.is_empty() {
52 return Ok(false);
53 }
54
55 if is_script_path {
56 if witness.len() < 2 {
58 return Ok(false);
59 }
60
61 let control_block = &witness[witness.len() - 1];
63 if control_block.len() < 33 {
64 return Ok(false);
65 }
66
67 if (control_block.len() - 33) % 32 != 0 {
70 return Ok(false);
71 }
72 } else {
73 if witness.len() != 1 {
75 return Ok(false);
76 }
77 if witness[0].len() != 64 {
78 return Ok(false);
79 }
80 }
81
82 Ok(true)
83}
84
85#[spec_locked("11.1.1", "CalculateTransactionWeight")]
91pub fn calculate_transaction_weight_segwit(base_size: Natural, total_size: Natural) -> Natural {
92 3 * base_size + total_size
93}
94
95#[spec_locked("11.1.1", "WeightToVSize")]
103pub fn weight_to_vsize(weight: Natural) -> Natural {
104 let result = weight.div_ceil(4);
105
106 let weight_div_4 = weight / 4;
109 debug_assert!(
110 result >= weight_div_4,
111 "Vsize ({result}) must be >= weight / 4 ({weight_div_4})"
112 );
113
114 let weight_div_4_plus_1 = weight_div_4 + 1;
117 debug_assert!(
118 result <= weight_div_4_plus_1,
119 "Vsize ({result}) must be <= (weight / 4) + 1 ({weight_div_4_plus_1})"
120 );
121
122 result
125}
126
127#[spec_locked("11.1.3", "ExtractWitnessVersion")]
132pub fn extract_witness_version(script: &ByteString) -> Option<WitnessVersion> {
133 if script.len() < 4 {
136 return None;
137 }
138
139 let push_opcode = script[1];
141 let program_len = push_opcode as usize; if !(2..=40).contains(&program_len) {
143 return None;
144 }
145 if script.len() != 2 + program_len {
147 return None;
148 }
149
150 match script[0] {
151 OP_1 => Some(WitnessVersion::TaprootV1),
152 OP_0 => Some(WitnessVersion::SegWitV0),
153 _ => None,
154 }
155}
156
157#[spec_locked("11.1.3", "ExtractWitnessProgram")]
165pub fn extract_witness_program(
166 script: &ByteString,
167 _version: WitnessVersion,
168) -> Option<ByteString> {
169 if script.len() < 3 {
170 return None;
171 }
172
173 let push_opcode = script[1];
176 let program_start = 2;
177
178 if script.len() < program_start + (push_opcode as usize) {
183 return None;
184 }
185
186 Some(script[program_start..program_start + (push_opcode as usize)].to_vec())
187}
188
189#[spec_locked("11.1.3", "ValidateWitnessProgramLength")]
194pub fn validate_witness_program_length(program: &ByteString, version: WitnessVersion) -> bool {
195 use crate::constants::{SEGWIT_P2WPKH_LENGTH, SEGWIT_P2WSH_LENGTH, TAPROOT_PROGRAM_LENGTH};
196
197 match version {
198 WitnessVersion::SegWitV0 => {
199 program.len() == SEGWIT_P2WPKH_LENGTH || program.len() == SEGWIT_P2WSH_LENGTH
201 }
202 WitnessVersion::TaprootV1 => {
203 program.len() == TAPROOT_PROGRAM_LENGTH
205 }
206 }
207}
208
209#[spec_locked("11.1.2", "IsWitnessEmpty")]
211pub fn is_witness_empty(witness: &Witness) -> bool {
212 witness.is_empty() || witness.iter().all(|elem| elem.is_empty())
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218
219 #[test]
220 fn test_validate_segwit_witness_structure() {
221 let witness = vec![
222 vec![0x01; 20], vec![0x02; 72], ];
225 assert!(validate_segwit_witness_structure(&witness).unwrap());
226
227 let invalid_witness = vec![vec![0x01; crate::constants::MAX_SCRIPT_ELEMENT_SIZE + 1]];
229 assert!(!validate_segwit_witness_structure(&invalid_witness).unwrap());
230 }
231
232 #[test]
233 fn test_validate_taproot_witness_structure_key_path() {
234 let witness = vec![vec![0x01; 64]];
236 assert!(validate_taproot_witness_structure(&witness, false).unwrap());
237
238 let invalid = vec![vec![0x01; 63]];
240 assert!(!validate_taproot_witness_structure(&invalid, false).unwrap());
241
242 let invalid2 = vec![vec![0x01; 64], vec![0x02; 32]];
244 assert!(!validate_taproot_witness_structure(&invalid2, false).unwrap());
245 }
246
247 #[test]
248 fn test_validate_taproot_witness_structure_script_path() {
249 let witness = vec![
251 vec![OP_1], vec![0u8; 33], ];
254 assert!(validate_taproot_witness_structure(&witness, true).unwrap());
255
256 let invalid = vec![vec![OP_1], vec![0u8; 32]];
258 assert!(!validate_taproot_witness_structure(&invalid, true).unwrap());
259
260 let invalid2 = vec![vec![OP_1]];
262 assert!(!validate_taproot_witness_structure(&invalid2, true).unwrap());
263 }
264
265 #[test]
266 fn test_calculate_transaction_weight_segwit() {
267 let base_size = 100;
268 let total_size = 150;
269 let weight = calculate_transaction_weight_segwit(base_size, total_size);
270 assert_eq!(weight, 3 * base_size + total_size); }
272
273 #[test]
274 fn test_weight_to_vsize() {
275 assert_eq!(weight_to_vsize(400), 100); assert_eq!(weight_to_vsize(401), 101); assert_eq!(weight_to_vsize(403), 101); assert_eq!(weight_to_vsize(404), 101); }
280
281 #[test]
282 fn test_extract_witness_version() {
283 let mut segwit_script = vec![OP_0, PUSH_20_BYTES];
285 segwit_script.extend([0x01u8; 20]);
286 assert_eq!(
287 extract_witness_version(&segwit_script),
288 Some(WitnessVersion::SegWitV0)
289 );
290
291 let mut taproot_script = vec![OP_1, PUSH_32_BYTES];
293 taproot_script.extend([0x02u8; 32]);
294 assert_eq!(
295 extract_witness_version(&taproot_script),
296 Some(WitnessVersion::TaprootV1)
297 );
298
299 let non_witness_script = vec![OP_DUP, OP_HASH160]; assert_eq!(extract_witness_version(&non_witness_script), None);
301 }
302
303 #[test]
304 fn test_extract_witness_program() {
305 let segwit_script = vec![
310 OP_0,
311 PUSH_20_BYTES,
312 0x01,
313 0x02,
314 0x03,
315 0x04,
316 0x05,
317 0x06,
318 0x07,
319 0x08,
320 0x09,
321 0x0a,
322 0x0b,
323 0x0c,
324 0x0d,
325 0x0e,
326 0x0f,
327 0x10,
328 0x11,
329 0x12,
330 0x13,
331 PUSH_20_BYTES,
332 ];
333 let program = extract_witness_program(&segwit_script, WitnessVersion::SegWitV0);
334 assert_eq!(
336 program,
337 Some(vec![
338 0x01,
339 0x02,
340 0x03,
341 0x04,
342 0x05,
343 0x06,
344 0x07,
345 0x08,
346 0x09,
347 0x0a,
348 0x0b,
349 0x0c,
350 0x0d,
351 0x0e,
352 0x0f,
353 0x10,
354 0x11,
355 0x12,
356 0x13,
357 PUSH_20_BYTES
358 ])
359 );
360 }
361
362 #[test]
363 fn test_validate_witness_program_length() {
364 let p2wpkh = vec![0u8; 20]; assert!(validate_witness_program_length(
366 &p2wpkh,
367 WitnessVersion::SegWitV0
368 ));
369
370 let p2wsh = vec![0u8; 32]; assert!(validate_witness_program_length(
372 &p2wsh,
373 WitnessVersion::SegWitV0
374 ));
375
376 let p2tr = vec![0u8; 32]; assert!(validate_witness_program_length(
378 &p2tr,
379 WitnessVersion::TaprootV1
380 ));
381
382 let invalid = vec![0u8; 33];
383 assert!(!validate_witness_program_length(
384 &invalid,
385 WitnessVersion::SegWitV0
386 ));
387 assert!(!validate_witness_program_length(
388 &invalid,
389 WitnessVersion::TaprootV1
390 ));
391 }
392
393 #[test]
394 fn test_is_witness_empty() {
395 assert!(is_witness_empty(&vec![]));
396 assert!(is_witness_empty(&vec![vec![]]));
397 assert!(!is_witness_empty(&vec![vec![0x01]]));
398 }
399}