Skip to main content

hns_script/
interpreter.rs

1use blake2::Blake2bVar;
2use blake2::digest::{Update, VariableOutput};
3use hns_transaction::{Address, Coin, Transaction, Witness};
4use k256::ecdsa::signature::hazmat::PrehashVerifier;
5use k256::ecdsa::{Signature, VerifyingKey};
6use ripemd::Ripemd160;
7use sha1::Sha1;
8use sha2::{Digest, Sha256};
9use sha3::{Keccak256, Sha3_256};
10use thiserror::Error;
11
12use crate::{
13    is_valid_signature_hash_type, signature_hash, verify_locktime_predicate,
14    verify_sequence_predicate,
15};
16
17/// Maximum serialized script size accepted by HSD consensus.
18pub const MAX_SCRIPT_SIZE: usize = 10_000;
19/// Maximum size of an individual pushed stack item.
20pub const MAX_SCRIPT_PUSH: usize = 520;
21/// Maximum number of non-push operations in a script.
22pub const MAX_SCRIPT_OPS: usize = 201;
23/// Maximum combined main- and alternate-stack item count.
24pub const MAX_SCRIPT_STACK: usize = 1_000;
25/// Maximum number of public keys consumed by `OP_CHECKMULTISIG`.
26pub const MAX_MULTISIG_PUBKEYS: usize = 20;
27
28pub const OP_0: u8 = 0x00;
29pub const OP_PUSHDATA1: u8 = 0x4c;
30pub const OP_PUSHDATA2: u8 = 0x4d;
31pub const OP_PUSHDATA4: u8 = 0x4e;
32pub const OP_1NEGATE: u8 = 0x4f;
33pub const OP_RESERVED: u8 = 0x50;
34pub const OP_1: u8 = 0x51;
35pub const OP_2: u8 = 0x52;
36pub const OP_3: u8 = 0x53;
37pub const OP_4: u8 = 0x54;
38pub const OP_5: u8 = 0x55;
39pub const OP_6: u8 = 0x56;
40pub const OP_7: u8 = 0x57;
41pub const OP_8: u8 = 0x58;
42pub const OP_9: u8 = 0x59;
43pub const OP_10: u8 = 0x5a;
44pub const OP_11: u8 = 0x5b;
45pub const OP_12: u8 = 0x5c;
46pub const OP_13: u8 = 0x5d;
47pub const OP_14: u8 = 0x5e;
48pub const OP_15: u8 = 0x5f;
49pub const OP_16: u8 = 0x60;
50pub const OP_NOP: u8 = 0x61;
51pub const OP_VER: u8 = 0x62;
52pub const OP_IF: u8 = 0x63;
53pub const OP_NOTIF: u8 = 0x64;
54pub const OP_VERIF: u8 = 0x65;
55pub const OP_VERNOTIF: u8 = 0x66;
56pub const OP_ELSE: u8 = 0x67;
57pub const OP_ENDIF: u8 = 0x68;
58pub const OP_VERIFY: u8 = 0x69;
59pub const OP_RETURN: u8 = 0x6a;
60pub const OP_TOALTSTACK: u8 = 0x6b;
61pub const OP_FROMALTSTACK: u8 = 0x6c;
62pub const OP_2DROP: u8 = 0x6d;
63pub const OP_2DUP: u8 = 0x6e;
64pub const OP_3DUP: u8 = 0x6f;
65pub const OP_2OVER: u8 = 0x70;
66pub const OP_2ROT: u8 = 0x71;
67pub const OP_2SWAP: u8 = 0x72;
68pub const OP_IFDUP: u8 = 0x73;
69pub const OP_DEPTH: u8 = 0x74;
70pub const OP_DROP: u8 = 0x75;
71pub const OP_DUP: u8 = 0x76;
72pub const OP_NIP: u8 = 0x77;
73pub const OP_OVER: u8 = 0x78;
74pub const OP_PICK: u8 = 0x79;
75pub const OP_ROLL: u8 = 0x7a;
76pub const OP_ROT: u8 = 0x7b;
77pub const OP_SWAP: u8 = 0x7c;
78pub const OP_TUCK: u8 = 0x7d;
79pub const OP_CAT: u8 = 0x7e;
80pub const OP_SUBSTR: u8 = 0x7f;
81pub const OP_LEFT: u8 = 0x80;
82pub const OP_RIGHT: u8 = 0x81;
83pub const OP_SIZE: u8 = 0x82;
84pub const OP_INVERT: u8 = 0x83;
85pub const OP_AND: u8 = 0x84;
86pub const OP_OR: u8 = 0x85;
87pub const OP_XOR: u8 = 0x86;
88pub const OP_EQUAL: u8 = 0x87;
89pub const OP_EQUALVERIFY: u8 = 0x88;
90pub const OP_RESERVED1: u8 = 0x89;
91pub const OP_RESERVED2: u8 = 0x8a;
92pub const OP_1ADD: u8 = 0x8b;
93pub const OP_1SUB: u8 = 0x8c;
94pub const OP_2MUL: u8 = 0x8d;
95pub const OP_2DIV: u8 = 0x8e;
96pub const OP_NEGATE: u8 = 0x8f;
97pub const OP_ABS: u8 = 0x90;
98pub const OP_NOT: u8 = 0x91;
99pub const OP_0NOTEQUAL: u8 = 0x92;
100pub const OP_ADD: u8 = 0x93;
101pub const OP_SUB: u8 = 0x94;
102pub const OP_MUL: u8 = 0x95;
103pub const OP_DIV: u8 = 0x96;
104pub const OP_MOD: u8 = 0x97;
105pub const OP_LSHIFT: u8 = 0x98;
106pub const OP_RSHIFT: u8 = 0x99;
107pub const OP_BOOLAND: u8 = 0x9a;
108pub const OP_BOOLOR: u8 = 0x9b;
109pub const OP_NUMEQUAL: u8 = 0x9c;
110pub const OP_NUMEQUALVERIFY: u8 = 0x9d;
111pub const OP_NUMNOTEQUAL: u8 = 0x9e;
112pub const OP_LESSTHAN: u8 = 0x9f;
113pub const OP_GREATERTHAN: u8 = 0xa0;
114pub const OP_LESSTHANOREQUAL: u8 = 0xa1;
115pub const OP_GREATERTHANOREQUAL: u8 = 0xa2;
116pub const OP_MIN: u8 = 0xa3;
117pub const OP_MAX: u8 = 0xa4;
118pub const OP_WITHIN: u8 = 0xa5;
119pub const OP_RIPEMD160: u8 = 0xa6;
120pub const OP_SHA1: u8 = 0xa7;
121pub const OP_SHA256: u8 = 0xa8;
122pub const OP_HASH160: u8 = 0xa9;
123pub const OP_HASH256: u8 = 0xaa;
124pub const OP_CODESEPARATOR: u8 = 0xab;
125pub const OP_CHECKSIG: u8 = 0xac;
126pub const OP_CHECKSIGVERIFY: u8 = 0xad;
127pub const OP_CHECKMULTISIG: u8 = 0xae;
128pub const OP_CHECKMULTISIGVERIFY: u8 = 0xaf;
129pub const OP_NOP1: u8 = 0xb0;
130pub const OP_CHECKLOCKTIMEVERIFY: u8 = 0xb1;
131pub const OP_CHECKSEQUENCEVERIFY: u8 = 0xb2;
132pub const OP_NOP4: u8 = 0xb3;
133pub const OP_NOP5: u8 = 0xb4;
134pub const OP_NOP6: u8 = 0xb5;
135pub const OP_NOP7: u8 = 0xb6;
136pub const OP_NOP8: u8 = 0xb7;
137pub const OP_NOP9: u8 = 0xb8;
138pub const OP_NOP10: u8 = 0xb9;
139pub const OP_BLAKE160: u8 = 0xc0;
140pub const OP_BLAKE256: u8 = 0xc1;
141pub const OP_SHA3: u8 = 0xc2;
142pub const OP_KECCAK: u8 = 0xc3;
143pub const OP_TYPE: u8 = 0xd0;
144pub const OP_INVALIDOPCODE: u8 = 0xff;
145
146#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
147pub struct ScriptFlags(u32);
148
149impl ScriptFlags {
150    pub const NONE: Self = Self(0);
151    pub const VERIFY_MINIMAL_DATA: Self = Self(1 << 1);
152    pub const VERIFY_DISCOURAGE_UPGRADABLE_NOPS: Self = Self(1 << 2);
153    pub const VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM: Self = Self(1 << 3);
154    pub const VERIFY_MINIMAL_IF: Self = Self(1 << 4);
155    pub const VERIFY_NULLFAIL: Self = Self(1 << 5);
156    pub const MANDATORY: Self =
157        Self(Self::VERIFY_MINIMAL_DATA.0 | Self::VERIFY_MINIMAL_IF.0 | Self::VERIFY_NULLFAIL.0);
158    pub const STANDARD: Self = Self(
159        Self::MANDATORY.0
160            | Self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS.0
161            | Self::VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM.0,
162    );
163
164    pub const fn contains(self, other: Self) -> bool {
165        self.0 & other.0 == other.0
166    }
167
168    pub const fn from_bits(bits: u32) -> Self {
169        Self(bits)
170    }
171
172    pub const fn bits(self) -> u32 {
173        self.0
174    }
175
176    pub const fn union(self, other: Self) -> Self {
177        Self(self.0 | other.0)
178    }
179}
180
181/// One bounded, decoded script instruction.
182#[derive(Clone, Debug, Eq, PartialEq)]
183pub struct Instruction {
184    pub opcode: u8,
185    pub data: Option<Vec<u8>>,
186    pub start: usize,
187    pub end: usize,
188}
189
190/// Parse a serialized script using HSD's pushdata encoding and consensus size
191/// bound. Push-size policy is enforced by execution so callers can inspect a
192/// syntactically valid script containing an oversized push.
193pub fn parse_script(script: &[u8]) -> Result<Vec<Instruction>, ScriptError> {
194    if script.len() > MAX_SCRIPT_SIZE {
195        return Err(ScriptError::ScriptSize);
196    }
197
198    let mut instructions = Vec::new();
199    let mut offset = 0usize;
200    while offset < script.len() {
201        let start = offset;
202        let opcode = script[offset];
203        offset += 1;
204        let data_len = match opcode {
205            0x01..=0x4b => Some(usize::from(opcode)),
206            OP_PUSHDATA1 => Some(usize::from(read_u8(script, &mut offset)?)),
207            OP_PUSHDATA2 => Some(usize::from(read_u16(script, &mut offset)?)),
208            OP_PUSHDATA4 => Some(
209                usize::try_from(read_u32(script, &mut offset)?)
210                    .map_err(|_| ScriptError::BadOpcode(opcode))?,
211            ),
212            _ => None,
213        };
214        let data = if let Some(data_len) = data_len {
215            let end = offset
216                .checked_add(data_len)
217                .filter(|end| *end <= script.len())
218                .ok_or(ScriptError::BadOpcode(opcode))?;
219            let data = script[offset..end].to_vec();
220            offset = end;
221            Some(data)
222        } else {
223            None
224        };
225        instructions.push(Instruction {
226            opcode,
227            data,
228            start,
229            end: offset,
230        });
231    }
232    Ok(instructions)
233}
234
235/// Count HSD witness sigops without executing the script. A malformed trailing
236/// push terminates scanning after the valid prefix, matching HSD.
237pub fn count_script_sigops(script: &[u8]) -> u32 {
238    let mut total = 0u32;
239    let mut offset = 0usize;
240    let mut last_opcode = None;
241
242    while offset < script.len() {
243        let opcode = script[offset];
244        offset += 1;
245        let data_length = match opcode {
246            0x01..=0x4b => Some(usize::from(opcode)),
247            OP_PUSHDATA1 => {
248                let Some(length) = script.get(offset).copied() else {
249                    break;
250                };
251                offset += 1;
252                Some(usize::from(length))
253            }
254            OP_PUSHDATA2 => {
255                let Some(bytes) = script.get(offset..offset.saturating_add(2)) else {
256                    break;
257                };
258                let Ok(bytes) = <[u8; 2]>::try_from(bytes) else {
259                    break;
260                };
261                offset += 2;
262                Some(usize::from(u16::from_le_bytes(bytes)))
263            }
264            OP_PUSHDATA4 => {
265                let Some(bytes) = script.get(offset..offset.saturating_add(4)) else {
266                    break;
267                };
268                let Ok(bytes) = <[u8; 4]>::try_from(bytes) else {
269                    break;
270                };
271                offset += 4;
272                let Ok(length) = usize::try_from(u32::from_le_bytes(bytes)) else {
273                    break;
274                };
275                Some(length)
276            }
277            _ => None,
278        };
279        if let Some(data_length) = data_length {
280            let Some(end) = offset.checked_add(data_length) else {
281                break;
282            };
283            if end > script.len() {
284                break;
285            }
286            offset = end;
287        }
288
289        match opcode {
290            OP_CHECKSIG | OP_CHECKSIGVERIFY => total = total.saturating_add(1),
291            OP_CHECKMULTISIG | OP_CHECKMULTISIGVERIFY => {
292                let sigops = match last_opcode {
293                    Some(opcode @ OP_1..=OP_16) => u32::from(opcode - 0x50),
294                    _ => MAX_MULTISIG_PUBKEYS as u32,
295                };
296                total = total.saturating_add(sigops);
297            }
298            _ => {}
299        }
300        last_opcode = Some(opcode);
301    }
302    total
303}
304
305pub fn witness_program_sigops(address: &Address, witness: &Witness) -> u32 {
306    if address.version != 0 {
307        return 0;
308    }
309    match address.hash.len() {
310        20 => 1,
311        32 => witness
312            .items
313            .last()
314            .map_or(0, |script| count_script_sigops(script)),
315        _ => 0,
316    }
317}
318
319pub fn transaction_sigops(
320    transaction: &Transaction,
321    input_coins: &[Coin],
322) -> Result<u32, ScriptError> {
323    if transaction.is_coinbase() {
324        return Ok(0);
325    }
326    if transaction.inputs.len() != input_coins.len() {
327        return Err(ScriptError::InputCoinCount {
328            inputs: transaction.inputs.len(),
329            coins: input_coins.len(),
330        });
331    }
332
333    transaction
334        .inputs
335        .iter()
336        .zip(input_coins)
337        .try_fold(0u32, |total, (input, coin)| {
338            if input.previous_output != coin.outpoint {
339                return Err(ScriptError::InputCoinMismatch);
340            }
341            Ok(total.saturating_add(witness_program_sigops(&coin.address, &input.witness)))
342        })
343}
344
345/// Pluggable compact-secp256k1 verifier used by the runtime-independent
346/// interpreter. Implementations must enforce HSD's low-S encoding rule.
347pub trait SignatureVerifier: Send + Sync {
348    fn validate_compact_signature(&self, signature: &[u8; 64]) -> Result<(), ScriptError>;
349
350    fn verify(
351        &self,
352        message: &[u8; 32],
353        signature: &[u8; 64],
354        public_key: &[u8; 33],
355    ) -> Result<bool, ScriptError>;
356
357    fn is_consensus_complete(&self) -> bool {
358        false
359    }
360}
361
362/// Fail-closed verifier for builds which intentionally supply no secp256k1
363/// backend.
364#[derive(Clone, Copy, Debug, Default)]
365pub struct UnavailableSignatureVerifier;
366
367impl SignatureVerifier for UnavailableSignatureVerifier {
368    fn validate_compact_signature(&self, _signature: &[u8; 64]) -> Result<(), ScriptError> {
369        Err(ScriptError::SignatureBackendUnavailable)
370    }
371
372    fn verify(
373        &self,
374        _message: &[u8; 32],
375        _signature: &[u8; 64],
376        _public_key: &[u8; 33],
377    ) -> Result<bool, ScriptError> {
378        Err(ScriptError::SignatureBackendUnavailable)
379    }
380}
381
382/// Pure Rust secp256k1 verifier backed by `k256`.
383#[derive(Clone, Copy, Debug, Default)]
384pub struct K256SignatureVerifier;
385
386impl SignatureVerifier for K256SignatureVerifier {
387    fn validate_compact_signature(&self, signature: &[u8; 64]) -> Result<(), ScriptError> {
388        let signature =
389            Signature::from_slice(signature).map_err(|_| ScriptError::SignatureEncoding)?;
390        if signature.normalize_s().is_some() {
391            return Err(ScriptError::SignatureEncoding);
392        }
393        Ok(())
394    }
395
396    fn verify(
397        &self,
398        message: &[u8; 32],
399        signature: &[u8; 64],
400        public_key: &[u8; 33],
401    ) -> Result<bool, ScriptError> {
402        let signature =
403            Signature::from_slice(signature).map_err(|_| ScriptError::SignatureEncoding)?;
404        let public_key = VerifyingKey::from_sec1_bytes(public_key)
405            .map_err(|_| ScriptError::PublicKeyEncoding)?;
406        Ok(public_key.verify_prehash(message, &signature).is_ok())
407    }
408
409    fn is_consensus_complete(&self) -> bool {
410        true
411    }
412}
413
414pub fn verify_witness_program(
415    transaction: &Transaction,
416    input_index: usize,
417    coin: &Coin,
418    flags: ScriptFlags,
419    signatures: &dyn SignatureVerifier,
420) -> Result<(), ScriptError> {
421    let input = transaction
422        .inputs
423        .get(input_index)
424        .ok_or(ScriptError::InputIndex {
425            requested: input_index,
426            inputs: transaction.inputs.len(),
427        })?;
428    if input.previous_output != coin.outpoint {
429        return Err(ScriptError::InputCoinMismatch);
430    }
431    let address = &coin.address;
432
433    if address.version == 31 {
434        return Err(ScriptError::OpReturn);
435    }
436    if input.witness.items.len() > MAX_SCRIPT_STACK {
437        return Err(ScriptError::StackSize);
438    }
439
440    let mut stack = input.witness.items.clone();
441    let redeem = if address.version == 0 {
442        match address.hash.len() {
443            32 => {
444                let witness_script = stack.pop().ok_or(ScriptError::WitnessProgramWitnessEmpty)?;
445                if witness_script.len() > MAX_SCRIPT_SIZE {
446                    return Err(ScriptError::ScriptSize);
447                }
448                if sha3_256(&witness_script).as_slice() != address.hash.as_slice() {
449                    return Err(ScriptError::WitnessProgramMismatch);
450                }
451                witness_script
452            }
453            20 => {
454                if stack.len() != 2 {
455                    return Err(ScriptError::WitnessProgramMismatch);
456                }
457                pubkey_hash_script(&address.hash)
458            }
459            _ => return Err(ScriptError::WitnessProgramWrongLength),
460        }
461    } else {
462        if flags.contains(ScriptFlags::VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) {
463            return Err(ScriptError::DiscourageUpgradableWitnessProgram);
464        }
465        return Ok(());
466    };
467
468    execute_script(
469        &redeem,
470        &mut stack,
471        transaction,
472        input_index,
473        coin.value.get(),
474        flags,
475        signatures,
476    )?;
477
478    if stack.len() != 1 || !cast_to_bool(&stack[0]) {
479        return Err(ScriptError::EvalFalse);
480    }
481    Ok(())
482}
483
484fn pubkey_hash_script(hash: &[u8]) -> Vec<u8> {
485    let mut script = Vec::with_capacity(25);
486    script.extend_from_slice(&[OP_DUP, OP_BLAKE160, 20]);
487    script.extend_from_slice(hash);
488    script.extend_from_slice(&[OP_EQUALVERIFY, OP_CHECKSIG]);
489    script
490}
491
492/// Execute one script against an existing stack. The caller retains the final
493/// stack, making this useful below witness-program policy while remaining
494/// bounded and independent of any async runtime or storage.
495#[allow(clippy::too_many_lines)]
496pub fn execute_script(
497    script: &[u8],
498    stack: &mut Vec<Vec<u8>>,
499    transaction: &Transaction,
500    input_index: usize,
501    previous_value: u64,
502    flags: ScriptFlags,
503    signatures: &dyn SignatureVerifier,
504) -> Result<(), ScriptError> {
505    if transaction.inputs.get(input_index).is_none() {
506        return Err(ScriptError::InputIndex {
507            requested: input_index,
508            inputs: transaction.inputs.len(),
509        });
510    }
511    if stack.len() > MAX_SCRIPT_STACK {
512        return Err(ScriptError::StackSize);
513    }
514    if stack.iter().any(|item| item.len() > MAX_SCRIPT_PUSH) {
515        return Err(ScriptError::PushSize);
516    }
517
518    let instructions = parse_script(script)?;
519    let mut alt_stack = Vec::<Vec<u8>>::new();
520    let mut conditions = Vec::<bool>::new();
521    let mut operation_count = 0usize;
522    let mut last_separator = 0usize;
523
524    for instruction in instructions {
525        if instruction
526            .data
527            .as_ref()
528            .is_some_and(|data| data.len() > MAX_SCRIPT_PUSH)
529        {
530            return Err(ScriptError::PushSize);
531        }
532        if instruction.opcode > OP_16 {
533            operation_count = operation_count
534                .checked_add(1)
535                .ok_or(ScriptError::OperationCount)?;
536            if operation_count > MAX_SCRIPT_OPS {
537                return Err(ScriptError::OperationCount);
538            }
539        }
540        if is_disabled_opcode(instruction.opcode) {
541            return Err(ScriptError::DisabledOpcode(instruction.opcode));
542        }
543
544        let executing = conditions.iter().all(|condition| *condition);
545        let is_branch = (OP_IF..=OP_ENDIF).contains(&instruction.opcode);
546        if !executing && !is_branch {
547            enforce_stack_limit(stack, &alt_stack)?;
548            continue;
549        }
550
551        if let Some(data) = instruction.data {
552            if flags.contains(ScriptFlags::VERIFY_MINIMAL_DATA)
553                && !is_minimal_push(instruction.opcode, &data)
554            {
555                return Err(ScriptError::MinimalData);
556            }
557            stack.push(data);
558            enforce_stack_limit(stack, &alt_stack)?;
559            continue;
560        }
561
562        match instruction.opcode {
563            OP_0 => stack.push(Vec::new()),
564            OP_1NEGATE => stack.push(encode_script_number(-1)),
565            OP_1..=OP_16 => {
566                stack.push(encode_script_number(i64::from(
567                    instruction.opcode - OP_1 + 1,
568                )));
569            }
570            OP_NOP => {}
571            OP_TYPE => {
572                let covenant_type = transaction
573                    .outputs
574                    .get(input_index)
575                    .map(|output| i64::from(output.covenant.kind.as_u8()))
576                    .unwrap_or(0);
577                stack.push(encode_script_number(covenant_type));
578            }
579            OP_CHECKLOCKTIMEVERIFY => {
580                let value = decode_top_number(stack, flags, 5)?;
581                if value < 0 {
582                    return Err(ScriptError::NegativeLocktime);
583                }
584                let predicate =
585                    u32::try_from(value).map_err(|_| ScriptError::UnsatisfiedLocktime)?;
586                if !verify_locktime_predicate(transaction, input_index, predicate) {
587                    return Err(ScriptError::UnsatisfiedLocktime);
588                }
589            }
590            OP_CHECKSEQUENCEVERIFY => {
591                let value = decode_top_number(stack, flags, 5)?;
592                if value < 0 {
593                    return Err(ScriptError::NegativeLocktime);
594                }
595                let predicate =
596                    u32::try_from(value).map_err(|_| ScriptError::UnsatisfiedLocktime)?;
597                if !verify_sequence_predicate(transaction, input_index, predicate) {
598                    return Err(ScriptError::UnsatisfiedLocktime);
599                }
600            }
601            opcode if opcode == OP_NOP1 || (OP_NOP4..=OP_NOP10).contains(&opcode) => {
602                if flags.contains(ScriptFlags::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
603                    return Err(ScriptError::DiscourageUpgradableNops);
604                }
605            }
606            OP_IF | OP_NOTIF => {
607                let parent_executing = conditions.iter().all(|condition| *condition);
608                let mut value = false;
609                if parent_executing {
610                    let item = stack.pop().ok_or(ScriptError::UnbalancedConditional)?;
611                    if flags.contains(ScriptFlags::VERIFY_MINIMAL_IF)
612                        && !(item.is_empty() || item.as_slice() == [1u8])
613                    {
614                        return Err(ScriptError::MinimalIf);
615                    }
616                    value = cast_to_bool(&item);
617                    if instruction.opcode == OP_NOTIF {
618                        value = !value;
619                    }
620                }
621                conditions.push(value);
622            }
623            OP_ELSE => {
624                let Some(condition) = conditions.last_mut() else {
625                    return Err(ScriptError::UnbalancedConditional);
626                };
627                *condition = !*condition;
628            }
629            OP_ENDIF => {
630                conditions.pop().ok_or(ScriptError::UnbalancedConditional)?;
631            }
632            OP_VERIFY => {
633                if !cast_to_bool(&pop(stack)?) {
634                    return Err(ScriptError::Verify);
635                }
636            }
637            OP_RETURN => return Err(ScriptError::OpReturn),
638            OP_TOALTSTACK => alt_stack.push(pop(stack)?),
639            OP_FROMALTSTACK => {
640                let item = alt_stack
641                    .pop()
642                    .ok_or(ScriptError::InvalidAltStackOperation)?;
643                stack.push(item);
644            }
645            OP_2DROP => {
646                require_stack(stack, 2)?;
647                stack.truncate(stack.len() - 2);
648            }
649            OP_2DUP => duplicate_tail(stack, 2)?,
650            OP_3DUP => duplicate_tail(stack, 3)?,
651            OP_2OVER => {
652                require_stack(stack, 4)?;
653                let len = stack.len();
654                let values = stack[len - 4..len - 2].to_vec();
655                stack.extend(values);
656            }
657            OP_2ROT => {
658                require_stack(stack, 6)?;
659                let len = stack.len();
660                let first = stack.remove(len - 6);
661                let second = stack.remove(len - 6);
662                stack.push(first);
663                stack.push(second);
664            }
665            OP_2SWAP => {
666                require_stack(stack, 4)?;
667                let len = stack.len();
668                stack.swap(len - 4, len - 2);
669                stack.swap(len - 3, len - 1);
670            }
671            OP_IFDUP => {
672                let value = stack.last().ok_or(ScriptError::InvalidStackOperation)?;
673                if cast_to_bool(value) {
674                    stack.push(value.clone());
675                }
676            }
677            OP_DEPTH => stack.push(encode_script_number(stack.len() as i64)),
678            OP_DROP => {
679                pop(stack)?;
680            }
681            OP_DUP => duplicate_tail(stack, 1)?,
682            OP_NIP => {
683                require_stack(stack, 2)?;
684                let len = stack.len();
685                stack.remove(len - 2);
686            }
687            OP_OVER => {
688                require_stack(stack, 2)?;
689                let len = stack.len();
690                stack.push(stack[len - 2].clone());
691            }
692            OP_PICK | OP_ROLL => {
693                let depth = decode_script_number(&pop(stack)?, minimal_numbers(flags), 4)?;
694                let depth =
695                    usize::try_from(depth).map_err(|_| ScriptError::InvalidStackOperation)?;
696                if depth >= stack.len() {
697                    return Err(ScriptError::InvalidStackOperation);
698                }
699                let index = stack.len() - 1 - depth;
700                let item = if instruction.opcode == OP_ROLL {
701                    stack.remove(index)
702                } else {
703                    stack[index].clone()
704                };
705                stack.push(item);
706            }
707            OP_ROT => {
708                require_stack(stack, 3)?;
709                let len = stack.len();
710                let item = stack.remove(len - 3);
711                stack.push(item);
712            }
713            OP_SWAP => {
714                require_stack(stack, 2)?;
715                let len = stack.len();
716                stack.swap(len - 2, len - 1);
717            }
718            OP_TUCK => {
719                require_stack(stack, 2)?;
720                let len = stack.len();
721                let item = stack[len - 1].clone();
722                stack.insert(len - 2, item);
723            }
724            OP_SIZE => {
725                let size = stack
726                    .last()
727                    .ok_or(ScriptError::InvalidStackOperation)?
728                    .len();
729                stack.push(encode_script_number(size as i64));
730            }
731            OP_EQUAL | OP_EQUALVERIFY => {
732                require_stack(stack, 2)?;
733                let right = pop(stack)?;
734                let left = pop(stack)?;
735                let equal = left == right;
736                if instruction.opcode == OP_EQUALVERIFY {
737                    if !equal {
738                        return Err(ScriptError::EqualVerify);
739                    }
740                } else {
741                    push_bool(stack, equal);
742                }
743            }
744            OP_1ADD | OP_1SUB | OP_NEGATE | OP_ABS | OP_NOT | OP_0NOTEQUAL => {
745                let value = decode_script_number(&pop(stack)?, minimal_numbers(flags), 4)?;
746                let result = match instruction.opcode {
747                    OP_1ADD => value.checked_add(1),
748                    OP_1SUB => value.checked_sub(1),
749                    OP_NEGATE => value.checked_neg(),
750                    OP_ABS => value.checked_abs(),
751                    OP_NOT => Some(i64::from(value == 0)),
752                    OP_0NOTEQUAL => Some(i64::from(value != 0)),
753                    _ => None,
754                }
755                .ok_or(ScriptError::NumericOverflow)?;
756                stack.push(encode_script_number(result));
757            }
758            OP_ADD
759            | OP_SUB
760            | OP_BOOLAND
761            | OP_BOOLOR
762            | OP_NUMEQUAL
763            | OP_NUMEQUALVERIFY
764            | OP_NUMNOTEQUAL
765            | OP_LESSTHAN
766            | OP_GREATERTHAN
767            | OP_LESSTHANOREQUAL
768            | OP_GREATERTHANOREQUAL
769            | OP_MIN
770            | OP_MAX => {
771                require_stack(stack, 2)?;
772                let right = decode_script_number(&pop(stack)?, minimal_numbers(flags), 4)?;
773                let left = decode_script_number(&pop(stack)?, minimal_numbers(flags), 4)?;
774                let result = match instruction.opcode {
775                    OP_ADD => left
776                        .checked_add(right)
777                        .ok_or(ScriptError::NumericOverflow)?,
778                    OP_SUB => left
779                        .checked_sub(right)
780                        .ok_or(ScriptError::NumericOverflow)?,
781                    OP_BOOLAND => i64::from(left != 0 && right != 0),
782                    OP_BOOLOR => i64::from(left != 0 || right != 0),
783                    OP_NUMEQUAL | OP_NUMEQUALVERIFY => i64::from(left == right),
784                    OP_NUMNOTEQUAL => i64::from(left != right),
785                    OP_LESSTHAN => i64::from(left < right),
786                    OP_GREATERTHAN => i64::from(left > right),
787                    OP_LESSTHANOREQUAL => i64::from(left <= right),
788                    OP_GREATERTHANOREQUAL => i64::from(left >= right),
789                    OP_MIN => left.min(right),
790                    OP_MAX => left.max(right),
791                    _ => unreachable!(),
792                };
793                if instruction.opcode == OP_NUMEQUALVERIFY {
794                    if result == 0 {
795                        return Err(ScriptError::NumEqualVerify);
796                    }
797                } else {
798                    stack.push(encode_script_number(result));
799                }
800            }
801            OP_WITHIN => {
802                require_stack(stack, 3)?;
803                let maximum = decode_script_number(&pop(stack)?, minimal_numbers(flags), 4)?;
804                let minimum = decode_script_number(&pop(stack)?, minimal_numbers(flags), 4)?;
805                let value = decode_script_number(&pop(stack)?, minimal_numbers(flags), 4)?;
806                push_bool(stack, minimum <= value && value < maximum);
807            }
808            OP_BLAKE160 | OP_BLAKE256 | OP_SHA3 | OP_KECCAK => {
809                let item = pop(stack)?;
810                let digest = match instruction.opcode {
811                    OP_BLAKE160 => blake2b_160(&item).to_vec(),
812                    OP_BLAKE256 => blake2b_256(&item).to_vec(),
813                    OP_SHA3 => sha3_256(&item).to_vec(),
814                    OP_KECCAK => Keccak256::digest(&item).to_vec(),
815                    _ => unreachable!(),
816                };
817                stack.push(digest);
818            }
819            OP_RIPEMD160 | OP_SHA1 | OP_SHA256 | OP_HASH160 | OP_HASH256 => {
820                let item = pop(stack)?;
821                let digest = match instruction.opcode {
822                    OP_RIPEMD160 => Ripemd160::digest(&item).to_vec(),
823                    OP_SHA1 => Sha1::digest(&item).to_vec(),
824                    OP_SHA256 => Sha256::digest(&item).to_vec(),
825                    OP_HASH160 => Ripemd160::digest(Sha256::digest(&item)).to_vec(),
826                    OP_HASH256 => Sha256::digest(Sha256::digest(&item)).to_vec(),
827                    _ => unreachable!(),
828                };
829                stack.push(digest);
830            }
831            OP_CODESEPARATOR => last_separator = instruction.end,
832            OP_CHECKSIG | OP_CHECKSIGVERIFY => {
833                require_stack(stack, 2)?;
834                let public_key = pop(stack)?;
835                let signature = pop(stack)?;
836                let valid = check_signature(
837                    transaction,
838                    input_index,
839                    previous_value,
840                    &script[last_separator..],
841                    &signature,
842                    &public_key,
843                    signatures,
844                )?;
845                if !valid && flags.contains(ScriptFlags::VERIFY_NULLFAIL) && !signature.is_empty() {
846                    return Err(ScriptError::NullFail);
847                }
848                if instruction.opcode == OP_CHECKSIGVERIFY {
849                    if !valid {
850                        return Err(ScriptError::CheckSigVerify);
851                    }
852                } else {
853                    push_bool(stack, valid);
854                }
855            }
856            OP_CHECKMULTISIG | OP_CHECKMULTISIGVERIFY => {
857                let valid = check_multisig(
858                    stack,
859                    transaction,
860                    input_index,
861                    previous_value,
862                    &script[last_separator..],
863                    flags,
864                    signatures,
865                    &mut operation_count,
866                )?;
867                if instruction.opcode == OP_CHECKMULTISIGVERIFY {
868                    if !valid {
869                        return Err(ScriptError::CheckMultiSigVerify);
870                    }
871                } else {
872                    push_bool(stack, valid);
873                }
874            }
875            OP_RESERVED | OP_VER | OP_VERIF | OP_VERNOTIF | OP_RESERVED1 | OP_RESERVED2
876            | OP_INVALIDOPCODE => return Err(ScriptError::BadOpcode(instruction.opcode)),
877            opcode => return Err(ScriptError::BadOpcode(opcode)),
878        }
879
880        enforce_stack_limit(stack, &alt_stack)?;
881    }
882
883    if !conditions.is_empty() {
884        return Err(ScriptError::UnbalancedConditional);
885    }
886    Ok(())
887}
888
889fn check_signature(
890    transaction: &Transaction,
891    input_index: usize,
892    previous_value: u64,
893    subscript: &[u8],
894    signature: &[u8],
895    public_key: &[u8],
896    signatures: &dyn SignatureVerifier,
897) -> Result<bool, ScriptError> {
898    let compact = if signature.is_empty() {
899        None
900    } else {
901        if signature.len() != 65 || !is_valid_signature_hash_type(signature[64]) {
902            return Err(ScriptError::SignatureEncoding);
903        }
904        let compact: &[u8; 64] = signature[..64]
905            .try_into()
906            .map_err(|_| ScriptError::SignatureEncoding)?;
907        signatures.validate_compact_signature(compact)?;
908        Some(compact)
909    };
910    let public_key: &[u8; 33] = public_key
911        .try_into()
912        .map_err(|_| ScriptError::PublicKeyEncoding)?;
913    if !matches!(public_key[0], 0x02 | 0x03) {
914        return Err(ScriptError::PublicKeyEncoding);
915    }
916    let Some(compact) = compact else {
917        return Ok(false);
918    };
919    let message = signature_hash(
920        transaction,
921        input_index,
922        subscript,
923        previous_value,
924        u32::from(signature[64]),
925    )?;
926    signatures.verify(&message, compact, public_key)
927}
928
929#[allow(clippy::too_many_arguments)]
930fn check_multisig(
931    stack: &mut Vec<Vec<u8>>,
932    transaction: &Transaction,
933    input_index: usize,
934    previous_value: u64,
935    subscript: &[u8],
936    flags: ScriptFlags,
937    signatures: &dyn SignatureVerifier,
938    operation_count: &mut usize,
939) -> Result<bool, ScriptError> {
940    let key_count = decode_script_number(&pop(stack)?, minimal_numbers(flags), 4)?;
941    let key_count = usize::try_from(key_count).map_err(|_| ScriptError::PublicKeyCount)?;
942    if key_count > MAX_MULTISIG_PUBKEYS {
943        return Err(ScriptError::PublicKeyCount);
944    }
945    *operation_count = (*operation_count)
946        .checked_add(key_count)
947        .ok_or(ScriptError::OperationCount)?;
948    if *operation_count > MAX_SCRIPT_OPS {
949        return Err(ScriptError::OperationCount);
950    }
951    require_stack(stack, key_count.saturating_add(1))?;
952    let keys_start = stack.len() - key_count;
953    let keys = stack.split_off(keys_start);
954    let signature_count = decode_script_number(&pop(stack)?, minimal_numbers(flags), 4)?;
955    let signature_count =
956        usize::try_from(signature_count).map_err(|_| ScriptError::SignatureCount)?;
957    if signature_count > key_count {
958        return Err(ScriptError::SignatureCount);
959    }
960    require_stack(stack, signature_count.saturating_add(1))?;
961    let signatures_start = stack.len() - signature_count;
962    let candidate_signatures = stack.split_off(signatures_start);
963    let dummy = pop(stack)?;
964
965    let mut remaining_signatures = candidate_signatures.len();
966    let mut remaining_keys = keys.len();
967    let mut valid = true;
968    while remaining_signatures > 0 {
969        if remaining_signatures > remaining_keys {
970            valid = false;
971            break;
972        }
973        let signature = &candidate_signatures[remaining_signatures - 1];
974        let key = &keys[remaining_keys - 1];
975        if check_signature(
976            transaction,
977            input_index,
978            previous_value,
979            subscript,
980            signature,
981            key,
982            signatures,
983        )? {
984            remaining_signatures -= 1;
985        }
986        remaining_keys -= 1;
987    }
988    valid &= remaining_signatures == 0;
989
990    if !valid
991        && flags.contains(ScriptFlags::VERIFY_NULLFAIL)
992        && candidate_signatures
993            .iter()
994            .any(|signature| !signature.is_empty())
995    {
996        return Err(ScriptError::NullFail);
997    }
998    if !dummy.is_empty() {
999        return Err(ScriptError::SignatureNullDummy);
1000    }
1001    Ok(valid)
1002}
1003
1004fn read_u8(script: &[u8], offset: &mut usize) -> Result<u8, ScriptError> {
1005    let value = *script
1006        .get(*offset)
1007        .ok_or(ScriptError::BadOpcode(OP_PUSHDATA1))?;
1008    *offset += 1;
1009    Ok(value)
1010}
1011
1012fn read_u16(script: &[u8], offset: &mut usize) -> Result<u16, ScriptError> {
1013    let end = offset
1014        .checked_add(2)
1015        .filter(|end| *end <= script.len())
1016        .ok_or(ScriptError::BadOpcode(OP_PUSHDATA2))?;
1017    let bytes: [u8; 2] = script[*offset..end]
1018        .try_into()
1019        .map_err(|_| ScriptError::BadOpcode(OP_PUSHDATA2))?;
1020    *offset = end;
1021    Ok(u16::from_le_bytes(bytes))
1022}
1023
1024fn read_u32(script: &[u8], offset: &mut usize) -> Result<u32, ScriptError> {
1025    let end = offset
1026        .checked_add(4)
1027        .filter(|end| *end <= script.len())
1028        .ok_or(ScriptError::BadOpcode(OP_PUSHDATA4))?;
1029    let bytes: [u8; 4] = script[*offset..end]
1030        .try_into()
1031        .map_err(|_| ScriptError::BadOpcode(OP_PUSHDATA4))?;
1032    *offset = end;
1033    Ok(u32::from_le_bytes(bytes))
1034}
1035
1036fn is_minimal_push(opcode: u8, data: &[u8]) -> bool {
1037    if data.is_empty() {
1038        return opcode == OP_0;
1039    }
1040    if data.len() == 1 && (1..=16).contains(&data[0]) {
1041        return opcode == OP_1 + data[0] - 1;
1042    }
1043    if data == [0x81] {
1044        return opcode == OP_1NEGATE;
1045    }
1046    match data.len() {
1047        1..=75 => opcode == data.len() as u8,
1048        76..=255 => opcode == OP_PUSHDATA1,
1049        256..=65_535 => opcode == OP_PUSHDATA2,
1050        _ => opcode == OP_PUSHDATA4,
1051    }
1052}
1053
1054fn is_disabled_opcode(opcode: u8) -> bool {
1055    matches!(
1056        opcode,
1057        OP_CAT
1058            | OP_SUBSTR
1059            | OP_LEFT
1060            | OP_RIGHT
1061            | OP_INVERT
1062            | OP_AND
1063            | OP_OR
1064            | OP_XOR
1065            | OP_2MUL
1066            | OP_2DIV
1067            | OP_MUL
1068            | OP_DIV
1069            | OP_MOD
1070            | OP_LSHIFT
1071            | OP_RSHIFT
1072    )
1073}
1074
1075fn minimal_numbers(flags: ScriptFlags) -> bool {
1076    flags.contains(ScriptFlags::VERIFY_MINIMAL_DATA)
1077}
1078
1079fn decode_top_number(
1080    stack: &[Vec<u8>],
1081    flags: ScriptFlags,
1082    maximum_size: usize,
1083) -> Result<i64, ScriptError> {
1084    let item = stack.last().ok_or(ScriptError::InvalidStackOperation)?;
1085    decode_script_number(item, minimal_numbers(flags), maximum_size)
1086}
1087
1088fn decode_script_number(
1089    bytes: &[u8],
1090    require_minimal: bool,
1091    maximum_size: usize,
1092) -> Result<i64, ScriptError> {
1093    if bytes.len() > maximum_size {
1094        return Err(ScriptError::NumericOverflow);
1095    }
1096    if require_minimal && !is_minimal_script_number(bytes) {
1097        return Err(ScriptError::NonMinimalNumber);
1098    }
1099    if bytes.is_empty() {
1100        return Ok(0);
1101    }
1102
1103    let mut magnitude = 0u64;
1104    for (index, byte) in bytes.iter().copied().enumerate() {
1105        magnitude |= u64::from(byte) << (8 * index);
1106    }
1107    let sign_bit = 1u64 << (bytes.len() * 8 - 1);
1108    let negative = magnitude & sign_bit != 0;
1109    magnitude &= !sign_bit;
1110    let magnitude = i64::try_from(magnitude).map_err(|_| ScriptError::NumericOverflow)?;
1111    Ok(if negative { -magnitude } else { magnitude })
1112}
1113
1114fn encode_script_number(value: i64) -> Vec<u8> {
1115    if value == 0 {
1116        return Vec::new();
1117    }
1118
1119    let negative = value < 0;
1120    let mut magnitude = value.unsigned_abs();
1121    let mut bytes = Vec::new();
1122    while magnitude != 0 {
1123        bytes.push(magnitude as u8);
1124        magnitude >>= 8;
1125    }
1126    if bytes.last().is_some_and(|byte| byte & 0x80 != 0) {
1127        bytes.push(if negative { 0x80 } else { 0x00 });
1128    } else if negative && let Some(last) = bytes.last_mut() {
1129        *last |= 0x80;
1130    }
1131    bytes
1132}
1133
1134fn is_minimal_script_number(bytes: &[u8]) -> bool {
1135    if bytes.is_empty() {
1136        return true;
1137    }
1138    if bytes.last().is_some_and(|byte| byte & 0x7f == 0) {
1139        if bytes.len() == 1 {
1140            return false;
1141        }
1142        if bytes[bytes.len() - 2] & 0x80 == 0 {
1143            return false;
1144        }
1145    }
1146    true
1147}
1148
1149fn cast_to_bool(bytes: &[u8]) -> bool {
1150    for (index, byte) in bytes.iter().copied().enumerate() {
1151        if byte == 0 {
1152            continue;
1153        }
1154        if index == bytes.len() - 1 && byte == 0x80 {
1155            return false;
1156        }
1157        return true;
1158    }
1159    false
1160}
1161
1162fn push_bool(stack: &mut Vec<Vec<u8>>, value: bool) {
1163    stack.push(if value { vec![1] } else { Vec::new() });
1164}
1165
1166fn pop(stack: &mut Vec<Vec<u8>>) -> Result<Vec<u8>, ScriptError> {
1167    stack.pop().ok_or(ScriptError::InvalidStackOperation)
1168}
1169
1170fn require_stack(stack: &[Vec<u8>], count: usize) -> Result<(), ScriptError> {
1171    if stack.len() < count {
1172        Err(ScriptError::InvalidStackOperation)
1173    } else {
1174        Ok(())
1175    }
1176}
1177
1178fn duplicate_tail(stack: &mut Vec<Vec<u8>>, count: usize) -> Result<(), ScriptError> {
1179    require_stack(stack, count)?;
1180    let start = stack.len() - count;
1181    let values = stack[start..].to_vec();
1182    stack.extend(values);
1183    Ok(())
1184}
1185
1186fn enforce_stack_limit(stack: &[Vec<u8>], alt_stack: &[Vec<u8>]) -> Result<(), ScriptError> {
1187    if stack.len().saturating_add(alt_stack.len()) > MAX_SCRIPT_STACK {
1188        Err(ScriptError::StackSize)
1189    } else {
1190        Ok(())
1191    }
1192}
1193
1194fn blake2b_160(input: &[u8]) -> [u8; 20] {
1195    let mut hasher = Blake2bVar::new(20).expect("valid BLAKE2b output length");
1196    hasher.update(input);
1197    let mut output = [0u8; 20];
1198    hasher
1199        .finalize_variable(&mut output)
1200        .expect("valid BLAKE2b output buffer");
1201    output
1202}
1203
1204fn blake2b_256(input: &[u8]) -> [u8; 32] {
1205    let mut hasher = Blake2bVar::new(32).expect("valid BLAKE2b output length");
1206    hasher.update(input);
1207    let mut output = [0u8; 32];
1208    hasher
1209        .finalize_variable(&mut output)
1210        .expect("valid BLAKE2b output buffer");
1211    output
1212}
1213
1214fn sha3_256(input: &[u8]) -> [u8; 32] {
1215    Sha3_256::digest(input).into()
1216}
1217
1218#[derive(Debug, Error)]
1219pub enum ScriptError {
1220    #[error("signature input {requested} is outside {inputs} inputs")]
1221    InputIndex { requested: usize, inputs: usize },
1222    #[error("invalid signature hash type 0x{0:08x}")]
1223    InvalidSignatureHashType(u32),
1224    #[error(transparent)]
1225    Transaction(#[from] hns_transaction::TransactionError),
1226    #[error("resolved input count {coins} does not match transaction input count {inputs}")]
1227    InputCoinCount { inputs: usize, coins: usize },
1228    #[error("resolved input coin does not match the transaction outpoint")]
1229    InputCoinMismatch,
1230    #[error("OP_RETURN")]
1231    OpReturn,
1232    #[error("script exceeds the consensus size limit")]
1233    ScriptSize,
1234    #[error("script stack exceeds the consensus item limit")]
1235    StackSize,
1236    #[error("script push exceeds the consensus item-size limit")]
1237    PushSize,
1238    #[error("witness program has an empty witness")]
1239    WitnessProgramWitnessEmpty,
1240    #[error("witness program does not match the committed hash or shape")]
1241    WitnessProgramMismatch,
1242    #[error("version-zero witness program has the wrong length")]
1243    WitnessProgramWrongLength,
1244    #[error("upgradable witness program is discouraged by policy")]
1245    DiscourageUpgradableWitnessProgram,
1246    #[error("upgradable NOP is discouraged by policy")]
1247    DiscourageUpgradableNops,
1248    #[error("script evaluated to false")]
1249    EvalFalse,
1250    #[error("script contains malformed or invalid opcode 0x{0:02x}")]
1251    BadOpcode(u8),
1252    #[error("script contains disabled opcode 0x{0:02x}")]
1253    DisabledOpcode(u8),
1254    #[error("script operation count exceeds the consensus limit")]
1255    OperationCount,
1256    #[error("script push is not minimally encoded")]
1257    MinimalData,
1258    #[error("script number is not minimally encoded")]
1259    NonMinimalNumber,
1260    #[error("conditional argument is not minimally encoded")]
1261    MinimalIf,
1262    #[error("script conditional is unbalanced")]
1263    UnbalancedConditional,
1264    #[error("invalid main-stack operation")]
1265    InvalidStackOperation,
1266    #[error("invalid alt-stack operation")]
1267    InvalidAltStackOperation,
1268    #[error("VERIFY failed")]
1269    Verify,
1270    #[error("EQUALVERIFY failed")]
1271    EqualVerify,
1272    #[error("NUMEQUALVERIFY failed")]
1273    NumEqualVerify,
1274    #[error("negative locktime")]
1275    NegativeLocktime,
1276    #[error("locktime predicate is not satisfied")]
1277    UnsatisfiedLocktime,
1278    #[error("script number overflow")]
1279    NumericOverflow,
1280    #[error("public key count is invalid")]
1281    PublicKeyCount,
1282    #[error("signature count is invalid")]
1283    SignatureCount,
1284    #[error("public key encoding is invalid")]
1285    PublicKeyEncoding,
1286    #[error("signature encoding is invalid")]
1287    SignatureEncoding,
1288    #[error("multisig dummy argument is not empty")]
1289    SignatureNullDummy,
1290    #[error("NULLFAIL")]
1291    NullFail,
1292    #[error("CHECKSIGVERIFY failed")]
1293    CheckSigVerify,
1294    #[error("CHECKMULTISIGVERIFY failed")]
1295    CheckMultiSigVerify,
1296    #[error("secp256k1 signature backend is unavailable")]
1297    SignatureBackendUnavailable,
1298}
1299
1300impl ScriptError {
1301    /// Return HSD's rejection code for the same failure class.
1302    pub const fn hsd_code(&self) -> &'static str {
1303        match self {
1304            Self::InputIndex { .. }
1305            | Self::InvalidSignatureHashType(_)
1306            | Self::Transaction(_)
1307            | Self::InputCoinCount { .. }
1308            | Self::InputCoinMismatch
1309            | Self::NumericOverflow
1310            | Self::NonMinimalNumber
1311            | Self::SignatureBackendUnavailable => "UNKNOWN_ERROR",
1312            Self::OpReturn => "OP_RETURN",
1313            Self::ScriptSize => "SCRIPT_SIZE",
1314            Self::StackSize => "STACK_SIZE",
1315            Self::PushSize => "PUSH_SIZE",
1316            Self::WitnessProgramWitnessEmpty => "WITNESS_PROGRAM_WITNESS_EMPTY",
1317            Self::WitnessProgramMismatch => "WITNESS_PROGRAM_MISMATCH",
1318            Self::WitnessProgramWrongLength => "WITNESS_PROGRAM_WRONG_LENGTH",
1319            Self::DiscourageUpgradableWitnessProgram => "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM",
1320            Self::DiscourageUpgradableNops => "DISCOURAGE_UPGRADABLE_NOPS",
1321            Self::EvalFalse => "EVAL_FALSE",
1322            Self::BadOpcode(_) => "BAD_OPCODE",
1323            Self::DisabledOpcode(_) => "DISABLED_OPCODE",
1324            Self::OperationCount => "OP_COUNT",
1325            Self::MinimalData => "MINIMALDATA",
1326            Self::MinimalIf => "MINIMALIF",
1327            Self::UnbalancedConditional => "UNBALANCED_CONDITIONAL",
1328            Self::InvalidStackOperation => "INVALID_STACK_OPERATION",
1329            Self::InvalidAltStackOperation => "INVALID_ALTSTACK_OPERATION",
1330            Self::Verify => "VERIFY",
1331            Self::EqualVerify => "EQUALVERIFY",
1332            Self::NumEqualVerify => "NUMEQUALVERIFY",
1333            Self::NegativeLocktime => "NEGATIVE_LOCKTIME",
1334            Self::UnsatisfiedLocktime => "UNSATISFIED_LOCKTIME",
1335            Self::PublicKeyCount => "PUBKEY_COUNT",
1336            Self::SignatureCount => "SIG_COUNT",
1337            Self::PublicKeyEncoding => "PUBKEY_ENCODING",
1338            Self::SignatureEncoding => "SIG_ENCODING",
1339            Self::SignatureNullDummy => "SIG_NULLDUMMY",
1340            Self::NullFail => "NULLFAIL",
1341            Self::CheckSigVerify => "CHECKSIGVERIFY",
1342            Self::CheckMultiSigVerify => "CHECKMULTISIGVERIFY",
1343        }
1344    }
1345}
1346
1347#[cfg(test)]
1348mod tests {
1349    use hns_covenants::CovenantKind;
1350    use hns_transaction::{Input, Outpoint, Output};
1351
1352    use super::*;
1353
1354    const HSD_SCRIPT_VECTORS: &str = include_str!("../fixtures/hsd/script-tests-v1.txt");
1355
1356    #[derive(Debug)]
1357    struct HsdScriptVector {
1358        index: usize,
1359        expected: String,
1360        flags: ScriptFlags,
1361        value: u64,
1362        locktime: u32,
1363        sequence: u32,
1364        script: Vec<u8>,
1365        witness: Vec<Vec<u8>>,
1366    }
1367
1368    fn parse_vector(line: &str) -> HsdScriptVector {
1369        let mut fields = line.split('|');
1370        let index = fields
1371            .next()
1372            .expect("index")
1373            .parse()
1374            .expect("numeric index");
1375        let expected = fields.next().expect("result").to_owned();
1376        let flags = ScriptFlags::from_bits(
1377            fields
1378                .next()
1379                .expect("flags")
1380                .parse()
1381                .expect("numeric flags"),
1382        );
1383        let value = fields
1384            .next()
1385            .expect("value")
1386            .parse()
1387            .expect("numeric value");
1388        let locktime = fields
1389            .next()
1390            .expect("locktime")
1391            .parse()
1392            .expect("numeric locktime");
1393        let sequence = fields
1394            .next()
1395            .expect("sequence")
1396            .parse()
1397            .expect("numeric sequence");
1398        let script = hex::decode(fields.next().expect("script")).expect("script hex");
1399        let witness_count = fields
1400            .next()
1401            .expect("witness count")
1402            .parse::<usize>()
1403            .expect("numeric witness count");
1404        let witness_field = fields.next().expect("witness");
1405        assert!(fields.next().is_none(), "unexpected vector field");
1406        let witness = if witness_count == 0 {
1407            assert!(witness_field.is_empty());
1408            Vec::new()
1409        } else {
1410            let items = witness_field
1411                .split(',')
1412                .map(|item| hex::decode(item).expect("witness hex"))
1413                .collect::<Vec<_>>();
1414            assert_eq!(items.len(), witness_count);
1415            items
1416        };
1417        HsdScriptVector {
1418            index,
1419            expected,
1420            flags,
1421            value,
1422            locktime,
1423            sequence,
1424            script,
1425            witness,
1426        }
1427    }
1428
1429    fn spending_fixture(vector: &HsdScriptVector) -> (Transaction, Coin) {
1430        let script_address =
1431            Address::new(0, sha3_256(&vector.script).to_vec()).expect("script address");
1432        let funding = Transaction {
1433            version: 1,
1434            inputs: vec![Input {
1435                previous_output: Outpoint::NULL,
1436                sequence: u32::MAX,
1437                witness: Witness {
1438                    items: vec![vec![0], vec![0]],
1439                },
1440            }],
1441            outputs: vec![Output {
1442                value: vector.value.into(),
1443                address: script_address.clone(),
1444                covenant: Default::default(),
1445            }],
1446            locktime: 0,
1447        };
1448        let outpoint = Outpoint {
1449            transaction_hash: funding
1450                .transaction_hash()
1451                .expect("funding transaction hash"),
1452            index: 0,
1453        };
1454        let mut witness = vector.witness.clone();
1455        witness.push(vector.script.clone());
1456        let spending = Transaction {
1457            version: 1,
1458            inputs: vec![Input {
1459                previous_output: outpoint,
1460                sequence: vector.sequence,
1461                witness: Witness { items: witness },
1462            }],
1463            outputs: vec![Output {
1464                value: vector.value.into(),
1465                address: Address::new(0, vec![0; 20]).expect("null public-key hash"),
1466                covenant: Default::default(),
1467            }],
1468            locktime: vector.locktime,
1469        };
1470        let coin = Coin {
1471            outpoint,
1472            value: vector.value.into(),
1473            height: 1_u32.into(),
1474            coinbase: false,
1475            address: script_address,
1476            covenant: Default::default(),
1477        };
1478        (spending, coin)
1479    }
1480
1481    #[test]
1482    fn all_pinned_hsd_script_vectors_match_exact_results() {
1483        assert!(
1484            HSD_SCRIPT_VECTORS.contains("# hsd_revision=698e252ebc7b5c1dd0a9587e342fdd153d020ae4")
1485        );
1486        assert!(HSD_SCRIPT_VECTORS.contains(
1487            "# source_sha256=71548a587d1c7921cb899de192f59ed1833c85a6cd62d9dac8cd5b86b1225c86"
1488        ));
1489        let vectors = HSD_SCRIPT_VECTORS
1490            .lines()
1491            .filter(|line| !line.starts_with('#') && !line.is_empty())
1492            .map(parse_vector)
1493            .collect::<Vec<_>>();
1494        assert_eq!(vectors.len(), 876);
1495
1496        for vector in vectors {
1497            let (transaction, coin) = spending_fixture(&vector);
1498            let result = verify_witness_program(
1499                &transaction,
1500                0,
1501                &coin,
1502                vector.flags,
1503                &K256SignatureVerifier,
1504            );
1505            match (vector.expected.as_str(), result) {
1506                ("OK", Ok(())) => {}
1507                ("OK", Err(error)) => {
1508                    panic!(
1509                        "HSD script vector {} expected OK but returned {} ({})",
1510                        vector.index,
1511                        error.hsd_code(),
1512                        error
1513                    );
1514                }
1515                (expected, Err(error)) if error.hsd_code() == expected => {}
1516                (expected, Err(error)) => {
1517                    panic!(
1518                        "HSD script vector {} expected {} but returned {} ({})",
1519                        vector.index,
1520                        expected,
1521                        error.hsd_code(),
1522                        error
1523                    );
1524                }
1525                (expected, Ok(())) => {
1526                    panic!(
1527                        "HSD script vector {} expected {} but succeeded",
1528                        vector.index, expected
1529                    );
1530                }
1531            }
1532        }
1533    }
1534
1535    #[test]
1536    fn op_type_and_coin_binding_are_consensus_visible() {
1537        let vector = HsdScriptVector {
1538            index: 0,
1539            expected: "OK".to_owned(),
1540            flags: ScriptFlags::MANDATORY,
1541            value: 1_000,
1542            locktime: 0,
1543            sequence: u32::MAX,
1544            script: vec![OP_TYPE, OP_7, OP_EQUAL],
1545            witness: Vec::new(),
1546        };
1547        let (mut transaction, coin) = spending_fixture(&vector);
1548        transaction
1549            .outputs
1550            .first_mut()
1551            .expect("fixture output")
1552            .covenant
1553            .kind = CovenantKind::Update;
1554        verify_witness_program(
1555            &transaction,
1556            0,
1557            &coin,
1558            ScriptFlags::MANDATORY,
1559            &K256SignatureVerifier,
1560        )
1561        .expect("OP_TYPE matches output covenant");
1562
1563        let mut wrong_coin = coin.clone();
1564        wrong_coin.outpoint.index = 1;
1565        assert!(matches!(
1566            verify_witness_program(
1567                &transaction,
1568                0,
1569                &wrong_coin,
1570                ScriptFlags::MANDATORY,
1571                &K256SignatureVerifier
1572            ),
1573            Err(ScriptError::InputCoinMismatch)
1574        ));
1575    }
1576}