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 dump_signatures(tx, tx_in, tx_out, idx, netcode, address_prefix, traceback_f, disassembly_level): signatures = [] for opcode in opcode_list(tx_in.script): if not opcode.startswith("OP_"): try: signatures.append(parse_signature_blob(h2b(opcode[1:-1]))) except UnexpectedDER: pass if signatures: sig_types_identical = (tuple(zip(*signatures))[1].count( signatures[0][1]) == len(signatures)) i = 1 if len(signatures) > 1 else '' for sig_pair, sig_type in signatures: print(" r{0}: {1:#x}\n s{0}: {2:#x}".format( i, *sig_pair)) if not sig_types_identical and tx_out: print(" z{}: {:#x} {}".format( i, tx.signature_hash(tx_out.script, idx, sig_type), sighash_type_to_string(sig_type))) if i: i += 1 if sig_types_identical and tx_out: print(" z:{} {:#x} {}".format( ' ' if i else '', tx.signature_hash(tx_out.script, idx, sig_type), sighash_type_to_string(sig_type)))
def __eq__(a, b): if a.sighash != b.sighash: if a.sighash is not None and b.sighash is not None: return False rv = a.utxo == b.utxo and \ a.witness_utxo == b.witness_utxo and \ a.redeem_script == b.redeem_script and \ a.witness_script == b.witness_script and \ a.my_index == b.my_index and \ a.bip32_paths == b.bip32_paths and \ sorted(a.part_sigs.keys()) == sorted(b.part_sigs.keys()) if rv: # NOTE: equality test on signatures requires parsing DER stupidness # and some maybe understanding of R/S values on curve that I don't have. assert all( parse_signature_blob(a.part_sigs[k]) == parse_signature_blob( b.part_sigs[k]) for k in a.part_sigs) return rv
def add_signature_annotations(annotations, signature_blob, signature_for_hash_type_f, output_script): sig_pair, sig_type = parse_signature_blob(signature_blob) annotations.append("r: {0:#066x}".format(sig_pair[0])) annotations.append("s: {0:#066x}".format(sig_pair[1])) sig_hash = signature_for_hash_type_f(sig_type, output_script) annotations.append("z: {0:#066x}".format(sig_hash)) annotations.append("signature type %s" % sighash_type_to_string(sig_type)) addresses = [] pairs = possible_public_pairs_for_signature(generator_secp256k1, sig_hash, sig_pair) for pair in pairs: for comp in (True, False): address = public_pair_to_bitcoin_address(pair, compressed=comp, address_prefix=b'\0') addresses.append(address) annotations.append(" sig for %s" % " ".join(addresses))
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 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 dump_signatures(tx, tx_in, tx_out, idx, netcode, address_prefix, traceback_f, disassembly_level): signatures = [] for opcode in opcode_list(tx_in.script): if not opcode.startswith("OP_"): try: signatures.append(parse_signature_blob(h2b(opcode[1:-1]))) except UnexpectedDER: pass if signatures: sig_types_identical = ( tuple(zip(*signatures))[1].count(signatures[0][1]) == len(signatures)) i = 1 if len(signatures) > 1 else '' for sig_pair, sig_type in signatures: print(" r{0}: {1:#x}\n s{0}: {2:#x}".format(i, *sig_pair)) if not sig_types_identical and tx_out: print(" z{}: {:#x} {}".format(i, tx.signature_hash(tx_out.script, idx, sig_type), sighash_type_to_string(sig_type))) if i: i += 1 if sig_types_identical and tx_out: print(" z:{} {:#x} {}".format(' ' if i else '', tx.signature_hash( tx_out.script, idx, sig_type), sighash_type_to_string(sig_type)))
def dump_tx(tx, netcode, verbose_signature, disassembly_level, do_trace): address_prefix = address_prefix_for_netcode(netcode) tx_bin = stream_to_bytes(tx.stream) print("Version: %2d tx hash %s %d bytes " % (tx.version, tx.id(), len(tx_bin))) print("TxIn count: %d; TxOut count: %d" % (len(tx.txs_in), len(tx.txs_out))) if tx.lock_time == 0: meaning = "valid anytime" elif tx.lock_time < LOCKTIME_THRESHOLD: meaning = "valid after block index %d" % tx.lock_time else: when = datetime.datetime.utcfromtimestamp(tx.lock_time) meaning = "valid on or after %s utc" % when.isoformat() print("Lock time: %d (%s)" % (tx.lock_time, meaning)) print("Input%s:" % ('s' if len(tx.txs_in) != 1 else '')) missing_unspents = tx.missing_unspents() traceback_f = trace_script if do_trace else None for idx, tx_in in enumerate(tx.txs_in): if disassembly_level > 0: signature_for_hash_type_f = lambda hash_type, script: tx.signature_hash( script, idx, hash_type) if tx.is_coinbase(): print("%4d: COINBASE %12.5f mBTC" % (idx, satoshi_to_mbtc(tx.total_in()))) else: suffix = "" if tx.missing_unspent(idx): tx_out = None address = tx_in.bitcoin_address(address_prefix=address_prefix) else: tx_out = tx.unspents[idx] sig_result = " sig ok" if tx.is_signature_ok(idx, traceback_f=traceback_f) else " BAD SIG" suffix = " %12.5f mBTC %s" % (satoshi_to_mbtc(tx_out.coin_value), sig_result) address = tx_out.bitcoin_address(netcode=netcode) t = "%4d: %34s from %s:%-4d%s" % (idx, address, b2h_rev(tx_in.previous_hash), tx_in.previous_index, suffix) print(t.rstrip()) if disassembly_level > 0: out_script = b'' if tx_out: out_script = tx_out.script for (pre_annotations, pc, opcode, instruction, post_annotations) in \ disassemble_scripts(tx_in.script, out_script, signature_for_hash_type_f): for l in pre_annotations: print(" %s" % l) print( " %4x: %02x %s" % (pc, opcode, instruction)) for l in post_annotations: print(" %s" % l) if verbose_signature: signatures = [] for opcode in opcode_list(tx_in.script): if not opcode.startswith("OP_"): try: signatures.append(parse_signature_blob(h2b(opcode))) except UnexpectedDER: pass if signatures: sig_types_identical = (zip(*signatures)[1]).count(signatures[0][1]) == len(signatures) i = 1 if len(signatures) > 1 else '' for sig_pair, sig_type in signatures: print(" r{0}: {1:#x}\n s{0}: {2:#x}".format(i, *sig_pair)) if not sig_types_identical and tx_out: print(" z{}: {:#x} {}".format(i, tx.signature_hash(tx_out.script, idx, sig_type), sighash_type_to_string(sig_type))) if i: i += 1 if sig_types_identical and tx_out: print(" z:{} {:#x} {}".format(' ' if i else '', tx.signature_hash(tx_out.script, idx, sig_type), sighash_type_to_string(sig_type))) print("Output%s:" % ('s' if len(tx.txs_out) != 1 else '')) for idx, tx_out in enumerate(tx.txs_out): amount_mbtc = satoshi_to_mbtc(tx_out.coin_value) address = tx_out.bitcoin_address(netcode=netcode) or "(unknown)" print("%4d: %34s receives %12.5f mBTC" % (idx, address, amount_mbtc)) if disassembly_level > 0: for (pre_annotations, pc, opcode, instruction, post_annotations) in \ disassemble_scripts(b'', tx_out.script, signature_for_hash_type_f): for l in pre_annotations: print(" %s" % l) print( " %4x: %02x %s" % (pc, opcode, instruction)) for l in post_annotations: print(" %s" % l) if not missing_unspents: print("Total input %12.5f mBTC" % satoshi_to_mbtc(tx.total_in())) print( "Total output %12.5f mBTC" % satoshi_to_mbtc(tx.total_out())) if not missing_unspents: print("Total fees %12.5f mBTC" % satoshi_to_mbtc(tx.fee()))
def dump_tx(tx, netcode, verbose_signature, disassembly_level, do_trace, use_pdb): address_prefix = address_prefix_for_netcode(netcode) tx_bin = stream_to_bytes(tx.stream) # print("Tx_type:%2d" % tx.tx_type) if TransactionUtils.isCFTransation(tx): print("original_hash : %s" % tx.cf_header.original_hash) print("target_amount : %s" % tx.cf_header.target_amount) print("pubkey : %s" % tx.cf_header.pubkey) print("end_time : %s" % tx.cf_header.end_time) print("pre_hash : %s" % tx.cf_header.pre_hash) print("lack_amount : %s" % tx.cf_header.lack_amount) print("Version: %2d tx hash %s %d bytes " % (tx.version, tx.id(), len(tx_bin))) print("TransactionIn count: %d; TransactionOut count: %d" % (len(tx.txs_in), len(tx.txs_out))) if tx.lock_time == 0: meaning = "valid anytime" elif tx.lock_time < LOCKTIME_THRESHOLD: meaning = "valid after block index %d" % tx.lock_time else: when = datetime.datetime.utcfromtimestamp(tx.lock_time) meaning = "valid on or after %s utc" % when.isoformat() print("Lock time: %d (%s)" % (tx.lock_time, meaning)) print("Input%s:" % ('s' if len(tx.txs_in) != 1 else '')) missing_unspents = tx.missing_unspents() def trace_script(old_pc, opcode, data, stack, altstack, if_condition_stack, is_signature): from pycoin.tx.script.tools import disassemble_for_opcode_data print("%3d : %02x %s" % (old_pc, opcode, disassemble_for_opcode_data(opcode, data))) if use_pdb: import pdb from pycoin.serialize import b2h print("stack: [%s]" % ', '.join(b2h(s) for s in stack)) if len(altstack) > 0: print("altstack: %s" % altstack) if len(if_condition_stack) > 0: print("condition stack: %s" % ', '.join(int(s) for s in if_condition_stack)) pdb.set_trace() traceback_f = trace_script if do_trace or use_pdb else None for idx, tx_in in enumerate(tx.txs_in): if disassembly_level > 0: def signature_for_hash_type_f(hash_type, script): return tx.signature_hash(script, idx, hash_type) if tx.is_coinbase(): print("%4d: COINBASE %12.5f mBTC" % (idx, satoshi_to_mbtc(tx.total_in()))) else: suffix = "" if tx.missing_unspent(idx): tx_out = None address = tx_in.bitcoin_address(address_prefix=address_prefix) else: tx_out = tx.unspents[idx] sig_result = " sig ok" if tx.is_signature_ok( idx, traceback_f=traceback_f) else " BAD SIG" suffix = " %12.5f mBTC %s" % (satoshi_to_mbtc( tx_out.coin_value), sig_result) address = tx_out.bitcoin_address(netcode=netcode) t = "%4d: %34s from %s:%-4d%s" % (idx, address, b2h_rev(tx_in.previous_hash), tx_in.previous_index, suffix) print(t.rstrip()) if disassembly_level > 0: out_script = b'' if tx_out: out_script = tx_out.script for (pre_annotations, pc, opcode, instruction, post_annotations) in \ disassemble_scripts( tx_in.script, out_script, tx.lock_time, signature_for_hash_type_f): for l in pre_annotations: print(" %s" % l) print(" %4x: %02x %s" % (pc, opcode, instruction)) for l in post_annotations: print(" %s" % l) if verbose_signature: signatures = [] for opcode in opcode_list(tx_in.script): if not opcode.startswith("OP_"): try: signatures.append(parse_signature_blob( h2b(opcode))) except UnexpectedDER: pass if signatures: sig_types_identical = (zip(*signatures)[1]).count( signatures[0][1]) == len(signatures) i = 1 if len(signatures) > 1 else '' for sig_pair, sig_type in signatures: print(" r{0}: {1:#x}\n s{0}: {2:#x}".format( i, *sig_pair)) if not sig_types_identical and tx_out: print(" z{}: {:#x} {}".format( i, tx.signature_hash(tx_out.script, idx, sig_type), sighash_type_to_string(sig_type))) if i: i += 1 if sig_types_identical and tx_out: print(" z:{} {:#x} {}".format( ' ' if i else '', tx.signature_hash(tx_out.script, idx, sig_type), sighash_type_to_string(sig_type))) print("Output%s:" % ('s' if len(tx.txs_out) != 1 else '')) for idx, tx_out in enumerate(tx.txs_out): amount_mbtc = satoshi_to_mbtc(tx_out.coin_value) address = tx_out.bitcoin_address(netcode=netcode) or "(unknown)" print("%4d: %34s receives %12.5f mBTC" % (idx, address, amount_mbtc)) if disassembly_level > 0: for (pre_annotations, pc, opcode, instruction, post_annotations) in \ disassemble_scripts(b'', tx_out.script, tx.lock_time, signature_for_hash_type_f): for l in pre_annotations: print(" %s" % l) print(" %4x: %02x %s" % (pc, opcode, instruction)) for l in post_annotations: print(" %s" % l) if not missing_unspents: print("Total input %12.5f mBTC" % satoshi_to_mbtc(tx.total_in())) print("Total output %12.5f mBTC" % satoshi_to_mbtc(tx.total_out())) if not missing_unspents: print("Total fees %12.5f mBTC" % satoshi_to_mbtc(tx.fee()))
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