def _EvalScript(stack: List[bytes], scriptIn: CScript, txTo: 'bitcointx.core.CTransaction', inIdx: int, flags: Set[ScriptVerifyFlag_Type] = set(), amount: int = 0, sigversion: SIGVERSION_Type = SIGVERSION_BASE ) -> None: """Evaluate a script """ if len(scriptIn) > MAX_SCRIPT_SIZE: raise EvalScriptError((f'script too large; got {len(scriptIn)} bytes; ' f'maximum {MAX_SCRIPT_SIZE} bytes'), ScriptEvalState(stack=stack, scriptIn=scriptIn, txTo=txTo, inIdx=inIdx, flags=flags)) altstack: List[bytes] = [] vfExec: List[bool] = [] pbegincodehash = 0 nOpCount = [0] v_bytes: bytes v_int: int v_bool: bool for (sop, sop_data, sop_pc) in scriptIn.raw_iter(): fExec = _CheckExec(vfExec) def get_eval_state() -> ScriptEvalState: return ScriptEvalState( sop=sop, sop_data=sop_data, sop_pc=sop_pc, stack=stack, scriptIn=scriptIn, txTo=txTo, inIdx=inIdx, flags=flags, altstack=altstack, vfExec=vfExec, pbegincodehash=pbegincodehash, nOpCount=nOpCount[0]) if sop in DISABLED_OPCODES: raise EvalScriptError(f'opcode {_opcode_name(sop)} is disabled', get_eval_state()) if sop > OP_16: nOpCount[0] += 1 if nOpCount[0] > MAX_SCRIPT_OPCODES: raise MaxOpCountError(get_eval_state()) def check_args(n: int) -> None: if len(stack) < n: raise MissingOpArgumentsError(get_eval_state(), expected_stack_depth=n) if sop <= OP_PUSHDATA4: assert sop_data is not None if len(sop_data) > MAX_SCRIPT_ELEMENT_SIZE: raise EvalScriptError( (f'PUSHDATA of length {len(sop_data)}; ' f'maximum allowed is {MAX_SCRIPT_ELEMENT_SIZE}'), get_eval_state()) elif fExec: stack.append(sop_data) continue elif fExec or (OP_IF <= sop <= OP_ENDIF): if sop == OP_1NEGATE or ((sop >= OP_1) and (sop <= OP_16)): v_int = sop - (OP_1 - 1) stack.append(bitcointx.core._bignum.bn2vch(v_int)) elif sop in _ISA_BINOP: _BinOp(sop, stack, get_eval_state) elif sop in _ISA_UNOP: _UnaryOp(sop, stack, get_eval_state) elif sop == OP_2DROP: check_args(2) stack.pop() stack.pop() elif sop == OP_2DUP: check_args(2) v1 = stack[-2] v2 = stack[-1] stack.append(v1) stack.append(v2) elif sop == OP_2OVER: check_args(4) v1 = stack[-4] v2 = stack[-3] stack.append(v1) stack.append(v2) elif sop == OP_2ROT: check_args(6) v1 = stack[-6] v2 = stack[-5] del stack[-6] del stack[-5] stack.append(v1) stack.append(v2) elif sop == OP_2SWAP: check_args(4) tmp = stack[-4] stack[-4] = stack[-2] stack[-2] = tmp tmp = stack[-3] stack[-3] = stack[-1] stack[-1] = tmp elif sop == OP_3DUP: check_args(3) v1 = stack[-3] v2 = stack[-2] v3 = stack[-1] stack.append(v1) stack.append(v2) stack.append(v3) elif sop == OP_CHECKMULTISIG or sop == OP_CHECKMULTISIGVERIFY: tmpScript = scriptIn.__class__(scriptIn[pbegincodehash:]) _CheckMultiSig(sop, tmpScript, stack, txTo, inIdx, flags, get_eval_state, nOpCount, amount=amount, sigversion=sigversion) elif sop == OP_CHECKSIG or sop == OP_CHECKSIGVERIFY: check_args(2) vchPubKey = stack[-1] vchSig = stack[-2] # Subset of script starting at the most recent codeseparator tmpScript = scriptIn.__class__(scriptIn[pbegincodehash:]) if sigversion == SIGVERSION_BASE: # Drop the signature in pre-segwit scripts but not segwit scripts tmpScript = FindAndDelete(tmpScript, scriptIn.__class__([vchSig])) ok = _CheckSig(vchSig, vchPubKey, tmpScript, txTo, inIdx, flags, amount=amount, sigversion=sigversion) if not ok and SCRIPT_VERIFY_NULLFAIL in flags and len(vchSig): raise VerifyScriptError("signature check failed, and signature is not empty") if not ok and sop == OP_CHECKSIGVERIFY: raise VerifyOpFailedError(get_eval_state()) else: stack.pop() stack.pop() if ok: if sop != OP_CHECKSIGVERIFY: stack.append(b"\x01") else: # FIXME: this is incorrect, but not caught by existing # test cases stack.append(b"\x00") elif sop == OP_CODESEPARATOR: pbegincodehash = sop_pc elif sop == OP_DEPTH: bn = len(stack) stack.append(bitcointx.core._bignum.bn2vch(bn)) elif sop == OP_DROP: check_args(1) stack.pop() elif sop == OP_DUP: check_args(1) v_bytes = stack[-1] stack.append(v_bytes) elif sop == OP_ELSE: if len(vfExec) == 0: raise EvalScriptError('ELSE found without prior IF', get_eval_state()) vfExec[-1] = not vfExec[-1] elif sop == OP_ENDIF: if len(vfExec) == 0: raise EvalScriptError('ENDIF found without prior IF', get_eval_state()) vfExec.pop() elif sop == OP_EQUAL: check_args(2) v1 = stack.pop() v2 = stack.pop() if v1 == v2: stack.append(b"\x01") else: stack.append(b"") elif sop == OP_EQUALVERIFY: check_args(2) v1 = stack[-1] v2 = stack[-2] if v1 == v2: stack.pop() stack.pop() else: raise VerifyOpFailedError(get_eval_state()) elif sop == OP_FROMALTSTACK: if len(altstack) < 1: raise MissingOpArgumentsError(get_eval_state(), expected_stack_depth=1) v_bytes = altstack.pop() stack.append(v_bytes) elif sop == OP_HASH160: check_args(1) stack.append(bitcointx.core.serialize.Hash160(stack.pop())) elif sop == OP_HASH256: check_args(1) stack.append(bitcointx.core.serialize.Hash(stack.pop())) elif sop == OP_IF or sop == OP_NOTIF: val = False if fExec: check_args(1) vch = stack.pop() if sigversion == SIGVERSION_WITNESS_V0 and SCRIPT_VERIFY_MINIMALIF in flags: if len(vch) > 1: raise VerifyScriptError("SCRIPT_VERIFY_MINIMALIF check failed") if len(vch) == 1 and vch[0] != 1: raise VerifyScriptError("SCRIPT_VERIFY_MINIMALIF check failed") val = _CastToBool(vch) if sop == OP_NOTIF: val = not val vfExec.append(val) elif sop == OP_IFDUP: check_args(1) vch = stack[-1] if _CastToBool(vch): stack.append(vch) elif sop == OP_NIP: check_args(2) del stack[-2] elif sop == OP_NOP: pass elif sop >= OP_NOP1 and sop <= OP_NOP10: if SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS in flags: raise EvalScriptError((f"{_opcode_name(sop)} reserved " f"for soft-fork upgrades"), get_eval_state()) else: pass elif sop == OP_OVER: check_args(2) vch = stack[-2] stack.append(vch) elif sop == OP_PICK or sop == OP_ROLL: check_args(2) n = _CastToBigNum(stack.pop(), get_eval_state) if n < 0 or n >= len(stack): raise EvalScriptError( f"Argument for {_opcode_name(sop)} out of bounds", get_eval_state()) vch = stack[-n-1] if sop == OP_ROLL: del stack[-n-1] stack.append(vch) elif sop == OP_RETURN: raise EvalScriptError("OP_RETURN called", get_eval_state()) elif sop == OP_RIPEMD160: check_args(1) h = hashlib.new('ripemd160') h.update(stack.pop()) stack.append(h.digest()) elif sop == OP_ROT: check_args(3) tmp = stack[-3] stack[-3] = stack[-2] stack[-2] = tmp tmp = stack[-2] stack[-2] = stack[-1] stack[-1] = tmp elif sop == OP_SIZE: check_args(1) bn = len(stack[-1]) stack.append(bitcointx.core._bignum.bn2vch(bn)) elif sop == OP_SHA1: check_args(1) stack.append(hashlib.sha1(stack.pop()).digest()) elif sop == OP_SHA256: check_args(1) stack.append(hashlib.sha256(stack.pop()).digest()) elif sop == OP_SWAP: check_args(2) tmp = stack[-2] stack[-2] = stack[-1] stack[-1] = tmp elif sop == OP_TOALTSTACK: check_args(1) v_bytes = stack.pop() altstack.append(v_bytes) elif sop == OP_TUCK: check_args(2) vch = stack[-1] stack.insert(len(stack) - 2, vch) elif sop == OP_VERIFY: check_args(1) v_bool = _CastToBool(stack[-1]) if v_bool: stack.pop() else: raise VerifyOpFailedError(get_eval_state()) elif sop == OP_WITHIN: check_args(3) bn3 = _CastToBigNum(stack[-1], get_eval_state) bn2 = _CastToBigNum(stack[-2], get_eval_state) bn1 = _CastToBigNum(stack[-3], get_eval_state) stack.pop() stack.pop() stack.pop() v_bool = (bn2 <= bn1) and (bn1 < bn3) if v_bool: stack.append(b"\x01") else: # FIXME: this is incorrect, but not caught by existing # test cases stack.append(b"\x00") else: raise EvalScriptError('unsupported opcode 0x%x' % sop, get_eval_state()) # size limits if len(stack) + len(altstack) > MAX_STACK_ITEMS: raise EvalScriptError('max stack items limit reached', get_eval_state()) # Unterminated IF/NOTIF/ELSE block if len(vfExec): raise EvalScriptError( 'Unterminated IF/ELSE block', ScriptEvalState(stack=stack, scriptIn=scriptIn, txTo=txTo, inIdx=inIdx, flags=flags))
def _CheckMultiSig(opcode: CScriptOp, script: CScript, stack: List[bytes], txTo: 'bitcointx.core.CTransaction', inIdx: int, flags: Set[ScriptVerifyFlag_Type], get_eval_state: Callable[[], ScriptEvalState], nOpCount: List[int], amount: int = 0, sigversion: SIGVERSION_Type = SIGVERSION_BASE) -> None: i = 1 if len(stack) < i: raise MissingOpArgumentsError(get_eval_state(), expected_stack_depth=i) keys_count = _CastToBigNum(stack[-i], get_eval_state) if keys_count < 0 or keys_count > 20: raise ArgumentsInvalidError("keys count invalid", get_eval_state()) i += 1 ikey = i # ikey2 is the position of last non-signature item in the stack. Top stack item = 1. # With SCRIPT_VERIFY_NULLFAIL, this is used for cleanup if operation fails. ikey2 = keys_count + 2 i += keys_count nOpCount[0] += keys_count if nOpCount[0] > MAX_SCRIPT_OPCODES: raise MaxOpCountError(get_eval_state()) if len(stack) < i: raise ArgumentsInvalidError("not enough keys on stack", get_eval_state()) sigs_count = _CastToBigNum(stack[-i], get_eval_state) if sigs_count < 0 or sigs_count > keys_count: raise ArgumentsInvalidError("sigs count invalid", get_eval_state()) i += 1 isig = i i += sigs_count if len(stack) < i-1: raise ArgumentsInvalidError("not enough sigs on stack", get_eval_state()) elif len(stack) < i: raise ArgumentsInvalidError("missing dummy value", get_eval_state()) if sigversion == SIGVERSION_BASE: # Drop the signature in pre-segwit scripts but not segwit scripts for k in range(sigs_count): sig = stack[-isig - k] script = FindAndDelete(script, script.__class__([sig])) success = True empty_sig_count = 0 while success and sigs_count > 0: sig = stack[-isig] empty_sig_count += int(len(sig) == 0) pubkey = stack[-ikey] if _CheckSig(sig, pubkey, script, txTo, inIdx, flags, amount=amount, sigversion=sigversion): isig += 1 sigs_count -= 1 ikey += 1 keys_count -= 1 if sigs_count > keys_count: success = False # with VERIFY bail now before we modify the stack if opcode == OP_CHECKMULTISIGVERIFY: raise VerifyOpFailedError(get_eval_state()) while i > 1: if not success and SCRIPT_VERIFY_NULLFAIL in flags and ikey2 == 0 and len(stack[-1]): raise VerifyScriptError("signature check failed, and some of the signatures are not empty") if ikey2 > 0: ikey2 -= 1 stack.pop() i -= 1 # Note how Bitcoin Core duplicates the len(stack) check, rather than # letting pop() handle it; maybe that's wrong? if len(stack) and SCRIPT_VERIFY_NULLDUMMY in flags: if stack[-1] != b'': raise ArgumentsInvalidError("dummy value not OP_0", get_eval_state()) stack.pop() if opcode == OP_CHECKMULTISIG: if success: stack.append(b"\x01") else: # FIXME: this is incorrect, but not caught by existing # test cases stack.append(b"\x00")