def make_ms_address(M, keys, idx=0, is_change=0, addr_fmt=AF_P2SH, testnet=1, **make_redeem_args): # Construct addr and script need to represent a p2sh address import bech32 from pycoin.encoding import b2a_hashed_base58, hash160 if 'path_mapper' not in make_redeem_args: make_redeem_args['path_mapper'] = lambda cosigner: [HARD(45), cosigner, is_change, idx] script, pubkeys, xfp_paths = make_redeem(M, keys, **make_redeem_args) if addr_fmt == AF_P2WSH: hrp = ['bc', 'tb'][testnet] data = sha256(script).digest() addr = bech32.encode(hrp, 0, data) scriptPubKey = bytes([0x0, 0x20]) + data else: if addr_fmt == AF_P2SH: digest = hash160(script) elif addr_fmt == AF_P2WSH_P2SH: digest = hash160(b'\x00\x20' + sha256(script).digest()) else: raise ValueError(addr_fmt) prefix = bytes([196]) if testnet else bytes([5]) addr = b2a_hashed_base58(prefix + digest) scriptPubKey = bytes([0xa9, 0x14]) + digest + bytes([0x87]) return addr, scriptPubKey, script, zip(pubkeys, xfp_paths)
def doit(given_addr, path=None, addr_fmt=None, script=None): if not script: try: # prefer using xpub if we can mk = BIP32Node.from_wallet_key(master_xpub) sk = mk.subkey_for_path(path[2:]) except PublicPrivateMismatchError: mk = BIP32Node.from_wallet_key(simulator_fixed_xprv) sk = mk.subkey_for_path(path[2:]) if addr_fmt == AF_CLASSIC: # easy assert sk.address() == given_addr elif addr_fmt & AFC_PUBKEY: pkh = sk.hash160(use_uncompressed=False) if addr_fmt == AF_P2WPKH: hrp, data = bech32_decode(given_addr) decoded = convertbits(data[1:], 5, 8, False) assert hrp in {'tb', 'bc' } assert bytes(decoded[-20:]) == pkh else: assert addr_fmt == AF_P2WPKH_P2SH assert given_addr[0] in '23' expect = a2b_hashed_base58(given_addr)[1:] assert len(expect) == 20 assert hash160(b'\x00\x14' + pkh) == expect elif addr_fmt & AFC_SCRIPT: assert script, 'need a redeem/witness script' if addr_fmt == AF_P2SH: assert given_addr[0] in '23' expect = a2b_hashed_base58(given_addr)[1:] assert hash160(script) == expect elif addr_fmt == AF_P2WSH: hrp, data = bech32_decode(given_addr) assert hrp in {'tb', 'bc' } decoded = convertbits(data[1:], 5, 8, False) assert bytes(decoded[-32:]) == sha256(script).digest() elif addr_fmt == AF_P2WSH_P2SH: assert given_addr[0] in '23' expect = a2b_hashed_base58(given_addr)[1:] assert hash160(b'\x00\x20' + sha256(script).digest()) == expect else: raise pytest.fail(f'not ready for {addr_fmt:x} yet') else: raise ValueError(addr_fmt) return sk if not script else None
def solve_change(self, **kwargs): hash160_lookup = kwargs["hash160_lookup"] spend_secret = kwargs["spend_secret"] private_key = hash160_lookup.get(encoding.hash160(self.payer_sec)) secret_exponent, public_pair, compressed = private_key sig = self._create_sig(secret_exponent, **kwargs) spend_secret_hash = get_deposit_spend_secret_hash(b2h(self.script)) provided_spend_secret_hash = b2h(encoding.hash160(h2b(spend_secret))) assert (spend_secret_hash == provided_spend_secret_hash) script_asm = CHANGE_SCRIPTSIG.format(sig=b2h(sig), secret=spend_secret) return tools.compile(script_asm)
def p2sh_address(key): """ Function to convert the given private key to the P2SH key used to signify a SegWit address starting with '3'. This SegWit address is a <redeemScript> (https://bitcoincore.org/en/segwit_wallet_dev/#creation-of-p2sh-p2wpkh-address). It starts with a OP_0, followed by a canonical push of the keyhash (i.e. 0x0014{20-byte keyhash}). """ return b2a_hashed_base58(b'\5' + hash160(b'\x00\x14' + hash160(key.sec(use_uncompressed=False))))
def solve_change(self, **kwargs): hash160_lookup = kwargs["hash160_lookup"] spend_secret = kwargs["spend_secret"] private_key = hash160_lookup.get(encoding.hash160(self.payer_sec)) secret_exponent, public_pair, compressed = private_key sig = self._create_script_signature( secret_exponent, kwargs["sign_value"], kwargs["signature_type"] ) spend_secret_hash = get_deposit_spend_secret_hash(self.script) provided_spend_secret_hash = b2h(hash160(h2b(spend_secret))) assert(spend_secret_hash == provided_spend_secret_hash) script_text = "{sig} {secret} OP_1 OP_0".format( sig=b2h(sig), secret=spend_secret ) return tools.compile(script_text)
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 doit(given_addr, path, addr_fmt): mk = BIP32Node.from_wallet_key(master_xpub) sk = mk.subkey_for_path(path[2:]) if addr_fmt == AF_CLASSIC: # easy assert sk.address() == given_addr elif addr_fmt & AFC_PUBKEY: pkh = sk.hash160(use_uncompressed=False) if addr_fmt == AF_P2WPKH: hrp, data = bech32_decode(given_addr) decoded = convertbits(data[1:], 5, 8, False) assert hrp in {'tb', 'bc'} assert bytes(decoded[-20:]) == pkh else: assert addr_fmt == AF_P2WPKH_P2SH assert given_addr[0] in '23' expect = a2b_hashed_base58(given_addr)[1:] assert len(expect) == 20 assert hash160(b'\x00\x14' + pkh) == expect elif addr_fmt & AFC_SCRIPT: raise pytest.fail('multisig/p2sh addr not handled') else: raise ValueError(addr_fmt)
def make_change_addr(wallet, style): # provide script, pubkey and xpath for a legit-looking change output import struct, random from pycoin.encoding import hash160 redeem_scr, actual_scr = None, None deriv = [12, 34, random.randint(0, 1000)] xfp, = struct.unpack('I', wallet.fingerprint()) dest = wallet.subkey_for_path('/'.join(str(i) for i in deriv)) target = dest.hash160() assert len(target) == 20 is_segwit = False if style == 'p2pkh': redeem_scr = bytes([0x76, 0xa9, 0x14]) + target + bytes([0x88, 0xac]) elif style == 'p2wpkh': redeem_scr = bytes([0, 20]) + target is_segwit = True elif style == 'p2wpkh-p2sh': redeem_scr = bytes([0, 20]) + target actual_scr = bytes([0xa9, 0x14]) + hash160(redeem_scr) + bytes([0x87]) else: raise ValueError('cant make fake change output of type: ' + style) return redeem_scr, actual_scr, is_segwit, dest.sec(), struct.pack( '4I', xfp, *deriv)
def address_for_pay_to_script(script, netcode=None): if netcode is None: netcode = get_current_netcode() address_prefix = pay_to_script_prefix_for_netcode(netcode) if address_prefix: return encoding.hash160_sec_to_bitcoin_address( encoding.hash160(script), address_prefix=address_prefix) return None
def solve_revoke(self, **kwargs): hash160_lookup = kwargs["hash160_lookup"] revoke_secret = kwargs["revoke_secret"] private_key = hash160_lookup.get(encoding.hash160(self.payer_sec)) secret_exponent, public_pair, compressed = private_key sig = self._create_sig(secret_exponent, **kwargs) return tools.compile( REVOKE_SCRIPTSIG.format(sig=b2h(sig), revoke_secret=revoke_secret))
def solve_timeout(self, **kwargs): hash160_lookup = kwargs["hash160_lookup"] private_key = hash160_lookup.get(encoding.hash160(self.payer_sec)) secret_exponent, public_pair, compressed = private_key sig = self._create_script_signature( secret_exponent, kwargs["sign_value"], kwargs["signature_type"] ) return tools.compile("{sig} OP_0 OP_0".format(sig=b2h(sig)))
def sign_data(self, data, privKey): with self.lock: symbols_set = string.ascii_letters + string.digits salt = ''.join([random.choice(symbols_set) for _ in xrange(20)]) data = int((hash160(str(data) + salt)).encode('hex'), 16) return { 'salt': salt, 'sign': ecdsa.sign(ecdsa.generator_secp256k1, privKey, data), }
def build_p2sh_lookup(args): scripts, warnings = parse_scripts(args) for w in warnings: print(w) p2sh_lookup = {} for script in scripts: p2sh_lookup[hash160(script)] = script return p2sh_lookup
def solve_payout(self, **kwargs): hash160_lookup = kwargs["hash160_lookup"] spend_secret = kwargs["spend_secret"] private_key = hash160_lookup.get(encoding.hash160(self.payee_sec)) secret_exponent, public_pair, compressed = private_key sig = self._create_sig(secret_exponent, **kwargs) return tools.compile( PAYOUT_SCRIPTSIG.format(sig=b2h(sig), spend_secret=spend_secret))
def solve_create_commit(self, **kwargs): hash160_lookup = kwargs["hash160_lookup"] private_key = hash160_lookup.get(encoding.hash160(self.payer_sec)) secret_exponent, public_pair, compressed = private_key sig = self._create_sig(secret_exponent, **kwargs) signature_placeholder = kwargs.get("signature_placeholder", DEFAULT_PLACEHOLDER_SIGNATURE) script_asm = COMMIT_SCRIPTSIG.format( payer_sig=b2h(sig), payee_sig=b2h(signature_placeholder)) return tools.compile(script_asm)
def hash160(self, use_uncompressed=None): """ Return the hash160 representation of this key, if available. If use_uncompressed is not set, the preferred representation is returned. """ use_uncompressed = self._use_uncompressed(use_uncompressed) if self.public_pair() is None: if use_uncompressed: return self._hash160_uncompressed return self._hash160_compressed if use_uncompressed: if self._hash160_uncompressed is None: self._hash160_uncompressed = hash160(self.sec(use_uncompressed=use_uncompressed)) return self._hash160_uncompressed if self._hash160_compressed is None: self._hash160_compressed = hash160(self.sec(use_uncompressed=use_uncompressed)) return self._hash160_compressed
def get_hash(self): if hasattr(self, "hash"): return self.hash full_data = "" full_data += str(self.name + self.last_name + str(self.sex) + str(self.date) + self.ID) self.hash = encoding.hash160(full_data.encode()) print(self.hash) return self.hash
def address(self, address_prefix=b'\0'): if self.is_coinbase(): return "(coinbase)" # attempt to return the source address sec = self.public_key_sec() if sec: bitcoin_address = encoding.hash160_sec_to_bitcoin_address( encoding.hash160(sec), address_prefix=address_prefix) return bitcoin_address return "(unknown)"
def test_public(sim_execfile): "verify contents of public 'dump' file" from pycoin.key.BIP32Node import BIP32Node from pycoin.contrib.segwit_addr import encode as sw_encode from pycoin.contrib.segwit_addr import decode as sw_decode from pycoin.encoding import a2b_hashed_base58, hash160 pub = sim_execfile('devtest/dump_public.py') assert 'Error' not in pub #print(pub) pub, dev = pub.split('#DEBUG#', 1) assert 'pub' in pub assert 'prv' not in pub assert 'prv' in dev lines = [i.strip() for i in pub.split('\n')] for ln in lines: if ln[1:4] == 'pub': node_pub = BIP32Node.from_wallet_key(ln) break node_prv = BIP32Node.from_wallet_key(dev.strip()) # pub and private are linked assert node_prv.hwif(as_private=False) == node_pub.hwif() # check every path we derived count = 0 for ln in lines: if ln[0:1] == 'm' and '=>' in ln: subpath, result = ln.split(' => ', 1) sk = node_prv.subkey_for_path(subpath[2:]) if result[0:2] in {'tp', 'xp'}: expect = BIP32Node.from_wallet_key(result) assert sk.hwif(as_private=False) == result elif result[0] in '1mn': assert result == sk.address(False) elif result[0:3] in {'bc1', 'tb1'}: h20 = sk.hash160() assert result == sw_encode(result[0:2], 0, h20) elif result[0] in '23': h20 = hash160(b'\x00\x14' + sk.hash160()) assert h20 == a2b_hashed_base58(result)[1:] else: raise ValueError(result) count += 1 print("OK: %s" % ln) assert count > 12
def payto_for_path(self, path): """Get the payto script for the path. See also :meth:`.script` :param: path: the derivation path :type: path: str :return: the script :rtype: LeafPayTo """ script = self.script_for_path(path) payto = LeafPayTo(hash160=encoding.hash160(script.script()), path=path) return payto
def doit(addr, sk): if addr[0] in '1mn': assert addr == sk.address(False) elif addr[0:3] in {'bc1', 'tb1'}: h20 = sk.hash160() assert addr == sw_encode(addr[0:2], 0, h20) elif addr[0] in '23': h20 = hash160(b'\x00\x14' + sk.hash160()) assert h20 == a2b_hashed_base58(addr)[1:] else: raise ValueError(addr)
def _calculate_all(self): for attr in "_secret_exponent _public_pair _wif_uncompressed _wif_compressed _sec_compressed" \ " _sec_uncompressed _hash160_compressed _hash160_uncompressed _address_compressed" \ " _address_uncompressed _netcode".split(): setattr(self, attr, getattr(self, attr, None)) if self._hierarchical_wallet: if self._hierarchical_wallet.is_private: self._secret_exponent = self._hierarchical_wallet.secret_exponent else: self._public_pair = self._hierarchical_wallet.public_pair self._netcode = self._hierarchical_wallet.netcode wif_prefix = wif_prefix_for_netcode(self._netcode) if self._secret_exponent: self._wif_uncompressed = secret_exponent_to_wif( self._secret_exponent, compressed=False, wif_prefix=wif_prefix) self._wif_compressed = secret_exponent_to_wif( self._secret_exponent, compressed=True, wif_prefix=wif_prefix) self._public_pair = ecdsa.public_pair_for_secret_exponent( ecdsa.generator_secp256k1, self._secret_exponent) if self._public_pair: self._sec_compressed = public_pair_to_sec(self._public_pair, compressed=True) self._sec_uncompressed = public_pair_to_sec(self._public_pair, compressed=False) self._hash160_compressed = hash160(self._sec_compressed) self._hash160_uncompressed = hash160(self._sec_uncompressed) address_prefix = address_prefix_for_netcode(self._netcode) if self._hash160_compressed: self._address_compressed = hash160_sec_to_bitcoin_address( self._hash160_compressed, address_prefix=address_prefix) if self._hash160_uncompressed: self._address_uncompressed = hash160_sec_to_bitcoin_address( self._hash160_uncompressed, address_prefix=address_prefix)
def solve_create_commit(self, **kwargs): hash160_lookup = kwargs["hash160_lookup"] private_key = hash160_lookup.get(encoding.hash160(self.payer_sec)) secret_exponent, public_pair, compressed = private_key sig = self._create_script_signature( secret_exponent, kwargs["sign_value"], kwargs["signature_type"] ) signature_placeholder = kwargs.get("signature_placeholder", DEFAULT_PLACEHOLDER_SIGNATURE) script_text = "OP_0 {payer_sig} {payee_sig} OP_1".format( payer_sig=b2h(sig), payee_sig=b2h(signature_placeholder) ) return tools.compile(script_text)
def scriptsig2adddr(scriptsig): if type(scriptsig) is not bytes: scriptsig = binascii.unhexlify(scriptsig) pos = 0 ln = scriptsig[pos] pos += 1 sig = sigscript[pos:pos + ln] pos += ln ln = scriptsig[pos] pos += 1 pubkey = scriptsig[pos:pos + ln:] pubkeyhash = encoding.hash160(pubkey) r = encoding.b2a_hashed_base58(settings.PUB_PREFIX + pubkeyhash) return r
def fromPrivkey(cls, ecdsaPrivkey): """Returns a new Address object from the private key. The private key can be used to get the public key, hence the need only for the private key. """ rawPrivkey = cls.PRIVATE_KEY_PREFIX + ecdsaPrivkey.to_string() privkey = b2a_hashed_base58(rawPrivkey) ecdsaPubkey = ecdsaPrivkey.get_verifying_key() rawPubkey = cls.PUBLIC_KEY_PREFIX + hash160( "\x04" + ecdsaPubkey.to_string()) pubkey = b2a_hashed_base58(rawPubkey) return cls(pubkey, privkey)
def sign_change_recover(get_txs_func, payer_wif, rawtx, deposit_script_hex, spend_secret): """ Sign change recover transaction. Args: get_txs_func (function): txid list -> matching raw transactions. payer_wif (str): Payer wif used for signing. rawtx (str): Change raw transaction to be signed. deposit_script_hex (str): Matching deposit script for transaction. spend_secret (str): Deposit spend secret to recover change. Return: Signed change raw transaction. """ validate_deposit_script(deposit_script_hex) spend_secret_hash = get_deposit_spend_secret_hash(deposit_script_hex) provided_spend_secret_hash = b2h(encoding.hash160(h2b(spend_secret))) assert provided_spend_secret_hash == spend_secret_hash return _sign_deposit_recover(get_txs_func, payer_wif, rawtx, deposit_script_hex, "change", spend_secret)
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 address(self, use_uncompressed=None): """ Return the public address representation of this key, if available. If use_uncompressed is not set, the preferred representation is returned. """ hash_160 = self.hash160(use_uncompressed=use_uncompressed) if hash_160: is_p2pwk = address_wit_for_netcode(self._netcode) if is_p2pwk: witness = ScriptPayToAddressWit(b'\0', hash_160) return witness.info(self._netcode)['address_f']() is_p2pwk_in_p2sh = pay_to_script_wit_for_netcode(self._netcode) if is_p2pwk_in_p2sh: address_prefix = pay_to_script_prefix_for_netcode( self._netcode) wit_script = ScriptPayToAddressWit(b'\0', hash_160).script() hash_160 = hash160(wit_script) else: address_prefix = address_prefix_for_netcode(self._netcode) return hash160_sec_to_bitcoin_address( hash_160, address_prefix=address_prefix) return None
def getJSONData(self): """Returns a dict that can later be plugged into the fromObj method for later retrieval of an Address. This is particularly useful for storing/retrieving from a data store.""" return {"pubkey": self.pubkey, "privkey": self.privkey} class TestnetAddress(Address): """TestnetAddress represents a Bitcoin Testnet address. Be sure that bitcoind is running with the "-testnet" flag. """ PUBLIC_KEY_PREFIX = "\x6F" PRIVATE_KEY_PREFIX = "\xEF" if __name__ == "__main__": # test the key generation test_key = 'a' * 32 address = Address.new(test_key) print address.pubkey rawPubkey = Address.PUBLIC_KEY_PREFIX + hash160( "\x04" + address.rawPubkey()) print b2a_hashed_base58(rawPubkey) assert address.privkey \ == "5JZB2s2RCtRUunKiqMbb6rAj3Z7TkJwa8zknL1cfTFpWoQArd6n", \ "address generation isn't what was expected" assert address.rawPrivkey() == test_key, "wrong priv key"
def main(): parser = argparse.ArgumentParser( description="Manipulate bitcoin (or alt coin) transactions.", epilog=EPILOG) parser.add_argument( '-t', "--transaction-version", type=int, help='Transaction version, either 1 (default) or 3 (not yet supported).' ) parser.add_argument( '-l', "--lock-time", type=parse_locktime, help='Lock time; either a block' 'index, or a date/time (example: "2014-01-01T15:00:00"') parser.add_argument( '-n', "--network", default="BTC", help='Define network code (M=Bitcoin mainnet, T=Bitcoin testnet).') parser.add_argument( '-a', "--augment", action='store_true', help='augment tx by adding any missing spendable metadata by fetching' ' inputs from cache and/or web services') parser.add_argument( "-i", "--fetch-spendables", metavar="address", action="append", help= 'Add all unspent spendables for the given bitcoin address. This information' ' is fetched from web services.') parser.add_argument( '-f', "--private-key-file", metavar="path-to-private-keys", action="append", help= 'file containing WIF or BIP0032 private keys. If file name ends with .gpg, ' '"gpg -d" will be invoked automatically. File is read one line at a time, and if ' 'the file contains only one WIF per line, it will also be scanned for a bitcoin ' 'address, and any addresses found will be assumed to be public keys for the given' ' private key.', type=argparse.FileType('r')) parser.add_argument('-g', "--gpg-argument", help='argument to pass to gpg (besides -d).', default='') parser.add_argument("--remove-tx-in", metavar="tx_in_index_to_delete", action="append", type=int, help='remove a tx_in') parser.add_argument("--remove-tx-out", metavar="tx_out_index_to_delete", action="append", type=int, help='remove a tx_out') parser.add_argument( '-F', "--fee", help='fee, in satoshis, to pay on transaction, or ' '"standard" to auto-calculate. This is only useful if the "split pool" ' 'is used; otherwise, the fee is automatically set to the unclaimed funds.', default="standard", metavar="transaction-fee", type=parse_fee) parser.add_argument( '-C', "--cache", help='force the resultant transaction into the transaction cache.' ' Mostly for testing.', action='store_true'), parser.add_argument( '-u', "--show-unspents", action='store_true', help='show TxOut items for this transaction in Spendable form.') parser.add_argument( '-b', "--bitcoind-url", help= 'URL to bitcoind instance to validate against (http://user:pass@host:port).' ) parser.add_argument( '-o', "--output-file", metavar="path-to-output-file", type=argparse.FileType('wb'), help='file to write transaction to. This supresses most other output.') parser.add_argument( '-p', "--pay-to-script", metavar="pay-to-script", action="append", help= 'a hex version of a script required for a pay-to-script input (a bitcoin address that starts with 3)' ) parser.add_argument( '-P', "--pay-to-script-file", metavar="pay-to-script-file", nargs=1, type=argparse.FileType('r'), help= 'a file containing hex scripts (one per line) corresponding to pay-to-script inputs' ) parser.add_argument( "argument", nargs="+", help='generic argument: can be a hex transaction id ' '(exactly 64 characters) to be fetched from cache or a web service;' ' a transaction as a hex string; a path name to a transaction to be loaded;' ' a spendable 4-tuple of the form tx_id/tx_out_idx/script_hex/satoshi_count ' 'to be added to TxIn list; an address/satoshi_count to be added to the TxOut ' 'list; an address to be added to the TxOut list and placed in the "split' ' pool".') args = parser.parse_args() # defaults txs = [] spendables = [] payables = [] key_iters = [] TX_ID_RE = re.compile(r"^[0-9a-fA-F]{64}$") # there are a few warnings we might optionally print out, but only if # they are relevant. We don't want to print them out multiple times, so we # collect them here and print them at the end if they ever kick in. warning_tx_cache = None warning_get_tx = None warning_spendables = None if args.private_key_file: wif_re = re.compile(r"[1-9a-km-zA-LMNP-Z]{51,111}") # address_re = re.compile(r"[1-9a-kmnp-zA-KMNP-Z]{27-31}") for f in args.private_key_file: if f.name.endswith(".gpg"): gpg_args = ["gpg", "-d"] if args.gpg_argument: gpg_args.extend(args.gpg_argument.split()) gpg_args.append(f.name) popen = subprocess.Popen(gpg_args, stdout=subprocess.PIPE) f = popen.stdout for line in f.readlines(): # decode if isinstance(line, bytes): line = line.decode("utf8") # look for WIFs possible_keys = wif_re.findall(line) def make_key(x): try: return Key.from_text(x) except Exception: return None keys = [make_key(x) for x in possible_keys] for key in keys: if key: key_iters.append((k.wif() for k in key.subkeys(""))) # if len(keys) == 1 and key.hierarchical_wallet() is None: # # we have exactly 1 WIF. Let's look for an address # potential_addresses = address_re.findall(line) # update p2sh_lookup p2sh_lookup = {} if args.pay_to_script: for p2s in args.pay_to_script: try: script = h2b(p2s) p2sh_lookup[hash160(script)] = script except Exception: print("warning: error parsing pay-to-script value %s" % p2s) if args.pay_to_script_file: hex_re = re.compile(r"[0-9a-fA-F]+") for f in args.pay_to_script_file: count = 0 for l in f: try: m = hex_re.search(l) if m: p2s = m.group(0) script = h2b(p2s) p2sh_lookup[hash160(script)] = script count += 1 except Exception: print("warning: error parsing pay-to-script file %s" % f.name) if count == 0: print("warning: no scripts found in %s" % f.name) # we create the tx_db lazily tx_db = None for arg in args.argument: # hex transaction id if TX_ID_RE.match(arg): if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_get_tx = message_about_get_tx_env() tx_db = get_tx_db() tx = tx_db.get(h2b_rev(arg)) if not tx: for m in [ warning_tx_cache, warning_get_tx, warning_spendables ]: if m: print("warning: %s" % m, file=sys.stderr) parser.error("can't find Tx with id %s" % arg) txs.append(tx) continue # hex transaction data try: tx = Tx.tx_from_hex(arg) txs.append(tx) continue except Exception: pass is_valid = is_address_valid(arg, allowable_netcodes=[args.network]) if is_valid: payables.append((arg, 0)) continue try: key = Key.from_text(arg) # TODO: check network if key.wif() is None: payables.append((key.address(), 0)) continue # TODO: support paths to subkeys key_iters.append((k.wif() for k in key.subkeys(""))) continue except Exception: pass if os.path.exists(arg): try: with open(arg, "rb") as f: if f.name.endswith("hex"): f = io.BytesIO(codecs.getreader("hex_codec")(f).read()) tx = Tx.parse(f) txs.append(tx) try: tx.parse_unspents(f) except Exception as ex: pass continue except Exception: pass parts = arg.split("/") if len(parts) == 4: # spendable try: spendables.append(Spendable.from_text(arg)) continue except Exception: pass if len(parts) == 2 and is_address_valid( parts[0], allowable_netcodes=[args.network]): try: payables.append(parts) continue except ValueError: pass parser.error("can't parse %s" % arg) if args.fetch_spendables: warning_spendables = message_about_spendables_for_address_env() for address in args.fetch_spendables: spendables.extend(spendables_for_address(address)) for tx in txs: if tx.missing_unspents() and args.augment: if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_get_tx = message_about_get_tx_env() tx_db = get_tx_db() tx.unspents_from_db(tx_db, ignore_missing=True) txs_in = [] txs_out = [] unspents = [] # we use a clever trick here to keep each tx_in corresponding with its tx_out for tx in txs: smaller = min(len(tx.txs_in), len(tx.txs_out)) txs_in.extend(tx.txs_in[:smaller]) txs_out.extend(tx.txs_out[:smaller]) unspents.extend(tx.unspents[:smaller]) for tx in txs: smaller = min(len(tx.txs_in), len(tx.txs_out)) txs_in.extend(tx.txs_in[smaller:]) txs_out.extend(tx.txs_out[smaller:]) unspents.extend(tx.unspents[smaller:]) for spendable in spendables: txs_in.append(spendable.tx_in()) unspents.append(spendable) for address, coin_value in payables: script = standard_tx_out_script(address) txs_out.append(TxOut(coin_value, script)) lock_time = args.lock_time version = args.transaction_version # if no lock_time is explicitly set, inherit from the first tx or use default if lock_time is None: if txs: lock_time = txs[0].lock_time else: lock_time = DEFAULT_LOCK_TIME # if no version is explicitly set, inherit from the first tx or use default if version is None: if txs: version = txs[0].version else: version = DEFAULT_VERSION if args.remove_tx_in: s = set(args.remove_tx_in) txs_in = [tx_in for idx, tx_in in enumerate(txs_in) if idx not in s] if args.remove_tx_out: s = set(args.remove_tx_out) txs_out = [ tx_out for idx, tx_out in enumerate(txs_out) if idx not in s ] tx = Tx(txs_in=txs_in, txs_out=txs_out, lock_time=lock_time, version=version, unspents=unspents) fee = args.fee try: distribute_from_split_pool(tx, fee) except ValueError as ex: print("warning: %s" % ex.args[0], file=sys.stderr) unsigned_before = tx.bad_signature_count() if unsigned_before > 0 and key_iters: def wif_iter(iters): while len(iters) > 0: for idx, iter in enumerate(iters): try: wif = next(iter) yield wif except StopIteration: iters = iters[:idx] + iters[idx + 1:] break print("signing...", file=sys.stderr) sign_tx(tx, wif_iter(key_iters), p2sh_lookup=p2sh_lookup) unsigned_after = tx.bad_signature_count() if unsigned_after > 0 and key_iters: print("warning: %d TxIn items still unsigned" % unsigned_after, file=sys.stderr) if len(tx.txs_in) == 0: print("warning: transaction has no inputs", file=sys.stderr) if len(tx.txs_out) == 0: print("warning: transaction has no outputs", file=sys.stderr) include_unspents = (unsigned_after > 0) tx_as_hex = tx.as_hex(include_unspents=include_unspents) if args.output_file: f = args.output_file if f.name.endswith(".hex"): f.write(tx_as_hex.encode("utf8")) else: tx.stream(f) if include_unspents: tx.stream_unspents(f) f.close() elif args.show_unspents: for spendable in tx.tx_outs_as_spendable(): print(spendable.as_text()) else: if not tx.missing_unspents(): check_fees(tx) dump_tx(tx, args.network) if include_unspents: print( "including unspents in hex dump since transaction not fully signed" ) print(tx_as_hex) if args.cache: if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_get_tx = message_about_get_tx_env() tx_db = get_tx_db() tx_db.put(tx) if args.bitcoind_url: if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_get_tx = message_about_get_tx_env() tx_db = get_tx_db() validate_bitcoind(tx, tx_db, args.bitcoind_url) if tx.missing_unspents(): print("\n** can't validate transaction as source transactions missing", file=sys.stderr) else: try: if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_get_tx = message_about_get_tx_env() tx_db = get_tx_db() tx.validate_unspents(tx_db) print('all incoming transaction values validated') except BadSpendableError as ex: print("\n**** ERROR: FEES INCORRECTLY STATED: %s" % ex.args[0], file=sys.stderr) except Exception as ex: print( "\n*** can't validate source transactions as untampered: %s" % ex.args[0], file=sys.stderr) # print warnings for m in [warning_tx_cache, warning_get_tx, warning_spendables]: if m: print("warning: %s" % m, file=sys.stderr)
def address_for_pay_to_script(script, netcode=None): if netcode is None: netcode = get_current_netcode() address_prefix = pay_to_script_prefix_for_netcode(netcode) return encoding.hash160_sec_to_bitcoin_address(encoding.hash160(script), address_prefix=address_prefix)
def hash160hex(hexdata): return b2h(hash160(h2b(hexdata)))
def main(): parser = argparse.ArgumentParser( description="Manipulate bitcoin (or alt coin) transactions.", epilog=EPILOG) parser.add_argument('-t', "--transaction-version", type=int, help='Transaction version, either 1 (default) or 3 (not yet supported).') parser.add_argument('-l', "--lock-time", type=parse_locktime, help='Lock time; either a block' 'index, or a date/time (example: "2014-01-01T15:00:00"') parser.add_argument('-n', "--network", default="BTC", help='Define network code (M=Bitcoin mainnet, T=Bitcoin testnet).') parser.add_argument('-a', "--augment", action='store_true', help='augment tx by adding any missing spendable metadata by fetching' ' inputs from cache and/or web services') parser.add_argument('-s', "--verbose-signature", action='store_true', help='Display technical signature details.') parser.add_argument("-i", "--fetch-spendables", metavar="address", action="append", help='Add all unspent spendables for the given bitcoin address. This information' ' is fetched from web services.') parser.add_argument('-f', "--private-key-file", metavar="path-to-private-keys", action="append", help='file containing WIF or BIP0032 private keys. If file name ends with .gpg, ' '"gpg -d" will be invoked automatically. File is read one line at a time, and if ' 'the file contains only one WIF per line, it will also be scanned for a bitcoin ' 'address, and any addresses found will be assumed to be public keys for the given' ' private key.', type=argparse.FileType('r')) parser.add_argument('-g', "--gpg-argument", help='argument to pass to gpg (besides -d).', default='') parser.add_argument("--remove-tx-in", metavar="tx_in_index_to_delete", action="append", type=int, help='remove a tx_in') parser.add_argument("--remove-tx-out", metavar="tx_out_index_to_delete", action="append", type=int, help='remove a tx_out') parser.add_argument('-F', "--fee", help='fee, in satoshis, to pay on transaction, or ' '"standard" to auto-calculate. This is only useful if the "split pool" ' 'is used; otherwise, the fee is automatically set to the unclaimed funds.', default="standard", metavar="transaction-fee", type=parse_fee) parser.add_argument('-C', "--cache", help='force the resultant transaction into the transaction cache.' ' Mostly for testing.', action='store_true'), parser.add_argument('-u', "--show-unspents", action='store_true', help='show TxOut items for this transaction in Spendable form.') parser.add_argument('-b', "--bitcoind-url", help='URL to bitcoind instance to validate against (http://user:pass@host:port).') parser.add_argument('-o', "--output-file", metavar="path-to-output-file", type=argparse.FileType('wb'), help='file to write transaction to. This supresses most other output.') parser.add_argument('-d', "--disassemble", action='store_true', help='Disassemble scripts.') parser.add_argument("--trace", action='store_true', help='Trace scripts.') parser.add_argument('-p', "--pay-to-script", metavar="pay-to-script", action="append", help='a hex version of a script required for a pay-to-script input (a bitcoin address that starts with 3)') parser.add_argument('-P', "--pay-to-script-file", metavar="pay-to-script-file", nargs=1, type=argparse.FileType('r'), help='a file containing hex scripts (one per line) corresponding to pay-to-script inputs') parser.add_argument("argument", nargs="+", help='generic argument: can be a hex transaction id ' '(exactly 64 characters) to be fetched from cache or a web service;' ' a transaction as a hex string; a path name to a transaction to be loaded;' ' a spendable 4-tuple of the form tx_id/tx_out_idx/script_hex/satoshi_count ' 'to be added to TxIn list; an address/satoshi_count to be added to the TxOut ' 'list; an address to be added to the TxOut list and placed in the "split' ' pool".') args = parser.parse_args() # defaults txs = [] spendables = [] payables = [] key_iters = [] TX_ID_RE = re.compile(r"^[0-9a-fA-F]{64}$") # there are a few warnings we might optionally print out, but only if # they are relevant. We don't want to print them out multiple times, so we # collect them here and print them at the end if they ever kick in. warning_tx_cache = None warning_tx_for_tx_hash = None warning_spendables = None if args.private_key_file: wif_re = re.compile(r"[1-9a-km-zA-LMNP-Z]{51,111}") # address_re = re.compile(r"[1-9a-kmnp-zA-KMNP-Z]{27-31}") for f in args.private_key_file: if f.name.endswith(".gpg"): gpg_args = ["gpg", "-d"] if args.gpg_argument: gpg_args.extend(args.gpg_argument.split()) gpg_args.append(f.name) popen = subprocess.Popen(gpg_args, stdout=subprocess.PIPE) f = popen.stdout for line in f.readlines(): # decode if isinstance(line, bytes): line = line.decode("utf8") # look for WIFs possible_keys = wif_re.findall(line) def make_key(x): try: return Key.from_text(x) except Exception: return None keys = [make_key(x) for x in possible_keys] for key in keys: if key: key_iters.append((k.wif() for k in key.subkeys(""))) # if len(keys) == 1 and key.hierarchical_wallet() is None: # # we have exactly 1 WIF. Let's look for an address # potential_addresses = address_re.findall(line) # update p2sh_lookup p2sh_lookup = {} if args.pay_to_script: for p2s in args.pay_to_script: try: script = h2b(p2s) p2sh_lookup[hash160(script)] = script except Exception: print("warning: error parsing pay-to-script value %s" % p2s) if args.pay_to_script_file: hex_re = re.compile(r"[0-9a-fA-F]+") for f in args.pay_to_script_file: count = 0 for l in f: try: m = hex_re.search(l) if m: p2s = m.group(0) script = h2b(p2s) p2sh_lookup[hash160(script)] = script count += 1 except Exception: print("warning: error parsing pay-to-script file %s" % f.name) if count == 0: print("warning: no scripts found in %s" % f.name) # we create the tx_db lazily tx_db = None for arg in args.argument: # hex transaction id if TX_ID_RE.match(arg): if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_tx_for_tx_hash = message_about_tx_for_tx_hash_env(args.network) tx_db = get_tx_db(args.network) tx = tx_db.get(h2b_rev(arg)) if not tx: for m in [warning_tx_cache, warning_tx_for_tx_hash, warning_spendables]: if m: print("warning: %s" % m, file=sys.stderr) parser.error("can't find Tx with id %s" % arg) txs.append(tx) continue # hex transaction data try: tx = Tx.from_hex(arg) txs.append(tx) continue except Exception: pass is_valid = is_address_valid(arg, allowable_netcodes=[args.network]) if is_valid: payables.append((arg, 0)) continue try: key = Key.from_text(arg) # TODO: check network if key.wif() is None: payables.append((key.address(), 0)) continue # TODO: support paths to subkeys key_iters.append((k.wif() for k in key.subkeys(""))) continue except Exception: pass if os.path.exists(arg): try: with open(arg, "rb") as f: if f.name.endswith("hex"): f = io.BytesIO(codecs.getreader("hex_codec")(f).read()) tx = Tx.parse(f) txs.append(tx) try: tx.parse_unspents(f) except Exception as ex: pass continue except Exception: pass parts = arg.split("/") if len(parts) == 4: # spendable try: spendables.append(Spendable.from_text(arg)) continue except Exception: pass if len(parts) == 2 and is_address_valid(parts[0], allowable_netcodes=[args.network]): try: payables.append(parts) continue except ValueError: pass parser.error("can't parse %s" % arg) if args.fetch_spendables: warning_spendables = message_about_spendables_for_address_env(args.network) for address in args.fetch_spendables: spendables.extend(spendables_for_address(address)) for tx in txs: if tx.missing_unspents() and args.augment: if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_tx_for_tx_hash = message_about_tx_for_tx_hash_env(args.network) tx_db = get_tx_db(args.network) tx.unspents_from_db(tx_db, ignore_missing=True) txs_in = [] txs_out = [] unspents = [] # we use a clever trick here to keep each tx_in corresponding with its tx_out for tx in txs: smaller = min(len(tx.txs_in), len(tx.txs_out)) txs_in.extend(tx.txs_in[:smaller]) txs_out.extend(tx.txs_out[:smaller]) unspents.extend(tx.unspents[:smaller]) for tx in txs: smaller = min(len(tx.txs_in), len(tx.txs_out)) txs_in.extend(tx.txs_in[smaller:]) txs_out.extend(tx.txs_out[smaller:]) unspents.extend(tx.unspents[smaller:]) for spendable in spendables: txs_in.append(spendable.tx_in()) unspents.append(spendable) for address, coin_value in payables: script = standard_tx_out_script(address) txs_out.append(TxOut(coin_value, script)) lock_time = args.lock_time version = args.transaction_version # if no lock_time is explicitly set, inherit from the first tx or use default if lock_time is None: if txs: lock_time = txs[0].lock_time else: lock_time = DEFAULT_LOCK_TIME # if no version is explicitly set, inherit from the first tx or use default if version is None: if txs: version = txs[0].version else: version = DEFAULT_VERSION if args.remove_tx_in: s = set(args.remove_tx_in) txs_in = [tx_in for idx, tx_in in enumerate(txs_in) if idx not in s] if args.remove_tx_out: s = set(args.remove_tx_out) txs_out = [tx_out for idx, tx_out in enumerate(txs_out) if idx not in s] tx = Tx(txs_in=txs_in, txs_out=txs_out, lock_time=lock_time, version=version, unspents=unspents) fee = args.fee try: distribute_from_split_pool(tx, fee) except ValueError as ex: print("warning: %s" % ex.args[0], file=sys.stderr) unsigned_before = tx.bad_signature_count() unsigned_after = unsigned_before if unsigned_before > 0 and key_iters: def wif_iter(iters): while len(iters) > 0: for idx, iter in enumerate(iters): try: wif = next(iter) yield wif except StopIteration: iters = iters[:idx] + iters[idx+1:] break print("signing...", file=sys.stderr) sign_tx(tx, wif_iter(key_iters), p2sh_lookup=p2sh_lookup) unsigned_after = tx.bad_signature_count() if unsigned_after > 0: print("warning: %d TxIn items still unsigned" % unsigned_after, file=sys.stderr) if len(tx.txs_in) == 0: print("warning: transaction has no inputs", file=sys.stderr) if len(tx.txs_out) == 0: print("warning: transaction has no outputs", file=sys.stderr) include_unspents = (unsigned_after > 0) tx_as_hex = tx.as_hex(include_unspents=include_unspents) if args.output_file: f = args.output_file if f.name.endswith(".hex"): f.write(tx_as_hex.encode("utf8")) else: tx.stream(f) if include_unspents: tx.stream_unspents(f) f.close() elif args.show_unspents: for spendable in tx.tx_outs_as_spendable(): print(spendable.as_text()) else: if not tx.missing_unspents(): check_fees(tx) dump_tx(tx, args.network, args.verbose_signature, args.disassemble, args.trace) if include_unspents: print("including unspents in hex dump since transaction not fully signed") print(tx_as_hex) if args.cache: if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_tx_for_tx_hash = message_about_tx_for_tx_hash_env(args.network) tx_db = get_tx_db(args.network) tx_db.put(tx) if args.bitcoind_url: if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_tx_for_tx_hash = message_about_tx_for_tx_hash_env(args.network) tx_db = get_tx_db(args.network) validate_bitcoind(tx, tx_db, args.bitcoind_url) if tx.missing_unspents(): print("\n** can't validate transaction as source transactions missing", file=sys.stderr) else: try: if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_tx_for_tx_hash = message_about_tx_for_tx_hash_env(args.network) tx_db = get_tx_db(args.network) tx.validate_unspents(tx_db) print('all incoming transaction values validated') except BadSpendableError as ex: print("\n**** ERROR: FEES INCORRECTLY STATED: %s" % ex.args[0], file=sys.stderr) except Exception as ex: print("\n*** can't validate source transactions as untampered: %s" % ex.args[0], file=sys.stderr) # print warnings for m in [warning_tx_cache, warning_tx_for_tx_hash, warning_spendables]: if m: print("warning: %s" % m, file=sys.stderr)
def parse_context(args, parser): # defaults txs = [] spendables = [] payables = [] key_iters = [] TX_ID_RE = re.compile(r"^[0-9a-fA-F]{64}$") # there are a few warnings we might optionally print out, but only if # they are relevant. We don't want to print them out multiple times, so we # collect them here and print them at the end if they ever kick in. warning_tx_cache = None warning_tx_for_tx_hash = None warning_spendables = None if args.private_key_file: wif_re = re.compile(r"[1-9a-km-zA-LMNP-Z]{51,111}") # address_re = re.compile(r"[1-9a-kmnp-zA-KMNP-Z]{27-31}") for f in args.private_key_file: if f.name.endswith(".gpg"): gpg_args = ["gpg", "-d"] if args.gpg_argument: gpg_args.extend(args.gpg_argument.split()) gpg_args.append(f.name) popen = subprocess.Popen(gpg_args, stdout=subprocess.PIPE) f = popen.stdout for line in f.readlines(): # decode if isinstance(line, bytes): line = line.decode("utf8") # look for WIFs possible_keys = wif_re.findall(line) def make_key(x): try: return Key.from_text(x) except Exception: return None keys = [make_key(x) for x in possible_keys] for key in keys: if key: key_iters.append((k.wif() for k in key.subkeys(""))) # if len(keys) == 1 and key.hierarchical_wallet() is None: # # we have exactly 1 WIF. Let's look for an address # potential_addresses = address_re.findall(line) # update p2sh_lookup p2sh_lookup = {} if args.pay_to_script: for p2s in args.pay_to_script: try: script = h2b(p2s) p2sh_lookup[hash160(script)] = script except Exception: print("warning: error parsing pay-to-script value %s" % p2s) if args.pay_to_script_file: hex_re = re.compile(r"[0-9a-fA-F]+") for f in args.pay_to_script_file: count = 0 for l in f: try: m = hex_re.search(l) if m: p2s = m.group(0) script = h2b(p2s) p2sh_lookup[hash160(script)] = script count += 1 except Exception: print("warning: error parsing pay-to-script file %s" % f.name) if count == 0: print("warning: no scripts found in %s" % f.name) # we create the tx_db lazily tx_db = None for arg in args.argument: # hex transaction id if TX_ID_RE.match(arg): if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_tx_for_tx_hash = message_about_tx_for_tx_hash_env(args.network) tx_db = get_tx_db(args.network) tx = tx_db.get(h2b_rev(arg)) if not tx: for m in [warning_tx_cache, warning_tx_for_tx_hash, warning_spendables]: if m: print("warning: %s" % m, file=sys.stderr) parser.error("can't find Tx with id %s" % arg) txs.append(tx) continue # hex transaction data try: tx = Tx.from_hex(arg) txs.append(tx) continue except Exception: pass is_valid = is_address_valid(arg, allowable_netcodes=[args.network]) if is_valid: payables.append((arg, 0)) continue try: key = Key.from_text(arg) # TODO: check network if key.wif() is None: payables.append((key.address(), 0)) continue # TODO: support paths to subkeys key_iters.append((k.wif() for k in key.subkeys(""))) continue except Exception: pass if os.path.exists(arg): try: with open(arg, "rb") as f: if f.name.endswith("hex"): f = io.BytesIO(codecs.getreader("hex_codec")(f).read()) tx = Tx.parse(f) txs.append(tx) try: tx.parse_unspents(f) except Exception as ex: pass continue except Exception: pass parts = arg.split("/") if len(parts) == 4: # spendable try: spendables.append(Spendable.from_text(arg)) continue except Exception: pass if len(parts) == 2 and is_address_valid(parts[0], allowable_netcodes=[args.network]): try: payables.append(parts) continue except ValueError: pass parser.error("can't parse %s" % arg) if args.fetch_spendables: warning_spendables = message_about_spendables_for_address_env(args.network) for address in args.fetch_spendables: spendables.extend(spendables_for_address(address)) for tx in txs: if tx.missing_unspents() and args.augment: if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_tx_for_tx_hash = message_about_tx_for_tx_hash_env(args.network) tx_db = get_tx_db(args.network) tx.unspents_from_db(tx_db, ignore_missing=True) return (txs, spendables, payables, key_iters, p2sh_lookup, tx_db, warning_tx_cache, warning_tx_for_tx_hash, warning_spendables)
def hash_bin(data): """ripemd160(hashlib.sha256(data)""" return hash160(data)
def hash_hex(hexdata): """ripemd160(hashlib.sha256(hexdata)""" return binascii.hexlify(hash160(binascii.unhexlify(hexdata)))
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 do_test(blob, expected_hash): self.assertEqual(encoding.hash160(blob), expected_hash)