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 let len = witness[0].len();
78 if len != 64 && len != 65 {
79 return Ok(false);
80 }
81 if len == 65 && witness[0][64] == 0x00 {
82 return Ok(false);
83 }
84 }
85
86 Ok(true)
87}
88
89#[spec_locked("11.1.1", "CalculateTransactionWeight")]
98#[blvm_spec_lock::requires(total_size >= base_size)]
99#[blvm_spec_lock::ensures(result >= total_size)]
100#[blvm_spec_lock::ensures(result >= 4 * base_size)]
101pub fn calculate_transaction_weight_segwit(base_size: Natural, total_size: Natural) -> Natural {
102 3 * base_size + total_size
103}
104
105#[spec_locked("11.1.1", "WeightToVSize")]
115#[blvm_spec_lock::ensures(result * 4 >= weight)]
116#[blvm_spec_lock::ensures(result <= weight)]
117pub fn weight_to_vsize(weight: Natural) -> Natural {
118 let result = weight.div_ceil(4);
119
120 let weight_div_4 = weight / 4;
123 debug_assert!(
124 result >= weight_div_4,
125 "Vsize ({result}) must be >= weight / 4 ({weight_div_4})"
126 );
127
128 let weight_div_4_plus_1 = weight_div_4 + 1;
131 debug_assert!(
132 result <= weight_div_4_plus_1,
133 "Vsize ({result}) must be <= (weight / 4) + 1 ({weight_div_4_plus_1})"
134 );
135
136 result
139}
140
141#[spec_locked("11.1.3", "ExtractWitnessVersion")]
146pub fn extract_witness_version(script: &ByteString) -> Option<WitnessVersion> {
147 if script.len() < 4 {
150 return None;
151 }
152
153 let push_opcode = script[1];
155 let program_len = push_opcode as usize; if !(2..=40).contains(&program_len) {
157 return None;
158 }
159 if script.len() != 2 + program_len {
161 return None;
162 }
163
164 match script[0] {
165 OP_1 => Some(WitnessVersion::TaprootV1),
166 OP_0 => Some(WitnessVersion::SegWitV0),
167 _ => None,
168 }
169}
170
171#[spec_locked("11.1.3", "ExtractWitnessProgram")]
179pub fn extract_witness_program(
180 script: &ByteString,
181 _version: WitnessVersion,
182) -> Option<ByteString> {
183 if script.len() < 3 {
184 return None;
185 }
186
187 let push_opcode = script[1];
190 let program_start = 2;
191
192 if script.len() < program_start + (push_opcode as usize) {
197 return None;
198 }
199
200 Some(script[program_start..program_start + (push_opcode as usize)].to_vec())
201}
202
203#[spec_locked("11.1.3", "ValidateWitnessProgramLength")]
211#[blvm_spec_lock::ensures(result == false || program.len() == 20 || program.len() == 32)]
212pub fn validate_witness_program_length(program: &ByteString, version: WitnessVersion) -> bool {
213 use crate::constants::{SEGWIT_P2WPKH_LENGTH, SEGWIT_P2WSH_LENGTH, TAPROOT_PROGRAM_LENGTH};
214
215 match version {
216 WitnessVersion::SegWitV0 => {
217 program.len() == SEGWIT_P2WPKH_LENGTH || program.len() == SEGWIT_P2WSH_LENGTH
219 }
220 WitnessVersion::TaprootV1 => {
221 program.len() == TAPROOT_PROGRAM_LENGTH
223 }
224 }
225}
226
227#[spec_locked("11.1.2", "IsWitnessEmpty")]
232#[blvm_spec_lock::ensures(result == true || witness.len() > 0)]
233pub fn is_witness_empty(witness: &Witness) -> bool {
234 witness.is_empty() || witness.iter().all(|elem| elem.is_empty())
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn test_validate_segwit_witness_structure() {
243 let witness = vec![
244 vec![0x01; 20], vec![0x02; 72], ];
247 assert!(validate_segwit_witness_structure(&witness).unwrap());
248
249 let invalid_witness = vec![vec![0x01; crate::constants::MAX_SCRIPT_ELEMENT_SIZE + 1]];
251 assert!(!validate_segwit_witness_structure(&invalid_witness).unwrap());
252 }
253
254 #[test]
255 fn test_validate_taproot_witness_structure_key_path() {
256 let witness = vec![vec![0x01; 64]];
258 assert!(validate_taproot_witness_structure(&witness, false).unwrap());
259
260 let invalid = vec![vec![0x01; 63]];
262 assert!(!validate_taproot_witness_structure(&invalid, false).unwrap());
263
264 let with_hashtype = vec![
266 vec![0x01; 64]
267 .into_iter()
268 .chain([0x01u8])
269 .collect::<Vec<_>>(),
270 ];
271 assert!(validate_taproot_witness_structure(&with_hashtype, false).unwrap());
272
273 let invalid_hashtype = vec![
275 vec![0x01; 64]
276 .into_iter()
277 .chain([0x00u8])
278 .collect::<Vec<_>>(),
279 ];
280 assert!(!validate_taproot_witness_structure(&invalid_hashtype, false).unwrap());
281
282 let invalid2 = vec![vec![0x01; 64], vec![0x02; 32]];
284 assert!(!validate_taproot_witness_structure(&invalid2, false).unwrap());
285 }
286
287 #[test]
288 fn test_validate_taproot_witness_structure_script_path() {
289 let witness = vec![
291 vec![OP_1], vec![0u8; 33], ];
294 assert!(validate_taproot_witness_structure(&witness, true).unwrap());
295
296 let invalid = vec![vec![OP_1], vec![0u8; 32]];
298 assert!(!validate_taproot_witness_structure(&invalid, true).unwrap());
299
300 let invalid2 = vec![vec![OP_1]];
302 assert!(!validate_taproot_witness_structure(&invalid2, true).unwrap());
303 }
304
305 #[test]
306 fn test_calculate_transaction_weight_segwit() {
307 let base_size = 100;
308 let total_size = 150;
309 let weight = calculate_transaction_weight_segwit(base_size, total_size);
310 assert_eq!(weight, 3 * base_size + total_size); }
312
313 #[test]
314 fn test_weight_to_vsize() {
315 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); }
320
321 #[test]
322 fn test_extract_witness_version() {
323 let mut segwit_script = vec![OP_0, PUSH_20_BYTES];
325 segwit_script.extend([0x01u8; 20]);
326 assert_eq!(
327 extract_witness_version(&segwit_script),
328 Some(WitnessVersion::SegWitV0)
329 );
330
331 let mut taproot_script = vec![OP_1, PUSH_32_BYTES];
333 taproot_script.extend([0x02u8; 32]);
334 assert_eq!(
335 extract_witness_version(&taproot_script),
336 Some(WitnessVersion::TaprootV1)
337 );
338
339 let non_witness_script = vec![OP_DUP, OP_HASH160]; assert_eq!(extract_witness_version(&non_witness_script), None);
341 }
342
343 #[test]
344 fn test_extract_witness_program() {
345 let segwit_script = vec![
350 OP_0,
351 PUSH_20_BYTES,
352 0x01,
353 0x02,
354 0x03,
355 0x04,
356 0x05,
357 0x06,
358 0x07,
359 0x08,
360 0x09,
361 0x0a,
362 0x0b,
363 0x0c,
364 0x0d,
365 0x0e,
366 0x0f,
367 0x10,
368 0x11,
369 0x12,
370 0x13,
371 PUSH_20_BYTES,
372 ];
373 let program = extract_witness_program(&segwit_script, WitnessVersion::SegWitV0);
374 assert_eq!(
376 program,
377 Some(vec![
378 0x01,
379 0x02,
380 0x03,
381 0x04,
382 0x05,
383 0x06,
384 0x07,
385 0x08,
386 0x09,
387 0x0a,
388 0x0b,
389 0x0c,
390 0x0d,
391 0x0e,
392 0x0f,
393 0x10,
394 0x11,
395 0x12,
396 0x13,
397 PUSH_20_BYTES
398 ])
399 );
400 }
401
402 #[test]
403 fn test_validate_witness_program_length() {
404 let p2wpkh = vec![0u8; 20]; assert!(validate_witness_program_length(
406 &p2wpkh,
407 WitnessVersion::SegWitV0
408 ));
409
410 let p2wsh = vec![0u8; 32]; assert!(validate_witness_program_length(
412 &p2wsh,
413 WitnessVersion::SegWitV0
414 ));
415
416 let p2tr = vec![0u8; 32]; assert!(validate_witness_program_length(
418 &p2tr,
419 WitnessVersion::TaprootV1
420 ));
421
422 let invalid = vec![0u8; 33];
423 assert!(!validate_witness_program_length(
424 &invalid,
425 WitnessVersion::SegWitV0
426 ));
427 assert!(!validate_witness_program_length(
428 &invalid,
429 WitnessVersion::TaprootV1
430 ));
431 }
432
433 #[test]
434 fn test_is_witness_empty() {
435 assert!(is_witness_empty(&vec![]));
436 assert!(is_witness_empty(&vec![vec![]]));
437 assert!(!is_witness_empty(&vec![vec![0x01]]));
438 }
439}