def disassemble_scripts(input_script, output_script, lock_time, signature_for_hash_type_f): "yield pre_annotations, pc, opcode, instruction, post_annotations" input_annotations_f, output_annotations_f = annotation_f_for_scripts( input_script, output_script, signature_for_hash_type_f) pc = 0 while pc < len(input_script): opcode, data, new_pc = get_opcode(input_script, pc) pre_annotations, post_annotations = input_annotations_f(pc, opcode, data) yield pre_annotations, pc, opcode, instruction_for_opcode(opcode, data), post_annotations pc = new_pc pc = 0 while pc < len(output_script): opcode, data, new_pc = get_opcode(output_script, pc) pre_annotations, post_annotations = output_annotations_f(pc, opcode, data) yield pre_annotations, pc, opcode, instruction_for_opcode(opcode, data), post_annotations pc = new_pc if not is_pay_to_script_hash(output_script): return stack = [] eval_script(input_script, signature_for_hash_type_f, lock_time, expected_hash_type=None, stack=stack) if stack: signatures, new_output_script = stack[:-1], stack[-1] new_input_script = bin_script(signatures) else: signatures, new_output_script, new_input_script = [], b'', b'' for r in disassemble_scripts(new_input_script, new_output_script, lock_time, signature_for_hash_type_f): yield r
def match_script_to_templates(self): """Examine the script passed in by tx_out_script and see if it matches the form of one of the templates in TEMPLATES. If so, return the form it matches; otherwise, return None.""" for script2 in TxScript.TEMPLATES: r = [] pc1 = pc2 = 0 while 1: if pc1 == len(self.script) and pc2 == len(script2): return r opcode1, data1, pc1 = tools.get_opcode(self.script, pc1) opcode2, data2, pc2 = tools.get_opcode(script2, pc2) if opcode2 == opcodes.OP_PUBKEY: l1 = len(data1) if l1 < 33 or l1 > 120: break r.append((opcode2, data1)) elif opcode2 == opcodes.OP_PUBKEYHASH: if len(data1) != 160/8: break r.append((opcode2, data1)) elif (opcode1, data1) != (opcode2, data2): break raise SolvingError("don't recognize output script")
def solve_finalize_commit(self, **kwargs): hash160_lookup = kwargs.get("hash160_lookup") sign_value = kwargs.get("sign_value") signature_type = kwargs.get("signature_type") existing_script = kwargs.get("existing_script") # FIXME validate on receiving the commit # validate payer sig opcode, data, pc = tools.get_opcode(existing_script, 0) # OP_0 opcode, payer_sig, pc = tools.get_opcode(existing_script, pc) sig_pair, actual_signature_type = parse_signature_blob(payer_sig) try: public_pair = encoding.sec_to_public_pair(self.payer_sec) sig_pair, signature_type = parse_signature_blob(payer_sig) valid = ecdsa.verify(ecdsa.generator_secp256k1, public_pair, sign_value, sig_pair) if not valid: raise Exception("Invalid payer public_pair!") except (encoding.EncodingError, UnexpectedDER): raise Exception("Invalid payer public_pair!") # sign private_key = hash160_lookup.get(encoding.hash160(self.payee_sec)) secret_exponent, public_pair, compressed = private_key payee_sig = self._create_script_signature( secret_exponent, sign_value, signature_type ) script_text = "OP_0 {payer_sig} {payee_sig} OP_1".format( payer_sig=b2h(payer_sig), payee_sig=b2h(payee_sig) ) return tools.compile(script_text)
def match_script_to_templates(self): """Examine the script passed in by tx_out_script and see if it matches the form of one of the templates in TEMPLATES. If so, return the form it matches; otherwise, return None.""" for script2 in TxScript.TEMPLATES: r = [] pc1 = pc2 = 0 while 1: if pc1 == len(self.script) and pc2 == len(script2): return r opcode1, data1, pc1 = tools.get_opcode(self.script, pc1) opcode2, data2, pc2 = tools.get_opcode(script2, pc2) if opcode2 == opcodes.OP_PUBKEY: l1 = len(data1) if l1 < 33 or l1 > 120: break r.append((opcode2, data1)) elif opcode2 == opcodes.OP_PUBKEYHASH: if len(data1) != 160 / 8: break r.append((opcode2, data1)) elif (opcode1, data1) != (opcode2, data2): break raise SolvingError("don't recognize output script")
def get_nulldata(tx): index, out = _get_nulldata_output(tx) if not out: raise exceptions.NoNulldataOutput(tx) opcode, data, pc = tools.get_opcode(out.script, 0) opcode, data, pc = tools.get_opcode(out.script, pc) return index, data
def _validate(reference_script_hex, untrusted_script_hex): ref_script_bin = h2b(reference_script_hex) untrusted_script_bin = h2b(untrusted_script_hex) r_pc = 0 u_pc = 0 while r_pc < len(ref_script_bin) and u_pc < len(untrusted_script_bin): r_opcode, r_data, r_pc = tools.get_opcode(ref_script_bin, r_pc) u_opcode, u_data, u_pc = tools.get_opcode(untrusted_script_bin, u_pc) if r_data is not None and b2h(r_data) == "deadbeef": continue # placeholder for expected variable if r_opcode != u_opcode or r_data != u_data: raise InvalidScript(b2h(untrusted_script_bin)) if r_pc != len(ref_script_bin) or u_pc != len(untrusted_script_bin): raise InvalidScript(b2h(untrusted_script_bin))
def get_word(script_bin, index): pc = 0 i = 0 while pc < len(script_bin) and i <= index: opcode, data, pc = tools.get_opcode(script_bin, pc) i += 1 if i != index + 1: raise ValueError(index) return opcode, data, tools.disassemble_for_opcode_data(opcode, data)
def get_word(script, index): pc = 0 i = 0 while pc < len(script) and i <= index: opcode, data, pc = tools.get_opcode(script, pc) i += 1 if i != index + 1: raise ValueError(index) return opcode, data, tools.disassemble_for_opcode_data(opcode, data)
def solve_finalize_commit(self, **kwargs): hash160_lookup = kwargs.get("hash160_lookup") signature_type = kwargs.get("signature_type") existing_script = kwargs.get("existing_script") # validate existing script reference_script_hex = _compile_commit_scriptsig( "deadbeef", "deadbeef", b2h(self.script)) _validate(reference_script_hex, b2h(existing_script)) # check provided payer signature try: opcode, data, pc = tools.get_opcode(existing_script, 0) # OP_0 opcode, payer_sig, pc = tools.get_opcode(existing_script, pc) # verify signature type sig_r_s, actual_signature_type = parse_signature_blob(payer_sig) assert (signature_type == actual_signature_type) # verify payer signature public_pair = encoding.sec_to_public_pair(self.payer_sec) sign_value = kwargs.get("sign_value") public_pair = encoding.sec_to_public_pair(self.payer_sec) sign_value = kwargs["signature_for_hash_type_f"]( signature_type, kwargs["script_to_hash"]) if not ecdsa.verify(ecdsa.generator_secp256k1, public_pair, sign_value, sig_r_s): raise InvalidPayerSignature("invalid r s values") except UnexpectedDER: raise InvalidPayerSignature("not in DER format") # sign private_key = hash160_lookup.get(encoding.hash160(self.payee_sec)) secret_exponent, public_pair, compressed = private_key payee_sig = self._create_sig(secret_exponent, **kwargs) script_asm = COMMIT_SCRIPTSIG.format(payer_sig=b2h(payer_sig), payee_sig=b2h(payee_sig)) return tools.compile(script_asm)
def who_signed_tx(tx, tx_in_idx, netcode='BTC'): """ Given a transaction (tx) an input index (tx_in_idx), attempt to figure out which addresses where used in signing (so far). This method depends on tx.unspents being properly configured. This should work on partially-signed MULTISIG transactions (it will return as many addresses as there are good signatures). Returns a list of ( address, sig_type ) pairs. Raises NoAddressesForScriptTypeError if addresses cannot be determined for the input's script. TODO: This does not yet support P2SH. """ tx_in = tx.txs_in[tx_in_idx] parent_tx_out_idx = tx_in.previous_index parent_tx_out_script = tx.unspents[tx_in_idx].script script_obj = script_obj_from_script(parent_tx_out_script) signed_by = [] if type(script_obj) not in (ScriptPayToAddress, ScriptPayToPublicKey, ScriptMultisig): raise NoAddressesForScriptTypeError( 'unable to determine signing addresses for script type of parent tx {}[{}]' .format(b2h_rev(tx_in.previous_hash), parent_tx_out_idx)) script = tx_in.script pc = 0 while pc < len(script): opcode, data, pc = get_opcode(script, pc) if data is None: continue try: sig_pair, sig_type = parse_signature_blob(data) except (ValueError, TypeError, binascii.Error, UnexpectedDER): continue sig_hash = tx.signature_hash(parent_tx_out_script, parent_tx_out_idx, sig_type) for sec_key in script_obj.sec_keys: public_pair = sec_to_public_pair(sec_key) if ecdsa_verify(generator_secp256k1, public_pair, sig_hash, sig_pair): addr_pfx = address_prefix_for_netcode(netcode) addr = public_pair_to_bitcoin_address(public_pair, address_prefix=addr_pfx) signed_by.append((addr, sig_type)) return signed_by
def extract_msg(script): msg_hex = unhexlify(script['hex']) opcode, msg, _ = tools.get_opcode(msg_hex, 1) if 0x51 <= opcode and opcode <= 0x60: msg = bytes(chr(opcode - 0x50), 'utf-8') return msg
def solve(self, **kwargs): """ The kwargs required depend upon the script type. hash160_lookup: dict-like structure that returns a secret exponent for a hash160 existing_script: existing solution to improve upon (optional) sign_value: the integer value to sign (derived from the transaction hash) signature_type: usually SIGHASH_ALL (1) """ # we need a hash160 => secret_exponent lookup db = kwargs.get("hash160_lookup") if db is None: raise SolvingError("missing hash160_lookup parameter") sign_value = kwargs.get("sign_value") signature_type = kwargs.get("signature_type") secs_solved = set() existing_signatures = [] existing_script = kwargs.get("existing_script") if existing_script: pc = 0 opcode, data, pc = tools.get_opcode(existing_script, pc) # ignore the first opcode while pc < len(existing_script): opcode, data, pc = tools.get_opcode(existing_script, pc) sig_pair, actual_signature_type = parse_signature_blob(data) for sec_key in self.sec_keys: try: public_pair = encoding.sec_to_public_pair(sec_key) sig_pair, signature_type = parse_signature_blob(data) v = ecdsa.verify(ecdsa.generator_secp256k1, public_pair, sign_value, sig_pair) if v: existing_signatures.append(data) secs_solved.add(sec_key) break except encoding.EncodingError: # if public_pair is invalid, we just ignore it pass for sec_key in self.sec_keys: if sec_key in secs_solved: continue if len(existing_signatures) >= self.n: break hash160 = encoding.hash160(sec_key) result = db.get(hash160) if result is None: continue secret_exponent, public_pair, compressed = result binary_signature = self._create_script_signature(secret_exponent, sign_value, signature_type) existing_signatures.append(b2h(binary_signature)) DUMMY_SIGNATURE = "OP_0" while len(existing_signatures) < self.n: existing_signatures.append(DUMMY_SIGNATURE) script = "OP_0 %s" % " ".join(s for s in existing_signatures) solution = tools.compile(script) return solution
def get_hash160_data(tx, output_index): out = tx.txs_out[output_index] opcode, data, pc = tools.get_opcode(out.script, 0) opcode, data, pc = tools.get_opcode(out.script, pc) opcode, data, pc = tools.get_opcode(out.script, pc) return data
def solve(self, **kwargs): """ The kwargs required depend upon the script type. hash160_lookup: dict-like structure that returns a secret exponent for a hash160 existing_script: existing solution to improve upon (optional) sign_value: the integer value to sign (derived from the transaction hash) signature_type: usually SIGHASH_ALL (1) """ # we need a hash160 => secret_exponent lookup db = kwargs.get("hash160_lookup") if db is None: raise SolvingError("missing hash160_lookup parameter") sign_value = kwargs.get("sign_value") signature_type = kwargs.get("signature_type") secs_solved = set() existing_signatures = [] existing_script = kwargs.get("existing_script") if existing_script: pc = 0 opcode, data, pc = tools.get_opcode(existing_script, pc) # ignore the first opcode while pc < len(existing_script): opcode, data, pc = tools.get_opcode(existing_script, pc) sig_pair, actual_signature_type = parse_signature_blob(data) for sec_key in self.sec_keys: try: public_pair = encoding.sec_to_public_pair(sec_key) sig_pair, signature_type = parse_signature_blob(data) v = ecdsa.verify(ecdsa.generator_secp256k1, public_pair, sign_value, sig_pair) if v: existing_signatures.append(data) secs_solved.add(sec_key) break except encoding.EncodingError: # if public_pair is invalid, we just ignore it pass for sec_key in self.sec_keys: if sec_key in secs_solved: continue if len(existing_signatures) >= self.n: break hash160 = encoding.hash160(sec_key) result = db.get(hash160) if result is None: continue secret_exponent, public_pair, compressed = result binary_signature = self._create_script_signature( secret_exponent, sign_value, signature_type) existing_signatures.append(b2h(binary_signature)) DUMMY_SIGNATURE = "OP_0" while len(existing_signatures) < self.n: existing_signatures.append(DUMMY_SIGNATURE) script = "OP_0 %s" % " ".join(s for s in existing_signatures) solution = tools.compile(script) return solution