def get_human(self): """Get the script as a human-readable string.""" s = [] iterator = self.raw_iter() while 1: try: opcode, data, byte_index = next(iterator) op_name = OPCODE_NAMES.get(opcode) if op_name: s.append(op_name) elif opcode < OPCODES_BY_NAME["OP_PUSHDATA1"]: s.append("".join(["0x", data.encode("hex")])) except StopIteration: break except Exception: s.append("(CANNOT_PARSE)") return " ".join(s)
def get_human(self): """Get the script as a human-readable string.""" s = [] iterator = self.raw_iter() while 1: try: opcode, data, byte_index = next(iterator) op_name = OPCODE_NAMES.get(opcode) if op_name: s.append(op_name) elif opcode < OPCODES_BY_NAME['OP_PUSHDATA1']: s.append(''.join(['0x', data.encode('hex')])) except StopIteration: break except Exception: s.append('(CANNOT_PARSE)') return ' '.join(s)
def do_step(self): """Returns whether another step can be done.""" if self.step_counter == -1: txt = str(self.tx_script.toPlainText()) scr = CScript(txt.decode('hex')) # So we can show the opcode in the stack log self.script_ops = [i for i in scr.raw_iter()] self.stack.set_script(scr, self.tx, self.inIdx) self.stack_iterator = self.stack.step() self.stack_log.clear() self.step_counter += 1 step_again = False try: self.step_counter += 1 stack_state, action = self.stack_iterator.next() new_stack = [i.encode('hex') for i in reversed(stack_state)] self.stack_view.clear() self.stack_view.addItems(new_stack) op_name = OPCODE_NAMES.get( self.script_ops[self.step_counter - 1][0], 'PUSHDATA') self.highlight_step(self.script_ops[self.step_counter - 1]) item = QTreeWidgetItem( map(lambda i: str(i), [self.step_counter, op_name, action])) item.setTextAlignment(0, Qt.AlignLeft) item.setToolTip(1, 'Step {} operation'.format(self.step_counter)) item.setToolTip(2, 'Result of step {}'.format(self.step_counter)) self.stack_log.insertTopLevelItem(0, item) self.stack_result.setText(action) self.stack_result.setProperty('hasError', False) step_again = True except StopIteration: self.stack_result.setText('End of script.') except Exception as e: self.stack_result.setText(str(e)) self.stack_result.setProperty('hasError', True) finally: self.style().polish(self.stack_result) return step_again
def transform_human(text, variables=None): """Transform user input with given context. Args: text (str): User input. variables (dict): Variables for purposes of substitution. Returns: A 2-tuple of: (A human-readable script that Script can parse, A list of contextual information for tooltips, etc.) """ if variables is None: variables = {} # No mutable default value. # these are parseActions for pyparsing. def str_literal_to_hex(s, loc, toks): for i, t in enumerate(toks): toks[i] = ''.join(['0x', t.encode('hex')]) return toks def var_name_to_value(s, loc, toks): for i, t in enumerate(toks): val = variables.get(t[1:]) if val: toks[i] = val return toks def implicit_opcode_to_explicit(s, loc, toks): """Add "OP_" prefix to an opcode.""" for i, t in enumerate(toks): toks[i] = '_'.join(['OP', t]) return toks def hex_to_formatted_hex(s, loc, toks): """Add "0x" prefix and ensure even length.""" for i, t in enumerate(toks): new_tok = t # Add '0x' prefix if not t.startswith('0x'): if t.startswith('x'): new_tok = ''.join(['0', t]) else: new_tok = ''.join(['0x', t]) # Even-length string if len(new_tok) % 2 != 0: new_tok = ''.join([new_tok[0:2], '0', new_tok[2:]]) toks[i] = new_tok return toks # ^ parseActions for pyparsing end here. str_literal = QuotedString('"') str_literal.setParseAction(str_literal_to_hex) var_name = Combine(Word('$') + Word(pyparsing.alphas)) var_name.setParseAction(var_name_to_value) # Here we populate the list of contextual tips. # Explicit opcode names op_names = [str(i) for i in OPCODE_NAMES.keys()] op_names_explicit = ' '.join(op_names) def is_small_int(op): """True if op is one of OP_1, OP_2, ...OP_16""" try: i = int(op[3:]) return True except ValueError: return False op_names_implicit = ' '.join( [i[3:] for i in op_names if not is_small_int(i)]) # Hex, implicit (e.g. 'a') and explicit (e.g. '0x0a') explicit_hex = Combine( Word('0x') + Word(pyparsing.hexnums) + pyparsing.WordEnd()) implicit_hex = Combine(pyparsing.WordStart() + OneOrMore(Word(pyparsing.hexnums)) + pyparsing.WordEnd()) explicit_hex.setParseAction(hex_to_formatted_hex) implicit_hex.setParseAction(hex_to_formatted_hex) # Opcodes, implicit (e.g. 'ADD') and explicit (e.g. 'OP_ADD') explicit_op = pyparsing.oneOf(op_names_explicit) implicit_op = Combine(pyparsing.WordStart() + pyparsing.oneOf(op_names_implicit)) implicit_op.setParseAction(implicit_opcode_to_explicit) contexts = pyparsing.Optional( var_name('Variable') | str_literal('String literal') | explicit_op('Opcode') | implicit_op('Opcode') | explicit_hex('Hex') | implicit_hex('Hex')) matches = [(i[0].asDict(), i[1], i[2]) for i in contexts.scanString(text)] context_tips = [] for i in matches: d = i[0] if len(d.items()) == 0: continue match_type, value = d.items()[0] start = i[1] end = i[2] context_tips.append((start, end, value, match_type)) # Now we do the actual transformation. s = text s = var_name.transformString(s) s = str_literal.transformString(s) s = implicit_op.transformString(s) s = implicit_hex.transformString(s) s = explicit_hex.transformString(s) return s, context_tips
def transform_human(text, variables=None): """Transform user input with given context. Args: text (str): User input. variables (dict): Variables for purposes of substitution. Returns: A 2-tuple of: (A human-readable script that Script can parse, A list of contextual information for tooltips, etc.) """ if variables is None: variables = {} # No mutable default value. # these are parseActions for pyparsing. def str_literal_to_hex(s, loc, toks): for i, t in enumerate(toks): toks[i] = ''.join(['0x', t.encode('hex')]) return toks def var_name_to_value(s, loc, toks): for i, t in enumerate(toks): val = variables.get(t[1:]) if val: toks[i] = val return toks def implicit_opcode_to_explicit(s, loc, toks): """Add "OP_" prefix to an opcode.""" for i, t in enumerate(toks): toks[i] = '_'.join(['OP', t]) return toks def hex_to_formatted_hex(s, loc, toks): """Add "0x" prefix and ensure even length.""" for i, t in enumerate(toks): new_tok = t # Add '0x' prefix if not t.startswith('0x'): if t.startswith('x'): new_tok = ''.join(['0', t]) else: new_tok = ''.join(['0x', t]) # Even-length string if len(new_tok) % 2 != 0: new_tok = ''.join([new_tok[0:2], '0', new_tok[2:]]) toks[i] = new_tok return toks # ^ parseActions for pyparsing end here. str_literal = QuotedString('"') str_literal.setParseAction(str_literal_to_hex) var_name = Combine(Word('$') + Word(pyparsing.alphas)) var_name.setParseAction(var_name_to_value) # Here we populate the list of contextual tips. # Explicit opcode names op_names = [str(i) for i in OPCODE_NAMES.keys()] op_names_explicit = ' '.join(op_names) def is_small_int(op): """True if op is one of OP_1, OP_2, ...OP_16""" try: i = int(op[3:]) return True except ValueError: return False op_names_implicit = ' '.join([i[3:] for i in op_names if not is_small_int(i)]) # Hex, implicit (e.g. 'a') and explicit (e.g. '0x0a') explicit_hex = Combine(Word('0x') + Word(pyparsing.hexnums) + pyparsing.WordEnd()) implicit_hex = Combine(pyparsing.WordStart() + OneOrMore(Word(pyparsing.hexnums)) + pyparsing.WordEnd()) explicit_hex.setParseAction(hex_to_formatted_hex) implicit_hex.setParseAction(hex_to_formatted_hex) # Opcodes, implicit (e.g. 'ADD') and explicit (e.g. 'OP_ADD') explicit_op = pyparsing.oneOf(op_names_explicit) implicit_op = Combine(pyparsing.WordStart() + pyparsing.oneOf(op_names_implicit)) implicit_op.setParseAction(implicit_opcode_to_explicit) contexts = pyparsing.Optional(var_name('Variable') | str_literal('String literal') | explicit_op('Opcode') | implicit_op('Opcode') | explicit_hex('Hex') | implicit_hex('Hex')) matches = [(i[0].asDict(), i[1], i[2]) for i in contexts.scanString(text)] context_tips = [] for i in matches: d = i[0] if len(d.items()) == 0: continue match_type, value = d.items()[0] start = i[1] end = i[2] context_tips.append( (start, end, value, match_type) ) # Now we do the actual transformation. s = text s = var_name.transformString(s) s = str_literal.transformString(s) s = implicit_op.transformString(s) s = implicit_hex.transformString(s) s = explicit_hex.transformString(s) return s, context_tips
def eval_script(stack, scriptIn, debug=False): if len(scriptIn) > MAX_SCRIPT_SIZE: raise EvalScriptError( "script too large; got %d bytes; maximum %d bytes" % (len(scriptIn), MAX_SCRIPT_SIZE), stack=stack, scriptIn=scriptIn, ) altstack = [] vfExec = [] pbegincodehash = 0 nOpCount = [0] if debug: print("-- start script --") for (sop, sop_data, sop_pc) in scriptIn.raw_iter(): fExec = _CheckExec(vfExec) if debug and (sop <= OP_PUSHDATA4 or fExec or (OP_IF <= sop <= OP_ENDIF)): pp([s.hex() if type(s) == bytes else s for s in stack]) print(OPCODE_NAMES.get(sop, sop)) if sop in DISABLED_OPCODES: raise EvalScriptError if sop > OP_16: nOpCount[0] += 1 if nOpCount[0] > MAX_SCRIPT_OPCODES: raise EvalScriptError def check_args(n): if len(stack) < n: raise EvalScriptError if sop <= OP_PUSHDATA4: if len(sop_data) > MAX_SCRIPT_ELEMENT_SIZE: raise EvalScriptError 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 = sop - (OP_1 - 1) stack.append(bitcoin.core._bignum.bn2vch(v)) elif sop in _ISA_BINOP: _BinOp(sop, stack) elif sop in _ISA_UNOP: _UnaryOp(sop, stack) 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 = CScript(scriptIn[pbegincodehash:]) _CheckMultiSig(sop, tmpScript, stack, nOpCount) elif sop == OP_CHECKSIG or sop == OP_CHECKSIGVERIFY: check_args(2) vchPubKey = stack[-1] vchSig = stack[-2] tmpScript = CScript(scriptIn[pbegincodehash:]) # Drop the signature, since there's no way for a signature to sign itself # # Of course, this can only come up in very contrived cases now that # scriptSig and scriptPubKey are processed separately. tmpScript = FindAndDelete(tmpScript, CScript([vchSig])) ok = _CheckSig(vchSig, vchPubKey, tmpScript) if not ok and sop == OP_CHECKSIGVERIFY: raise EvalScriptError 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(bitcoin.core._bignum.bn2vch(bn)) elif sop == OP_DROP: check_args(1) stack.pop() elif sop == OP_DUP: check_args(1) v = stack[-1] stack.append(v) elif sop == OP_ELSE: if len(vfExec) == 0: raise EvalScriptError vfExec[-1] = not vfExec[-1] elif sop == OP_ENDIF: if len(vfExec) == 0: raise EvalScriptError 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 EvalScriptError elif sop == OP_FROMALTSTACK: if len(altstack) < 1: raise EvalScriptError v = altstack.pop() stack.append(v) elif sop == OP_HASH160: check_args(1) stack.append(bitcoin.core.serialize.Hash160(stack.pop())) elif sop == OP_HASH256: check_args(1) stack.append(bitcoin.core.serialize.Hash(stack.pop())) elif sop == OP_IF or sop == OP_NOTIF: val = False if fExec: check_args(1) vch = stack.pop() 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: 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()) if n < 0 or n >= len(stack): raise EvalScriptError vch = stack[-n - 1] if sop == OP_ROLL: del stack[-n - 1] stack.append(vch) elif sop == OP_RETURN: raise EvalScriptError 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(bitcoin.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 = stack.pop() altstack.append(v) elif sop == OP_TUCK: check_args(2) vch = stack[-1] stack.insert(len(stack) - 2, vch) elif sop == OP_VERIFY: check_args(1) v = _CastToBool(stack[-1]) if v: stack.pop() else: raise EvalScriptError elif sop == OP_WITHIN: check_args(3) bn3 = _CastToBigNum(stack[-1]) bn2 = _CastToBigNum(stack[-2]) bn1 = _CastToBigNum(stack[-3]) stack.pop() stack.pop() stack.pop() v = (bn2 <= bn1) and (bn1 < bn3) if v: stack.append(b"\x01") else: # FIXME: this is incorrect, but not caught by existing # test cases stack.append(b"\x00") else: raise EvalScriptError # size limits if len(stack) + len(altstack) > MAX_STACK_ITEMS: raise EvalScriptError # Unterminated IF/NOTIF/ELSE block if len(vfExec): raise EvalScriptError if debug: pp([s.hex() if type(s) == bytes else s for s in stack]) print("-- end --") return stack
def do_step(self): """Returns whether another step can be done.""" if self.step_counter == -1: txt = str(self.tx_script.toPlainText()) scr = CScript(txt.decode('hex')) # So we can show the opcode in the stack log self.script_ops = [i for i in scr.raw_iter()] self.stack.set_script(scr, self.tx, self.inIdx) self.stack_iterator = self.stack.step() self.stack_log.clear() self.step_counter += 1 step_again = False try: self.step_counter += 1 stack_state, action = self.stack_iterator.next() new_stack = [i.encode('hex') for i in reversed(stack_state)] self.stack_view.clear() self.stack_view.addItems(new_stack) op_name = OPCODE_NAMES.get(self.script_ops[self.step_counter - 1][0], 'PUSHDATA') self.highlight_step(self.script_ops[self.step_counter - 1]) item = QTreeWidgetItem(map(lambda i: str(i), [self.step_counter, op_name, action])) item.setTextAlignment(0, Qt.AlignLeft) item.setToolTip(1, 'Step {} operation'.format(self.step_counter)) item.setToolTip(2, 'Result of step {}'.format(self.step_counter)) self.stack_log.insertTopLevelItem(0, item) self.stack_result.setText(action) self.stack_result.setProperty('hasError', False) step_again = True except StopIteration: self.stack_result.setText('End of script.') except Exception as e: self.stack_result.setText(str(e)) self.stack_result.setProperty('hasError', True) finally: self.style().polish(self.stack_result) return step_again