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![vec![0x01; 64]
266 .into_iter()
267 .chain([0x01u8])
268 .collect::<Vec<_>>()];
269 assert!(validate_taproot_witness_structure(&with_hashtype, false).unwrap());
270
271 let invalid_hashtype = vec![vec![0x01; 64]
273 .into_iter()
274 .chain([0x00u8])
275 .collect::<Vec<_>>()];
276 assert!(!validate_taproot_witness_structure(&invalid_hashtype, false).unwrap());
277
278 let invalid2 = vec![vec![0x01; 64], vec![0x02; 32]];
280 assert!(!validate_taproot_witness_structure(&invalid2, false).unwrap());
281 }
282
283 #[test]
284 fn test_validate_taproot_witness_structure_script_path() {
285 let witness = vec![
287 vec![OP_1], vec![0u8; 33], ];
290 assert!(validate_taproot_witness_structure(&witness, true).unwrap());
291
292 let invalid = vec![vec![OP_1], vec![0u8; 32]];
294 assert!(!validate_taproot_witness_structure(&invalid, true).unwrap());
295
296 let invalid2 = vec![vec![OP_1]];
298 assert!(!validate_taproot_witness_structure(&invalid2, true).unwrap());
299 }
300
301 #[test]
302 fn test_calculate_transaction_weight_segwit() {
303 let base_size = 100;
304 let total_size = 150;
305 let weight = calculate_transaction_weight_segwit(base_size, total_size);
306 assert_eq!(weight, 3 * base_size + total_size); }
308
309 #[test]
310 fn test_weight_to_vsize() {
311 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); }
316
317 #[test]
318 fn test_extract_witness_version() {
319 let mut segwit_script = vec![OP_0, PUSH_20_BYTES];
321 segwit_script.extend([0x01u8; 20]);
322 assert_eq!(
323 extract_witness_version(&segwit_script),
324 Some(WitnessVersion::SegWitV0)
325 );
326
327 let mut taproot_script = vec![OP_1, PUSH_32_BYTES];
329 taproot_script.extend([0x02u8; 32]);
330 assert_eq!(
331 extract_witness_version(&taproot_script),
332 Some(WitnessVersion::TaprootV1)
333 );
334
335 let non_witness_script = vec![OP_DUP, OP_HASH160]; assert_eq!(extract_witness_version(&non_witness_script), None);
337 }
338
339 #[test]
340 fn test_extract_witness_program() {
341 let segwit_script = vec![
346 OP_0,
347 PUSH_20_BYTES,
348 0x01,
349 0x02,
350 0x03,
351 0x04,
352 0x05,
353 0x06,
354 0x07,
355 0x08,
356 0x09,
357 0x0a,
358 0x0b,
359 0x0c,
360 0x0d,
361 0x0e,
362 0x0f,
363 0x10,
364 0x11,
365 0x12,
366 0x13,
367 PUSH_20_BYTES,
368 ];
369 let program = extract_witness_program(&segwit_script, WitnessVersion::SegWitV0);
370 assert_eq!(
372 program,
373 Some(vec![
374 0x01,
375 0x02,
376 0x03,
377 0x04,
378 0x05,
379 0x06,
380 0x07,
381 0x08,
382 0x09,
383 0x0a,
384 0x0b,
385 0x0c,
386 0x0d,
387 0x0e,
388 0x0f,
389 0x10,
390 0x11,
391 0x12,
392 0x13,
393 PUSH_20_BYTES
394 ])
395 );
396 }
397
398 #[test]
399 fn test_validate_witness_program_length() {
400 let p2wpkh = vec![0u8; 20]; assert!(validate_witness_program_length(
402 &p2wpkh,
403 WitnessVersion::SegWitV0
404 ));
405
406 let p2wsh = vec![0u8; 32]; assert!(validate_witness_program_length(
408 &p2wsh,
409 WitnessVersion::SegWitV0
410 ));
411
412 let p2tr = vec![0u8; 32]; assert!(validate_witness_program_length(
414 &p2tr,
415 WitnessVersion::TaprootV1
416 ));
417
418 let invalid = vec![0u8; 33];
419 assert!(!validate_witness_program_length(
420 &invalid,
421 WitnessVersion::SegWitV0
422 ));
423 assert!(!validate_witness_program_length(
424 &invalid,
425 WitnessVersion::TaprootV1
426 ));
427 }
428
429 #[test]
430 fn test_is_witness_empty() {
431 assert!(is_witness_empty(&vec![]));
432 assert!(is_witness_empty(&vec![vec![]]));
433 assert!(!is_witness_empty(&vec![vec![0x01]]));
434 }
435}