def _walk_ast(self, ast): for a in ast: total_stack_size = len(self._stack) + len(self._alt_stack) if total_stack_size > 1000: raise ScriptInterpreterError( "Too many items (%d) on the stack!" % total_stack_size) if self.stop: break opcode = None args = None if isinstance(a, list): opcode = a[0] args = a[1:] else: opcode = a if opcode in self.DISABLED_OPS + self.RESERVED_WORDS: self.stop = True break if opcode in Script.BTC_OPCODE_TABLE: op = Script.BTC_OPCODE_TABLE[opcode] else: op = None if opcode == "OP_0": self._op_0() elif isinstance(opcode, bytes): self._op_push(opcode) elif opcode in ['OP_PUSHDATA1', 'OP_PUSHDATA2', 'OP_PUSHDATA4']: pushlen = int(opcode[-1]) if pushlen == 1: datalen = args[0][0] elif pushlen == 2: datalen = struct.unpack("<H", args[0])[0] elif pushlen == 4: datalen = struct.unpack("<I", args[0])[0] data = args[1] if pushlen != (datalen.bit_length() + 7) // 8: raise ScriptInterpreterError( "datalen does not correspond with opcode") if len(data) != datalen: raise ScriptInterpreterError("len(data) != datalen in %s" % opcode) self._op_pushdata(datalen, data) elif op and op >= Script.BTC_OPCODE_TABLE['OP_1'] and \ op <= Script.BTC_OPCODE_TABLE['OP_16']: self._op_pushnum(opcode) elif opcode in ['OP_IF', 'OP_NOTIF']: self._op_if(opcode, args) elif hasattr(self, "_" + opcode.lower()): f = getattr(self, "_" + opcode.lower()) f()
def _op_endif(self): """ Ends an if/else block. All blocks must end, or the transaction is invalid. An OP_ENDIF without OP_IF earlier is also invalid. """ if len(self._if_else_stack) == 0: self.stop = True raise ScriptInterpreterError("OP_ENDIF without OP_IF/NOTIF") self._if_else_stack.pop()
def _op_push(self, data): """ Next opcode bytes are pushed onto stack Args: data (bytes): Array of bytes of at least opcode length. """ if len(data) < 0x01 or len(data) > 0x4b: raise ScriptInterpreterError( "data must only be between 1 and 75 bytes long") self._stack.append(data)
def _op_pick(self, roll=False): """ Copies the nth item in the stack to the top """ self._check_stack_len(2) n = self._get_int() if n <= 0 or n >= len(self._stack): self.stop = True raise ScriptInterpreterError("n (%d) is invalid." % n) x = self._stack[-n] if roll: del self._stack[-n] self._stack.append(x)
def _op_else(self, data): """ If the preceding OP_IF or OP_NOTIF or OP_ELSE was not executed then these statements are and if the preceding OP_IF or OP_NOTIF or OP_ELSE was executed then these statements are not. """ if len(self._if_else_stack) == 0: self.stop = True raise ScriptInterpreterError("In OP_ELSE without OP_IF/NOTIF") if not self._if_else_stack[-1]: self._walk_ast(data)
def _op_pushdata(self, datalen, data): """ Next byte(s) determines number of bytes that are pushed onto stack Args: datalen (int): Number of bytes to be pushed data (bytes): Array of bytes. """ if len(data) < datalen: raise ScriptInterpreterError( "data should have at least %d bytes but has only %d." % (datalen, len(data))) self._stack.append(data)
def _check_stack_len(self, min_len, alt=False): """ Checks that the stack has a minimum number of elements. Args: min_len (int): The minimum number of elements that should be on the stack. alt (bool): If True, checks the altstack. Raises: ValueError: If the number of stack elements is fewer than min_len. """ s = self._alt_stack if alt else self._stack if len(s) < min_len: raise ScriptInterpreterError("Stack has fewer than %d operands." % min_len)
def _check_txn(self): """ Checks that a transaction object has been initialized in self._data. Raises: ValueError: If there is no Transaction object in self._data """ from two1.lib.bitcoin.txn import Transaction if not isinstance(self._txn, Transaction): raise ScriptInterpreterError("No transaction found!") if self._input_index < 0 or self._input_index >= len(self._txn.inputs): raise ValueError("Invalid input index.") if self._sub_script is None: raise ValueError("sub_script must not be None.") if not isinstance(self._sub_script, Script): raise TypeError("sub_script must be a Script object.")
def _op_if(self, opcode, data): """ If the top stack value is not 0 (OP_IF) or 1 (OP_NOTIF), the statements are executed. The top stack value is removed. """ self._check_stack_len(1) do = self._get_bool() if opcode == 'OP_NOTIF': do = not do if do: self._if_else_stack.append(do) self._walk_ast(data[0]) elif len(data) == 3: self._if_else_stack.append(do) self._op_else(data[1]) if data[-1] == "OP_ENDIF": if self._if_else_stack: self._op_endif() else: raise ScriptInterpreterError("No matching OP_ENDIF!")
def _op_checkmultisig(self, partial=False): """ Compares the first signature against each public key until it finds an ECDSA match. Starting with the subsequent public key, it compares the second signature against each remaining public key until it finds an ECDSA match. The process is repeated until all signatures have been checked or not enough public keys remain to produce a successful result. All signatures need to match a public key. Because public keys are not checked again if they fail any signature comparison, signatures must be placed in the scriptSig using the same order as their corresponding public keys were placed in the scriptPubKey or redeemScript. If all signatures are valid, 1 is returned, 0 otherwise. Due to a bug, one extra unused value is removed from the stack. """ self._check_stack_len(1) self._check_txn() num_keys = self._stack.pop() self._check_stack_len(num_keys) keys_bytes = [] for i in range(num_keys): keys_bytes.insert(0, self._stack.pop()) public_keys = [PublicKey.from_bytes(p) for p in keys_bytes] min_num_sigs = self._stack.pop() # Although "m" is the *minimum* number of required signatures, bitcoin # core only consumes "m" signatures and then expects an OP_0. This # means that if m < min_num_sigs <= n, bitcoin core will return a # script failure. See: # https://github.com/bitcoin/bitcoin/blob/0.10/src/script/interpreter.cpp#L840 # We will do the same. hash_types = set() sigs = [] for i in range(min_num_sigs): s = self._stack.pop() try: sig = Signature.from_der(s[:-1]) hash_types.add(s[-1]) sigs.insert(0, sig) except ValueError: if partial: # Put it back on stack self._stack.append(s) else: # If not a partial evaluation there are not enough # sigs rv = False break if len(hash_types) != 1: raise ScriptInterpreterError( "Not all signatures have the same hash type!") hash_type = hash_types.pop() txn_copy = self._txn._copy_for_sig(input_index=self._input_index, hash_type=hash_type, sub_script=self._sub_script) msg = bytes(txn_copy) + utils.pack_u32(hash_type) txn_digest = hashlib.sha256(msg).digest() # Now we verify last_match = -1 rv = True match_count = 0 for sig in sigs: matched_any = False for i, pub_key in enumerate(public_keys[last_match + 1:]): if pub_key.verify(txn_digest, sig): last_match = i match_count += 1 matched_any = True break if not matched_any: # Bail early if the sig couldn't be verified # by any public key rv = False break rv &= match_count >= min_num_sigs # Now make sure the last thing on the stack is OP_0 if len(self._stack) == 1: rv &= self._stack.pop() == b'' rv &= len(self._stack) == 0 else: rv = False self._stack.append(rv) if partial: self.match_count = match_count