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")]
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")]
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")]
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")]
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")]
132pub fn extract_witness_version(script: &ByteString) -> Option<WitnessVersion> {
133 if script.is_empty() {
134 return None;
135 }
136
137 match script[0] {
138 OP_1 => Some(WitnessVersion::TaprootV1),
139 OP_0 => Some(WitnessVersion::SegWitV0),
140 _ => None,
141 }
142}
143
144#[spec_locked("11.1.3")]
152pub fn extract_witness_program(
153 script: &ByteString,
154 _version: WitnessVersion,
155) -> Option<ByteString> {
156 if script.len() < 3 {
157 return None;
158 }
159
160 let push_opcode = script[1];
163 let program_start = 2;
164
165 if script.len() < program_start + (push_opcode as usize) {
170 return None;
171 }
172
173 Some(script[program_start..program_start + (push_opcode as usize)].to_vec())
174}
175
176#[spec_locked("11.1.3")]
181pub fn validate_witness_program_length(program: &ByteString, version: WitnessVersion) -> bool {
182 use crate::constants::{SEGWIT_P2WPKH_LENGTH, SEGWIT_P2WSH_LENGTH, TAPROOT_PROGRAM_LENGTH};
183
184 match version {
185 WitnessVersion::SegWitV0 => {
186 program.len() == SEGWIT_P2WPKH_LENGTH || program.len() == SEGWIT_P2WSH_LENGTH
188 }
189 WitnessVersion::TaprootV1 => {
190 program.len() == TAPROOT_PROGRAM_LENGTH
192 }
193 }
194}
195
196#[spec_locked("11.1.2")]
198pub fn is_witness_empty(witness: &Witness) -> bool {
199 witness.is_empty() || witness.iter().all(|elem| elem.is_empty())
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[test]
207 fn test_validate_segwit_witness_structure() {
208 let witness = vec![
209 vec![0x01; 20], vec![0x02; 72], ];
212 assert!(validate_segwit_witness_structure(&witness).unwrap());
213
214 let invalid_witness = vec![vec![0x01; crate::constants::MAX_SCRIPT_ELEMENT_SIZE + 1]];
216 assert!(!validate_segwit_witness_structure(&invalid_witness).unwrap());
217 }
218
219 #[test]
220 fn test_validate_taproot_witness_structure_key_path() {
221 let witness = vec![vec![0x01; 64]];
223 assert!(validate_taproot_witness_structure(&witness, false).unwrap());
224
225 let invalid = vec![vec![0x01; 63]];
227 assert!(!validate_taproot_witness_structure(&invalid, false).unwrap());
228
229 let invalid2 = vec![vec![0x01; 64], vec![0x02; 32]];
231 assert!(!validate_taproot_witness_structure(&invalid2, false).unwrap());
232 }
233
234 #[test]
235 fn test_validate_taproot_witness_structure_script_path() {
236 let witness = vec![
238 vec![OP_1], vec![0u8; 33], ];
241 assert!(validate_taproot_witness_structure(&witness, true).unwrap());
242
243 let invalid = vec![vec![OP_1], vec![0u8; 32]];
245 assert!(!validate_taproot_witness_structure(&invalid, true).unwrap());
246
247 let invalid2 = vec![vec![OP_1]];
249 assert!(!validate_taproot_witness_structure(&invalid2, true).unwrap());
250 }
251
252 #[test]
253 fn test_calculate_transaction_weight_segwit() {
254 let base_size = 100;
255 let total_size = 150;
256 let weight = calculate_transaction_weight_segwit(base_size, total_size);
257 assert_eq!(weight, 3 * base_size + total_size); }
259
260 #[test]
261 fn test_weight_to_vsize() {
262 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); }
267
268 #[test]
269 fn test_extract_witness_version() {
270 let segwit_script = vec![OP_0, PUSH_20_BYTES, 0x01, 0x02, 0x03]; assert_eq!(
272 extract_witness_version(&segwit_script),
273 Some(WitnessVersion::SegWitV0)
274 );
275
276 let taproot_script = vec![OP_1, PUSH_32_BYTES]; assert_eq!(
278 extract_witness_version(&taproot_script),
279 Some(WitnessVersion::TaprootV1)
280 );
281
282 let non_witness_script = vec![OP_DUP, OP_HASH160]; assert_eq!(extract_witness_version(&non_witness_script), None);
284 }
285
286 #[test]
287 fn test_extract_witness_program() {
288 let segwit_script = vec![
293 OP_0,
294 PUSH_20_BYTES,
295 0x01,
296 0x02,
297 0x03,
298 0x04,
299 0x05,
300 0x06,
301 0x07,
302 0x08,
303 0x09,
304 0x0a,
305 0x0b,
306 0x0c,
307 0x0d,
308 0x0e,
309 0x0f,
310 0x10,
311 0x11,
312 0x12,
313 0x13,
314 PUSH_20_BYTES,
315 ];
316 let program = extract_witness_program(&segwit_script, WitnessVersion::SegWitV0);
317 assert_eq!(
319 program,
320 Some(vec![
321 0x01,
322 0x02,
323 0x03,
324 0x04,
325 0x05,
326 0x06,
327 0x07,
328 0x08,
329 0x09,
330 0x0a,
331 0x0b,
332 0x0c,
333 0x0d,
334 0x0e,
335 0x0f,
336 0x10,
337 0x11,
338 0x12,
339 0x13,
340 PUSH_20_BYTES
341 ])
342 );
343 }
344
345 #[test]
346 fn test_validate_witness_program_length() {
347 let p2wpkh = vec![0u8; 20]; assert!(validate_witness_program_length(
349 &p2wpkh,
350 WitnessVersion::SegWitV0
351 ));
352
353 let p2wsh = vec![0u8; 32]; assert!(validate_witness_program_length(
355 &p2wsh,
356 WitnessVersion::SegWitV0
357 ));
358
359 let p2tr = vec![0u8; 32]; assert!(validate_witness_program_length(
361 &p2tr,
362 WitnessVersion::TaprootV1
363 ));
364
365 let invalid = vec![0u8; 33];
366 assert!(!validate_witness_program_length(
367 &invalid,
368 WitnessVersion::SegWitV0
369 ));
370 assert!(!validate_witness_program_length(
371 &invalid,
372 WitnessVersion::TaprootV1
373 ));
374 }
375
376 #[test]
377 fn test_is_witness_empty() {
378 assert!(is_witness_empty(&vec![]));
379 assert!(is_witness_empty(&vec![vec![]]));
380 assert!(!is_witness_empty(&vec![vec![0x01]]));
381 }
382}