def test_build_spends(self): # first, here is the tx database TX_DB = {} # create a coinbase Tx where we know the public & private key exponent = wif_to_secret_exponent("5JMys7YfK72cRVTrbwkq5paxU7vgkMypB55KyXEtN5uSnjV7K8Y") compressed = False public_key_sec = public_pair_to_sec( ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, exponent), compressed=compressed ) the_coinbase_tx = Tx.coinbase_tx(public_key_sec, int(50 * 1e8), COINBASE_BYTES_FROM_80971) TX_DB[the_coinbase_tx.hash()] = the_coinbase_tx # now create a Tx that spends the coinbase compressed = False exponent_2 = int("137f3276686959c82b454eea6eefc9ab1b9e45bd4636fb9320262e114e321da1", 16) bitcoin_address_2 = public_pair_to_bitcoin_address( ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, exponent_2), compressed=compressed ) self.assertEqual("12WivmEn8AUth6x6U8HuJuXHaJzDw3gHNZ", bitcoin_address_2) coins_from = [(the_coinbase_tx.hash(), 0, the_coinbase_tx.txs_out[0])] coins_to = [(int(50 * 1e8), bitcoin_address_2)] unsigned_coinbase_spend_tx = standard_tx(coins_from, coins_to) solver = build_hash160_lookup([exponent]) coinbase_spend_tx = unsigned_coinbase_spend_tx.sign(solver) # now check that it validates self.assertEqual(coinbase_spend_tx.bad_signature_count(), 0) TX_DB[coinbase_spend_tx.hash()] = coinbase_spend_tx ## now try to respend from priv_key_2 to priv_key_3 compressed = True exponent_3 = int("f8d39b8ecd0e1b6fee5a340519f239097569d7a403a50bb14fb2f04eff8db0ff", 16) bitcoin_address_3 = public_pair_to_bitcoin_address( ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, exponent_3), compressed=compressed ) self.assertEqual("13zzEHPCH2WUZJzANymow3ZrxcZ8iFBrY5", bitcoin_address_3) coins_from = [(coinbase_spend_tx.hash(), 0, coinbase_spend_tx.txs_out[0])] unsigned_spend_tx = standard_tx(coins_from, [(int(50 * 1e8), bitcoin_address_3)]) solver.update(build_hash160_lookup([exponent_2])) spend_tx = unsigned_spend_tx.sign(solver) # now check that it validates self.assertEqual(spend_tx.bad_signature_count(), 0)
def do_test(exp_hex, wif, c_wif, public_pair_sec, c_public_pair_sec, address_b58, c_address_b58): secret_exponent = int(exp_hex, 16) sec = binascii.unhexlify(public_pair_sec) c_sec = binascii.unhexlify(c_public_pair_sec) self.assertEqual( secret_exponent_to_wif(secret_exponent, compressed=False), wif) self.assertEqual( secret_exponent_to_wif(secret_exponent, compressed=True), c_wif) exponent, compressed = wif_to_tuple_of_secret_exponent_compressed( wif) self.assertEqual(exponent, secret_exponent) self.assertFalse(compressed) exponent, compressed = wif_to_tuple_of_secret_exponent_compressed( c_wif) self.assertEqual(exponent, secret_exponent) self.assertTrue(compressed) public_pair = public_pair_for_secret_exponent( generator_secp256k1, secret_exponent) pk_public_pair = public_pair_from_sec(sec) compressed = is_sec_compressed(sec) self.assertEqual(pk_public_pair, public_pair) self.assertFalse(is_sec_compressed(sec)) self.assertEqual( public_pair_to_sec(pk_public_pair, compressed=False), sec) pk_public_pair = public_pair_from_sec(c_sec) compressed = is_sec_compressed(c_sec) self.assertEqual(pk_public_pair, public_pair) self.assertTrue(compressed) self.assertEqual( public_pair_to_sec(pk_public_pair, compressed=True), c_sec) bca = public_pair_to_bitcoin_address(pk_public_pair, compressed=True) self.assertEqual(bca, c_address_b58) self.assertEqual( bitcoin_address_to_ripemd160_sha_sec(c_address_b58), public_pair_to_ripemd160_sha_sec(pk_public_pair, compressed=True)) bca = public_pair_to_bitcoin_address(pk_public_pair, compressed=False) self.assertEqual(bca, address_b58) self.assertEqual( bitcoin_address_to_ripemd160_sha_sec(address_b58), public_pair_to_ripemd160_sha_sec(pk_public_pair, compressed=False))
def main(): parser = argparse.ArgumentParser(description="Bitcoin utilities. WARNING: obsolete. Use ku instead.") parser.add_argument('-a', "--address", help='show as Bitcoin address', action='store_true') parser.add_argument('-1', "--hash160", help='show as hash 160', action='store_true') parser.add_argument('-v', "--verbose", help='dump all information available', action='store_true') parser.add_argument('-w', "--wif", help='show as Bitcoin WIF', action='store_true') parser.add_argument('-n', "--uncompressed", help='show in uncompressed form', action='store_true') parser.add_argument('item', help='a WIF, secret exponent, X/Y public pair, SEC (as hex), hash160 (as hex), Bitcoin address', nargs="+") args = parser.parse_args() for c in args.item: # figure out what it is: # - secret exponent # - WIF # - X/Y public key (base 10 or hex) # - sec # - hash160 # - Bitcoin address secret_exponent = parse_as_private_key(c) if secret_exponent: public_pair = ecdsa.public_pair_for_secret_exponent(secp256k1.generator_secp256k1, secret_exponent) print("secret exponent: %d" % secret_exponent) print(" hex: %x" % secret_exponent) print("WIF: %s" % encoding.secret_exponent_to_wif(secret_exponent, compressed=True)) print(" uncompressed: %s" % encoding.secret_exponent_to_wif(secret_exponent, compressed=False)) else: public_pair = parse_as_public_pair(c) if public_pair: bitcoin_address_uncompressed = encoding.public_pair_to_bitcoin_address(public_pair, compressed=False) bitcoin_address_compressed = encoding.public_pair_to_bitcoin_address(public_pair, compressed=True) print("public pair x: %d" % public_pair[0]) print("public pair y: %d" % public_pair[1]) print(" x as hex: %x" % public_pair[0]) print(" y as hex: %x" % public_pair[1]) print("y parity: %s" % "odd" if (public_pair[1] & 1) else "even") print("key pair as sec: %s" % b2h(encoding.public_pair_to_sec(public_pair, compressed=True))) s = b2h(encoding.public_pair_to_sec(public_pair, compressed=False)) print(" uncompressed: %s\\\n %s" % (s[:66], s[66:])) hash160 = encoding.public_pair_to_hash160_sec(public_pair, compressed=True) hash160_unc = encoding.public_pair_to_hash160_sec(public_pair, compressed=False) myeccpoint = encoding.public_pair_to_sec(public_pair, compressed=True) myhash = encoding.ripemd160( myeccpoint ).digest( ) print("BTSX PubKey: %s" % BTS_ADDRESS_PREFIX + encoding.b2a_base58(myeccpoint + myhash[ :4 ])) else: hash160 = parse_as_address(c) hash160_unc = None if not hash160: sys.stderr.write("can't decode input %s\n" % c) sys.exit(1) print("hash160: %s" % b2h(hash160)) if hash160_unc: print(" uncompressed: %s" % b2h(hash160_unc)) print("Bitcoin address: %s" % encoding.hash160_sec_to_bitcoin_address(hash160)) if hash160_unc: print(" uncompressed: %s" % encoding.hash160_sec_to_bitcoin_address(hash160_unc))
def do_test(exp_hex, wif, c_wif, public_pair_sec, c_public_pair_sec, address_b58, c_address_b58): secret_exponent = int(exp_hex, 16) sec = h2b(public_pair_sec) c_sec = h2b(c_public_pair_sec) self.assertEqual( secret_exponent_to_wif(secret_exponent, compressed=False), wif) self.assertEqual( secret_exponent_to_wif(secret_exponent, compressed=True), c_wif) exponent, compressed = wif_to_tuple_of_secret_exponent_compressed( wif) self.assertEqual(exponent, secret_exponent) self.assertFalse(compressed) exponent, compressed = wif_to_tuple_of_secret_exponent_compressed( c_wif) self.assertEqual(exponent, secret_exponent) self.assertTrue(compressed) public_pair = secret_exponent * secp256k1_generator pk_public_pair = sec_to_public_pair(sec) compressed = is_sec_compressed(sec) self.assertEqual(pk_public_pair, public_pair) self.assertFalse(is_sec_compressed(sec)) self.assertEqual( public_pair_to_sec(pk_public_pair, compressed=False), sec) pk_public_pair = sec_to_public_pair(c_sec) compressed = is_sec_compressed(c_sec) self.assertEqual(pk_public_pair, public_pair) self.assertTrue(compressed) self.assertEqual( public_pair_to_sec(pk_public_pair, compressed=True), c_sec) bca = public_pair_to_bitcoin_address(pk_public_pair, compressed=True) self.assertEqual(bca, c_address_b58) self.assertEqual( bitcoin_address_to_hash160_sec(c_address_b58), public_pair_to_hash160_sec(pk_public_pair, compressed=True)) bca = public_pair_to_bitcoin_address(pk_public_pair, compressed=False) self.assertEqual(bca, address_b58) self.assertEqual( bitcoin_address_to_hash160_sec(address_b58), public_pair_to_hash160_sec(pk_public_pair, compressed=False))
def main(): parser = argparse.ArgumentParser(description="Bitcoin utilities.") parser.add_argument('-a', "--address", help='show as Bitcoin address', action='store_true') parser.add_argument('-1', "--hash160", help='show as hash 160', action='store_true') parser.add_argument('-v', "--verbose", help='dump all information available', action='store_true') parser.add_argument('-w', "--wif", help='show as Bitcoin WIF', action='store_true') parser.add_argument('-n', "--uncompressed", help='show in uncompressed form', action='store_true') parser.add_argument('item', help='a WIF, secret exponent, X/Y public pair, SEC (as hex), hash160 (as hex), Bitcoin address', nargs="+") args = parser.parse_args() for c in args.item: # figure out what it is: # - secret exponent # - WIF # - X/Y public key (base 10 or hex) # - sec # - hash160 # - Bitcoin address secret_exponent = parse_as_private_key(c) if secret_exponent: public_pair = ecdsa.public_pair_for_secret_exponent(secp256k1.generator_secp256k1, secret_exponent) print("secret exponent: %d" % secret_exponent) print(" hex: %x" % secret_exponent) print("WIF: %s" % encoding.secret_exponent_to_wif(secret_exponent, compressed=True)) print(" uncompressed: %s" % encoding.secret_exponent_to_wif(secret_exponent, compressed=False)) else: public_pair = parse_as_public_pair(c) if public_pair: bitcoin_address_uncompressed = encoding.public_pair_to_bitcoin_address(public_pair, compressed=False) bitcoin_address_compressed = encoding.public_pair_to_bitcoin_address(public_pair, compressed=True) print("public pair x: %d" % public_pair[0]) print("public pair y: %d" % public_pair[1]) print(" x as hex: %x" % public_pair[0]) print(" y as hex: %x" % public_pair[1]) print("y parity: %s" % "odd" if (public_pair[1] & 1) else "even") print("key pair as sec: %s" % b2h(encoding.public_pair_to_sec(public_pair, compressed=True))) s = b2h(encoding.public_pair_to_sec(public_pair, compressed=False)) print(" uncompressed: %s\\\n %s" % (s[:66], s[66:])) hash160 = encoding.public_pair_to_hash160_sec(public_pair, compressed=True) hash160_unc = encoding.public_pair_to_hash160_sec(public_pair, compressed=False) else: hash160 = parse_as_address(c) hash160_unc = None if not hash160: sys.stderr.write("can't decode input %s\n" % c) sys.exit(1) print("hash160: %s" % b2h(hash160)) if hash160_unc: print(" uncompressed: %s" % b2h(hash160_unc)) print("Bitcoin address: %s" % encoding.hash160_sec_to_bitcoin_address(hash160)) if hash160_unc: print(" uncompressed: %s" % encoding.hash160_sec_to_bitcoin_address(hash160_unc))
def test_build_spends(self): # create a coinbase Tx where we know the public & private key exponent = wif_to_secret_exponent( "5JMys7YfK72cRVTrbwkq5paxU7vgkMypB55KyXEtN5uSnjV7K8Y") compressed = False public_key_sec = public_pair_to_sec( ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, exponent), compressed=compressed) the_coinbase_tx = Tx.coinbase_tx(public_key_sec, int(50 * 1e8), COINBASE_BYTES_FROM_80971) # now create a Tx that spends the coinbase compressed = False exponent_2 = int( "137f3276686959c82b454eea6eefc9ab1b9e45bd4636fb9320262e114e321da1", 16) bitcoin_address_2 = public_pair_to_bitcoin_address( ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, exponent_2), compressed=compressed) self.assertEqual("12WivmEn8AUth6x6U8HuJuXHaJzDw3gHNZ", bitcoin_address_2) TX_DB = dict((tx.hash(), tx) for tx in [the_coinbase_tx]) coins_from = [(the_coinbase_tx.hash(), 0)] coins_to = [(int(50 * 1e8), bitcoin_address_2)] coinbase_spend_tx = Tx.standard_tx(coins_from, coins_to, TX_DB, [exponent]) coinbase_spend_tx.validate(TX_DB) ## now try to respend from priv_key_2 to priv_key_3 compressed = True exponent_3 = int( "f8d39b8ecd0e1b6fee5a340519f239097569d7a403a50bb14fb2f04eff8db0ff", 16) bitcoin_address_3 = public_pair_to_bitcoin_address( ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, exponent_3), compressed=compressed) self.assertEqual("13zzEHPCH2WUZJzANymow3ZrxcZ8iFBrY5", bitcoin_address_3) TX_DB = dict((tx.hash(), tx) for tx in [coinbase_spend_tx]) spend_tx = Tx.standard_tx([(coinbase_spend_tx.hash(), 0)], [(int(50 * 1e8), bitcoin_address_3)], TX_DB, [exponent_2]) spend_tx.validate(TX_DB)
def test_signature_hash(self): compressed = False exponent_2 = int( "137f3276686959c82b454eea6eefc9ab1b9e45bd4636fb9320262e114e321da1", 16) bitcoin_address_2 = public_pair_to_bitcoin_address( ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, exponent_2), compressed=compressed) exponent = wif_to_secret_exponent( "5JMys7YfK72cRVTrbwkq5paxU7vgkMypB55KyXEtN5uSnjV7K8Y") public_key_sec = public_pair_to_sec( ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, exponent), compressed=compressed) the_coinbase_tx = Tx.coinbase_tx(public_key_sec, int(50 * 1e8), COINBASE_BYTES_FROM_80971) coins_from = [(the_coinbase_tx.hash(), 0, the_coinbase_tx.txs_out[0])] coins_to = [(int(50 * 1e8), bitcoin_address_2)] unsigned_coinbase_spend_tx = standard_tx(coins_from, coins_to) tx_out_script_to_check = the_coinbase_tx.txs_out[0].script idx = 0 actual_hash = unsigned_coinbase_spend_tx.signature_hash( tx_out_script_to_check, idx, hash_type=SIGHASH_ALL) self.assertEqual( actual_hash, 29819170155392455064899446505816569230970401928540834591675173488544269166940 )
def private_key_to_public_key(private_key_wif): secret_exponent, compressed = wif_to_tuple_of_secret_exponent_compressed( private_key_wif, is_test=config.TESTNET) public_pair = public_pair_for_secret_exponent(generator_secp256k1, secret_exponent) public_key = public_pair_to_sec(public_pair, compressed=compressed) public_key_hex = binascii.hexlify(public_key).decode('utf-8') return public_key_hex
def sec(self, use_uncompressed=None): """ Return the SEC representation of this key, if available. If use_uncompressed is not set, the preferred representation is returned. """ public_pair = self.public_pair() if public_pair is None: return None return public_pair_to_sec(public_pair, compressed=not self._use_uncompressed(use_uncompressed))
def test_build_spends(self): # first, here is the tx database TX_DB = {} def tx_out_for_hash_index_f(tx_hash, tx_out_idx): tx = TX_DB.get(tx_hash) return tx.txs_out[tx_out_idx] # create a coinbase Tx where we know the public & private key exponent = wif_to_secret_exponent("5JMys7YfK72cRVTrbwkq5paxU7vgkMypB55KyXEtN5uSnjV7K8Y") compressed = False public_key_sec = public_pair_to_sec(ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, exponent), compressed=compressed) the_coinbase_tx = Tx.coinbase_tx(public_key_sec, int(50 * 1e8), COINBASE_BYTES_FROM_80971) TX_DB[the_coinbase_tx.hash()] = the_coinbase_tx # now create a Tx that spends the coinbase compressed = False exponent_2 = int("137f3276686959c82b454eea6eefc9ab1b9e45bd4636fb9320262e114e321da1", 16) bitcoin_address_2 = public_pair_to_bitcoin_address( ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, exponent_2), compressed=compressed) self.assertEqual("12WivmEn8AUth6x6U8HuJuXHaJzDw3gHNZ", bitcoin_address_2) coins_from = [(the_coinbase_tx.hash(), 0, the_coinbase_tx.txs_out[0])] coins_to = [(int(50 * 1e8), bitcoin_address_2)] unsigned_coinbase_spend_tx = UnsignedTx.standard_tx(coins_from, coins_to) solver = SecretExponentSolver([exponent]) coinbase_spend_tx = unsigned_coinbase_spend_tx.sign(solver) # now check that it validates coinbase_spend_tx.validate(tx_out_for_hash_index_f) TX_DB[coinbase_spend_tx.hash()] = coinbase_spend_tx ## now try to respend from priv_key_2 to priv_key_3 compressed = True exponent_3 = int("f8d39b8ecd0e1b6fee5a340519f239097569d7a403a50bb14fb2f04eff8db0ff", 16) bitcoin_address_3 = public_pair_to_bitcoin_address( ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, exponent_3), compressed=compressed) self.assertEqual("13zzEHPCH2WUZJzANymow3ZrxcZ8iFBrY5", bitcoin_address_3) unsigned_spend_tx = UnsignedTx.standard_tx([(coinbase_spend_tx.hash(), 0, coinbase_spend_tx.txs_out[0])], [(int(50 * 1e8), bitcoin_address_3)]) solver.add_secret_exponents([exponent_2]) spend_tx = unsigned_spend_tx.sign(solver) # now check that it validates spend_tx.validate(tx_out_for_hash_index_f)
def private_key_to_public_key (private_key_wif): try: secret_exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(private_key_wif, [wif_prefix(is_test=config.TESTNET)]) except EncodingError: raise exceptions.AltcoinSupportError('pycoin: unsupported WIF prefix') public_pair = public_pair_for_secret_exponent(generator_secp256k1, secret_exponent) public_key = public_pair_to_sec(public_pair, compressed=compressed) public_key_hex = binascii.hexlify(public_key).decode('utf-8') return public_key_hex
def compress_pubkey(uncompressed_pubkey): """ Convert uncompressed public key to compressed public key. Args: pubkey (str): Hex encoded 65Byte uncompressed public key Return: str: Hex encoded 33Byte compressed public key """ public_pair = encoding.sec_to_public_pair(h2b(uncompressed_pubkey)) return b2h(encoding.public_pair_to_sec(public_pair, compressed=True))
def uncompress_pubkey(pubkey): """ Convert compressed public key to uncompressed public key. Args: pubkey (str): Hex encoded 33Byte compressed public key Return: str: Hex encoded uncompressed 65byte public key (4 + x + y). """ public_pair = encoding.sec_to_public_pair(h2b(pubkey)) return b2h(encoding.public_pair_to_sec(public_pair, compressed=False))
def do_test(as_public_pair, as_sec, is_compressed, as_hash160_sec, as_bitcoin_address): self.assertEqual(encoding.sec_to_public_pair(as_sec), as_public_pair) self.assertEqual(encoding.public_pair_to_sec(as_public_pair, compressed=is_compressed), as_sec) self.assertEqual(encoding.is_sec_compressed(as_sec), is_compressed) self.assertEqual(encoding.public_pair_to_hash160_sec(as_public_pair, compressed=is_compressed), as_hash160_sec) self.assertEqual(encoding.hash160_sec_to_bitcoin_address(as_hash160_sec), as_bitcoin_address) self.assertEqual(encoding.public_pair_to_bitcoin_address(as_public_pair, compressed=is_compressed), as_bitcoin_address) self.assertTrue(encoding.is_valid_bitcoin_address(as_bitcoin_address)) bad_address = as_bitcoin_address[:17] + chr(ord(as_bitcoin_address[17]) + 1) + as_bitcoin_address[18:] self.assertFalse(encoding.is_valid_bitcoin_address(bad_address))
def dumppubkey(self, address): if is_valid_bitcoin_address(address)==False: return Exception("Invalid address %s" % address) try: secret_exponent = self.getsecretexponent(address) public_pair = ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, secret_exponent) pubkey = public_pair_to_sec(public_pair, compressed=True) pubkey = b2h(pubkey) except: raise Exception("Unknown address: %s" % address) return pubkey
def do_test(exp_hex, wif, c_wif, public_pair_sec, c_public_pair_sec, address_b58, c_address_b58): secret_exponent = int(exp_hex, 16) sec = h2b(public_pair_sec) c_sec = h2b(c_public_pair_sec) self.assertEqual(secret_exponent_to_wif(secret_exponent, compressed=False), wif) self.assertEqual(secret_exponent_to_wif(secret_exponent, compressed=True), c_wif) exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(wif) self.assertEqual(exponent, secret_exponent) self.assertFalse(compressed) exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(c_wif) self.assertEqual(exponent, secret_exponent) self.assertTrue(compressed) public_pair = secret_exponent * secp256k1_generator pk_public_pair = sec_to_public_pair(sec) compressed = is_sec_compressed(sec) self.assertEqual(pk_public_pair, public_pair) self.assertFalse(is_sec_compressed(sec)) self.assertEqual(public_pair_to_sec(pk_public_pair, compressed=False), sec) pk_public_pair = sec_to_public_pair(c_sec) compressed = is_sec_compressed(c_sec) self.assertEqual(pk_public_pair, public_pair) self.assertTrue(compressed) self.assertEqual(public_pair_to_sec(pk_public_pair, compressed=True), c_sec) bca = public_pair_to_bitcoin_address(pk_public_pair, compressed=True) self.assertEqual(bca, c_address_b58) self.assertEqual(bitcoin_address_to_hash160_sec(c_address_b58), public_pair_to_hash160_sec(pk_public_pair, compressed=True)) bca = public_pair_to_bitcoin_address(pk_public_pair, compressed=False) self.assertEqual(bca, address_b58) self.assertEqual(bitcoin_address_to_hash160_sec(address_b58), public_pair_to_hash160_sec(pk_public_pair, compressed=False))
def dumppubkey(self, address): if is_valid_bitcoin_address(address) == False: return Exception("Invalid address %s" % address) try: secret_exponent = self.getsecretexponent(address) public_pair = ecdsa.public_pair_for_secret_exponent( ecdsa.generator_secp256k1, secret_exponent) pubkey = public_pair_to_sec(public_pair, compressed=True) pubkey = b2h(pubkey) except: raise Exception("Unknown address: %s" % address) return pubkey
def _report_addresses(irange): ms = _get_wallets() n = get_n() for i in irange: try: ms_i = [hexlify(public_pair_to_sec(m.subkey(i).public_pair)) for m in ms] except pycoin.wallet.InvalidKeyGeneratedError: continue info = conf.bitcoind.createmultisig(n, ms_i) yield i, info["address"]
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 do_test(exp_hex, wif, c_wif, public_pair_sec, c_public_pair_sec, address_b58, c_address_b58): secret_exponent = int(exp_hex, 16) sec = binascii.unhexlify(public_pair_sec) c_sec = binascii.unhexlify(c_public_pair_sec) self.assertEqual(secret_exponent_to_wif(secret_exponent, compressed=False), wif) self.assertEqual(secret_exponent_to_wif(secret_exponent, compressed=True), c_wif) exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(wif) self.assertEqual(exponent, secret_exponent) self.assertFalse(compressed) exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(c_wif) self.assertEqual(exponent, secret_exponent) self.assertTrue(compressed) public_pair = public_pair_for_secret_exponent(generator_secp256k1, secret_exponent) pk_public_pair = public_pair_from_sec(sec) compressed = is_sec_compressed(sec) self.assertEqual(pk_public_pair, public_pair) self.assertFalse(is_sec_compressed(sec)) self.assertEqual(public_pair_to_sec(pk_public_pair, compressed=False), sec) pk_public_pair = public_pair_from_sec(c_sec) compressed = is_sec_compressed(c_sec) self.assertEqual(pk_public_pair, public_pair) self.assertTrue(compressed) self.assertEqual(public_pair_to_sec(pk_public_pair, compressed=True), c_sec) bca = public_pair_to_bitcoin_address(pk_public_pair, compressed=True) self.assertEqual(bca, c_address_b58) self.assertEqual(bitcoin_address_to_ripemd160_sha_sec(c_address_b58), public_pair_to_ripemd160_sha_sec(pk_public_pair, compressed=True)) bca = public_pair_to_bitcoin_address(pk_public_pair, compressed=False) self.assertEqual(bca, address_b58) self.assertEqual(bitcoin_address_to_ripemd160_sha_sec(address_b58), public_pair_to_ripemd160_sha_sec(pk_public_pair, compressed=False))
def private_key_to_public_key(private_key_wif): if config.TESTNET: allowable_wif_prefixes = [config.PRIVATEKEY_VERSION_TESTNET] else: allowable_wif_prefixes = [config.PRIVATEKEY_VERSION_MAINNET] try: secret_exponent, compressed = wif_to_tuple_of_secret_exponent_compressed( private_key_wif, allowable_wif_prefixes=allowable_wif_prefixes) except EncodingError: raise AltcoinSupportError('pycoin: unsupported WIF prefix') public_pair = public_pair_for_secret_exponent(generator_secp256k1, secret_exponent) public_key = public_pair_to_sec(public_pair, compressed=compressed) public_key_hex = binascii.hexlify(public_key).decode('utf-8') return public_key_hex
def private_key_to_public_key (private_key_wif): if config.TESTNET: allowable_wif_prefixes = [config.PRIVATEKEY_VERSION_TESTNET] else: allowable_wif_prefixes = [config.PRIVATEKEY_VERSION_MAINNET] try: secret_exponent, compressed = wif_to_tuple_of_secret_exponent_compressed( private_key_wif, allowable_wif_prefixes=allowable_wif_prefixes) except EncodingError: raise AltcoinSupportError('pycoin: unsupported WIF prefix') public_pair = public_pair_for_secret_exponent(generator_secp256k1, secret_exponent) public_key = public_pair_to_sec(public_pair, compressed=compressed) public_key_hex = binascii.hexlify(public_key).decode('utf-8') return public_key_hex
def create_coinbase_tx(parser): args = parser.parse_args() try: if len(args.txinfo) != 1: parser.error("coinbase transactions need exactly one output parameter (wif/BTC count)") wif, btc_amount = args.txinfo[0].split("/") satoshi_amount = btc_to_satoshi(btc_amount) secret_exponent, compressed = encoding.wif_to_tuple_of_secret_exponent_compressed(wif) public_pair = ecdsa.public_pair_for_secret_exponent(ecdsa.secp256k1.generator_secp256k1, secret_exponent) public_key_sec = encoding.public_pair_to_sec(public_pair, compressed=compressed) coinbase_tx = Tx.coinbase_tx(public_key_sec, satoshi_amount) return coinbase_tx except Exception: parser.error("coinbase transactions need exactly one output parameter (wif/BTC count)")
def get_auth_data(self, moniker): with self.lock: coin = self._wallet.make_coin_query({ 'asset': self.get_asset_definition(moniker), 'spent': False, }).get_result()[0] pubKey = public_pair_to_sec(coin.address_rec.publicPoint.pair(), compressed=False) return { 'color_set': clubAsset['color_set'][0], 'txhash': coin.txhash, 'outindex': coin.outindex, 'pubkey': pubKey.encode('hex'), 'privkey': coin.address_rec.rawPrivKey, 'address_rec': coin.address_rec, }
def test_signature_hash(self): compressed = False exponent_2 = int("137f3276686959c82b454eea6eefc9ab1b9e45bd4636fb9320262e114e321da1", 16) bitcoin_address_2 = public_pair_to_bitcoin_address(exponent_2 * secp256k1_generator, compressed=compressed) exponent = wif_to_secret_exponent("5JMys7YfK72cRVTrbwkq5paxU7vgkMypB55KyXEtN5uSnjV7K8Y") public_key_sec = public_pair_to_sec(exponent * secp256k1_generator, compressed=compressed) the_coinbase_tx = Tx.coinbase_tx(public_key_sec, int(50 * 1e8), COINBASE_BYTES_FROM_80971) coins_from = [(the_coinbase_tx.hash(), 0, the_coinbase_tx.txs_out[0])] coins_to = [(int(50 * 1e8), bitcoin_address_2)] unsigned_coinbase_spend_tx = standard_tx(coins_from, coins_to) tx_out_script_to_check = the_coinbase_tx.txs_out[0].script idx = 0 actual_hash = unsigned_coinbase_spend_tx.signature_hash(tx_out_script_to_check, idx, hash_type=SIGHASH_ALL) self.assertEqual(actual_hash, 29819170155392455064899446505816569230970401928540834591675173488544269166940)
def test_build_spends(self): # create a coinbase Tx where we know the public & private key exponent = wif_to_secret_exponent("5JMys7YfK72cRVTrbwkq5paxU7vgkMypB55KyXEtN5uSnjV7K8Y") compressed = False public_key_sec = public_pair_to_sec(ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, exponent), compressed=compressed) the_coinbase_tx = Tx.coinbase_tx(public_key_sec, int(50 * 1e8), COINBASE_BYTES_FROM_80971) # now create a Tx that spends the coinbase compressed = False exponent_2 = int("137f3276686959c82b454eea6eefc9ab1b9e45bd4636fb9320262e114e321da1", 16) bitcoin_address_2 = public_pair_to_bitcoin_address( ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, exponent_2), compressed=compressed) self.assertEqual("12WivmEn8AUth6x6U8HuJuXHaJzDw3gHNZ", bitcoin_address_2) TX_DB = dict((tx.hash(), tx) for tx in [the_coinbase_tx]) coins_from = [(the_coinbase_tx.hash(), 0)] coins_to = [(int(50 * 1e8), bitcoin_address_2)] coinbase_spend_tx = Tx.standard_tx(coins_from, coins_to, TX_DB, [exponent]) coinbase_spend_tx.validate(TX_DB) ## now try to respend from priv_key_2 to priv_key_3 compressed = True exponent_3 = int("f8d39b8ecd0e1b6fee5a340519f239097569d7a403a50bb14fb2f04eff8db0ff", 16) bitcoin_address_3 = public_pair_to_bitcoin_address( ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, exponent_3), compressed=compressed) self.assertEqual("13zzEHPCH2WUZJzANymow3ZrxcZ8iFBrY5", bitcoin_address_3) TX_DB = dict((tx.hash(), tx) for tx in [coinbase_spend_tx]) spend_tx = Tx.standard_tx([(coinbase_spend_tx.hash(), 0)], [(int(50 * 1e8), bitcoin_address_3)], TX_DB, [exponent_2]) spend_tx.validate(TX_DB)
def __call__(self, tx_out_script, signature_hash, signature_type): """Figure out how to create a signature for the incoming transaction, and sign it. tx_out_script: the tx_out script that needs to be "solved" signature_hash: the bignum hash value of the new transaction reassigning the coins signature_type: always SIGHASH_ALL (1) """ if signature_hash == 0: raise SolvingError("signature_hash can't be 0") tx_script = TxScript(tx_out_script) opcode_value_list = tx_script.match_script_to_templates() ba = bytearray() compressed = True for opcode, v in opcode_value_list: if opcode == opcodes.OP_PUBKEY: public_pair = sec_to_public_pair(v) bitcoin_address = public_pair_to_bitcoin_address( public_pair, compressed=compressed) elif opcode == opcodes.OP_PUBKEYHASH: bitcoin_address = hash160_sec_to_bitcoin_address(v) else: raise SolvingError("can't determine how to sign this script") secret_exponent = self.wallet.getsecretexponent(bitcoin_address) r, s = ecdsa.sign(ecdsa.generator_secp256k1, secret_exponent, signature_hash) sig = der.sigencode_der(r, s) + bytes_from_int(signature_type) ba += tools.compile(binascii.hexlify(sig).decode("utf8")) if opcode == opcodes.OP_PUBKEYHASH: public_pair = ecdsa.public_pair_for_secret_exponent( ecdsa.generator_secp256k1, secret_exponent) ba += tools.compile( binascii.hexlify( public_pair_to_sec( public_pair, compressed=compressed)).decode("utf8")) return bytes(ba)
def __call__(self, tx_out_script, signature_hash, signature_type): """Figure out how to create a signature for the incoming transaction, and sign it. tx_out_script: the tx_out script that needs to be "solved" signature_hash: the bignum hash value of the new transaction reassigning the coins signature_type: always SIGHASH_ALL (1) """ if signature_hash == 0: raise SolvingError("signature_hash can't be 0") tx_script = TxScript(tx_out_script) opcode_value_list = tx_script.match_script_to_templates() ba = bytearray() compressed = True for opcode, v in opcode_value_list: if opcode == opcodes.OP_PUBKEY: public_pair = sec_to_public_pair(v) bitcoin_address = public_pair_to_bitcoin_address(public_pair, compressed=compressed) elif opcode == opcodes.OP_PUBKEYHASH: bitcoin_address = hash160_sec_to_bitcoin_address(v) else: raise SolvingError("can't determine how to sign this script") secret_exponent = self.wallet.getsecretexponent(bitcoin_address) r, s = ecdsa.sign(ecdsa.generator_secp256k1, secret_exponent, signature_hash) sig = der.sigencode_der(r, s) + bytes_from_int(signature_type) ba += tools.compile(binascii.hexlify(sig).decode("utf8")) if opcode == opcodes.OP_PUBKEYHASH: public_pair = ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, secret_exponent) ba += tools.compile( binascii.hexlify(public_pair_to_sec(public_pair, compressed=compressed)).decode("utf8") ) return bytes(ba)
def test_sign(self, keynums_satoshi, out_addr, out_satoshi, change_keynum, change_satoshi, prevtx_keynums, prevtx_outputs, prevtx_inputs): """ Performs a tx signing test, comparing Polly's signed tx against the reference wallet. Basic tx signing parameters: keynums_satoshi - list of tuples (keynum, satoshis) with key indices and their unspent value to use as tx inputs. Funding above out_satoshi + change_satoshi will be fees. out_addr - output address in bitcoin address format. out_satoshi - output amount in satoshis. change_keynum - change key index in the wallet, use None for no change. change_satoshi - change amount in satoshis, use 0 for no change. Supporting (previous) txs will be created to fund keynums and are controlled by these parameters: prevtx_keynums - keynums will show up as outputs of previous txs. A number randomly picked from this list controls how many keynums are chosen to include per prev tx. prevtx_outputs - in addition to previous tx outputs funding keynums, other outputs may be present. A number randomly picked from this list controls how many ignored outputs are injected per keynum. prevtx_inputs - previous txs need inputs too. A number randomly picked from this list controls how many inputs are chosen per previous tx. """ total_in_satoshi = sum(satoshi for _, satoshi in keynums_satoshi) fee_satoshi = total_in_satoshi - out_satoshi - change_satoshi chain0 = self.wallet.subkey(0, is_hardened = True).subkey(0) chain1 = self.wallet.subkey(0, is_hardened = True).subkey(1) assert total_in_satoshi >= out_satoshi + change_satoshi assert len(keynums_satoshi) <= 32 # # Step 1: send the inputs and outputs to use in the signed tx # # Create the (key num, compressed public key) tuple, input keys assume an m/0h/0/keynum path for now. keys = [(keynum, encoding.public_pair_to_sec(chain0.subkey(keynum).public_pair)) for (keynum, _) in keynums_satoshi] # Convert base58 address to raw hex address out_addr_160 = encoding.bitcoin_address_to_hash160_sec(out_addr) print() print("Sign tx parameters:", "") for i, (keynum, satoshi) in enumerate(keynums_satoshi): print("{:<10}{:16.8f} btc < key {}".format (" inputs" if 0 == i else "", satoshi / 100000000, keynum)) print("{:<10}{:16.8f} btc > {}".format (" output", out_satoshi / 100000000, self.hexstr(out_addr_160))) print("{:<10}{:16.8f} btc > key {}".format (" change", change_satoshi / 100000000, change_keynum)) print("{:<10}{:16.8f} btc".format (" fee", fee_satoshi / 100000000)) print("{:<10}{:16.8f} btc".format (" total", total_in_satoshi / 100000000)) print() print(self.PAD.format("Send tx parameters"), end='') # ---> send to Polly self.polly.send_sign_tx(keys, out_addr_160, out_satoshi, change_keynum, change_satoshi) print(self.__outok()) # # Step 2: send previous txs to fund the inputs # print() cur = 0 prevtx_info = [] while cur < len(keynums_satoshi) : prevtx_outputs_satoshi = [] # Calculate how many keynums will be associated with this prev tx end = min(cur + random.choice(prevtx_keynums), len(keynums_satoshi)) # Create the prev tx output list for keynum, satoshi in keynums_satoshi[cur:end] : # Inject a random number of outputs not associated with tx input keynums for _ in range(0, random.choice(prevtx_outputs)) : prevtx_outputs_satoshi.append((random.randint(0, 0x7FFFFFFF), random.randint(0, 2099999997690000))) # Add the outputs funding the tx input keynums prevtx_outputs_satoshi.append((keynum, satoshi)) # Create output script addr = chain0.subkey(keynum, as_private = True).bitcoin_address() script = standard_tx_out_script(addr) # Capture some info we'll use later to verify the signed tx prevtx_info.append((keynum, satoshi, script, 0, # This is the hash and will be replaced later len(prevtx_outputs_satoshi) - 1)) # Index of the valid output print("{:30}{}".format("Make prev tx for keys", " ".join(str(keynum) for (keynum, _, _, _, _) in prevtx_info[cur:]))) # Create the prev tx prevtx = self.create_prev_tx(win = Wallet.from_master_secret(bytes(0)), # create a dummy wallet in_keynum = list(range(0, random.choice(prevtx_inputs))), sources_per_input = 1, wout = chain0, out_keynum_satoshi = prevtx_outputs_satoshi, fees_satoshi = random.randint(100, 1000)) # We have built the prev tx, calculate its hash (and reverse the bytes) prevtx_hash = encoding.double_sha256(prevtx)[::-1] # Update the hashes now that we have a full prev tx for i, (keynum, satoshi, script, _, outidx) in enumerate(prevtx_info[cur:]) : prevtx_info[i + cur] = (keynum, satoshi, script, prevtx_hash, outidx) # Create the index table that matches a keynum index with an ouput index in this prev tx idx_table = [(keynum_idx + cur, outidx) for keynum_idx, (_, _, _, _, outidx) in enumerate(prevtx_info[cur:])] print(self.PAD.format("Send prev tx "), end='') # ---> send to Polly self.polly.send_prev_tx(idx_table, prevtx) print(self.__outok()) cur = end # # Step 3: generate a signed tx with the reference wallet and compare against Polly's # spendables = [] wifs = [] # Make sure that the inputs add up correctly, and prep the input_sources for reference wallet signing for (keynum, satoshi, script, prevtx_hash, outidx) in prevtx_info: spendables.append(Spendable(satoshi, script, prevtx_hash, outidx)) wifs.append(chain0.subkey(keynum, as_private = True).wif()) change_addr = chain1.subkey(change_keynum).bitcoin_address() payables = [(out_addr, out_satoshi), (change_addr, change_satoshi)] print() print(self.PAD.format("Make reference signature")) signed_tx = create_signed_tx(spendables, payables, wifs, fee_satoshi) signed_tx = self.get_tx_bytes(signed_tx) print(self.PAD.format("Get signed tx"), end='', flush = True) # <--- get the signed tx from Polly polly_signed_tx = self.polly.send_get_signed_tx() #print(self.txstr(polly_signed_tx)) #print(self.txstr(signed_tx)) print(self.__outok()) # Compare reference wallet signed tx with polly's assert signed_tx == polly_signed_tx, "test_sign: signature mismatch\nExpected:\n" + self.hexstr(signed_tx) + "\n\nActual:\n" + self.hexstr(polly_signed_tx)
def coinbase_tx(secret_exponent): public_pair = ecdsa.public_pair_for_secret_exponent( ecdsa.secp256k1.generator_secp256k1, secret_exponent) public_key_sec = public_pair_to_sec(public_pair) return Tx.coinbase_tx(public_key_sec, 2500000000)
def _get_multisig_addr(ms, n, i): ms_i = [hexlify(public_pair_to_sec(m.subkey(i).public_pair)) for m in ms] info = conf.bitcoind.createmultisig(n, ms_i) return info
assert(my_key.sec_as_hex() == bitcoin.core.b2x(my_key.sec())) print("Public key hash160: ", b2h(my_key.hash160())) print(" uncompressed: ", b2h(my_key.hash160(use_uncompressed=True))) #print("Bitcoin Address : ", my_key.address()) addr_compressed = encoding.public_pair_to_bitcoin_address(public_key, True, my_addr_prefix) addr_uncompressed = encoding.public_pair_to_bitcoin_address(public_key, False, my_addr_prefix) print("Bitcoin Address: ", addr_compressed) print(" uncompressed: ", addr_uncompressed) assert(encoding.is_valid_bitcoin_address(addr_compressed, my_addr_prefix)) assert(encoding.is_valid_bitcoin_address(addr_uncompressed, my_addr_prefix)) assert(my_key.address() == addr_compressed) pubkey_bytes = encoding.public_pair_to_sec(public_key, True); assert(my_key.sec_as_hex() == b2h(pubkey_bytes)) pubkey_bytes = encoding.public_pair_to_sec(public_key, False); assert(my_key.sec_as_hex(use_uncompressed=True) == b2h(pubkey_bytes)) print() #CBitcoinAddress.from_bytes(bitcoin.core.serialize.Hash160(my_key.address()), 111) btc_addr = CBitcoinAddress.from_bytes(bitcoin.base58.decode(my_key.address()), bitcoin.params.BASE58_PREFIXES['PUBKEY_ADDR']) print("Bitcoin Address hex: ", hexlify(btc_addr.to_bytes())) assert(bitcoin.base58.encode(btc_addr.to_bytes()) == addr_compressed) pubkey_b58 = encoding.b2a_base58(pubkey_bytes) #CBitcoinAddress.from_scriptPubKey(pubkey_b58)
def transaction(tx_info, encoding, exact_fee=None, fee_provided=0, unittest=False, public_key_hex=None, allow_unconfirmed_inputs=False): (source, destination_outputs, data) = tx_info if exact_fee and not isinstance(exact_fee, int): raise exceptions.TransactionError('Exact fees must be in satoshis.') if not isinstance(fee_provided, int): raise exceptions.TransactionError('Fee provided must be in satoshis.') if encoding not in ('pubkeyhash', 'multisig', 'opreturn'): raise exceptions.TransactionError('Unknown encoding‐scheme.') # If public key is necessary for construction of (unsigned) transaction, # either use the public key provided, or derive it from a private key # retrieved from wallet. public_key = None if encoding in ('multisig', 'pubkeyhash'): # get the address type pdb.set_trace() p2sh = get_address_type(source) if p2sh: temp = rpc('validateaddress', [source]) redeemscript = temp['hex'] else: # If no public key was provided, derive from private key. if not public_key_hex: # Get private key. if unittest: private_key_wif = 'cPdUqd5EbBWsjcG9xiL1hz8bEyGFiz4SW99maU9JgpL9TEcxUf3j' else: private_key_wif = rpc('dumpprivkey', [source]) # Derive public key. public_key_hex = private_key_to_public_key(private_key_wif) pubkeypair = bitcoin_utils.parse_as_public_pair(public_key_hex) if not pubkeypair: raise exceptions.InputError('Invalid private key.') public_key = public_pair_to_sec(pubkeypair, compressed=True) # Protocol change. if encoding == 'pubkeyhash' and get_block_count( ) < 293000 and not config.TESTNET: raise exceptions.TransactionError( 'pubkeyhash encoding unsupported before block 293000') if config.PREFIX == config.UNITTEST_PREFIX: unittest = True # Validate source and all destination addresses. destinations = [address for address, value in destination_outputs] for address in destinations + [source]: if address: try: base58_decode(address, config.ADDRESSVERSION, config.ADDRESSVERSION_MULTISIG) except Exception: # TODO raise exceptions.AddressError('Invalid Bitcoin address:', address) # Check that the source is in wallet. if not unittest and encoding in ('multisig') and not public_key: if not rpc('validateaddress', [source])['ismine']: raise exceptions.AddressError('Not one of your Bitcoin addresses:', source) # Check that the destination output isn't a dust output. # Set null values to dust size. new_destination_outputs = [] for address, value in destination_outputs: if encoding == 'multisig': if value == None: value = config.MULTISIG_DUST_SIZE if not value >= config.MULTISIG_DUST_SIZE: raise exceptions.TransactionError( 'Destination output is below the dust target value.') else: if value == None: value = config.REGULAR_DUST_SIZE if not value >= config.REGULAR_DUST_SIZE: raise exceptions.TransactionError( 'Destination output is below the dust target value.') new_destination_outputs.append((address, value)) destination_outputs = new_destination_outputs # Divide data into chunks. if data: def chunks(l, n): """ Yield successive n‐sized chunks from l. """ for i in range(0, len(l), n): yield l[i:i + n] if encoding == 'pubkeyhash': data_array = list(chunks(data + config.PREFIX, 20 - 1)) # Prefix is also a suffix here. elif encoding == 'multisig': data_array = list(chunks(data, 33 - 1)) elif encoding == 'opreturn': data_array = list(chunks(data, 80)) assert len( data_array ) == 1 # Only one OP_RETURN output currently supported (messages should all be shorter than 80 bytes, at the moment). else: data_array = [] # Calculate total BTC to be sent. btc_out = 0 if encoding == 'multisig': data_value = config.MULTISIG_DUST_SIZE elif encoding == 'opreturn': data_value = config.OP_RETURN_VALUE else: data_value = config.REGULAR_DUST_SIZE # Pay‐to‐PubKeyHash btc_out = sum([data_value for data_chunk in data_array]) btc_out += sum([value for address, value in destination_outputs]) # Get size of outputs. if encoding == 'multisig': data_output_size = 81 # 71 for the data elif encoding == 'opreturn': data_output_size = 90 # 80 for the data else: data_output_size = 25 + 9 # Pay‐to‐PubKeyHash (25 for the data?) outputs_size = ((25 + 9) * len(destination_outputs)) + (len(data_array) * data_output_size) # Get inputs. unspent = get_unspent_txouts(source, normalize=True, unittest=unittest) unspent = sort_unspent_txouts(unspent, allow_unconfirmed_inputs) inputs, btc_in = [], 0 change_quantity = 0 sufficient_funds = False final_fee = config.FEE_PER_KB for coin in unspent: inputs.append(coin) btc_in += round(coin['amount'] * config.UNIT) # If exact fee is specified, use that. Otherwise, calculate size of tx and base fee on that (plus provide a minimum fee for selling BTC). if exact_fee: final_fee = exact_fee else: size = 181 * len(inputs) + outputs_size + 10 necessary_fee = (int(size / 10000) + 1) * config.FEE_PER_KB final_fee = max(fee_provided, necessary_fee) assert final_fee >= 1 * config.FEE_PER_KB # Check if good. change_quantity = btc_in - (btc_out + final_fee) if change_quantity == 0 or change_quantity >= config.REGULAR_DUST_SIZE: # If change is necessary, must not be a dust output. sufficient_funds = True break if not sufficient_funds: # Approximate needed change, fee by with most recently calculated quantities. total_btc_out = btc_out + max(change_quantity, 0) + final_fee raise exceptions.BalanceError( 'Insufficient bitcoins at address {}. (Need approximately {} BTC.)' .format(source, total_btc_out / config.UNIT)) # Construct outputs. if data: data_output = (data_array, data_value) else: data_output = None if change_quantity: change_output = (source, change_quantity) else: change_output = None # Serialise inputs and outputs. transaction = serialise(encoding, inputs, destination_outputs, data_output, change_output, source=source, public_key=public_key) unsigned_tx_hex = binascii.hexlify(transaction).decode('utf-8') return unsigned_tx_hex
def generate_new_key(): secret = secrets.randbits(256) secret_hex = b2a_hex(to_bytes_32(secret)).decode() key = Key(secret_exponent=secret) sec = public_pair_to_sec(key.public_pair()) return b2a_hex(sec).decode(), secret_hex
def get_compressed_pubkey_format(pubkey): public_pair=encoding.sec_to_public_pair(encoding.binascii.unhexlify(pubkey)) return encoding.binascii.hexlify(encoding.public_pair_to_sec(public_pair))
pubkey_hashed = encoding.public_pair_to_hash160_sec(pubkey_pair, False) print(" uncompressed: ", bitcoin.core.b2x(pubkey_hashed)) pubkey_addr = encoding.hash160_sec_to_bitcoin_address( pubkey_hashed, address_prefix=my_pubaddr_prefix) assert (addr_uncompressed == pubkey_addr) print("Bitcoin Address: ", addr_compressed) print(" uncompressed: ", addr_uncompressed) assert (encoding.is_valid_bitcoin_address( addr_compressed, allowable_prefixes=my_pubaddr_prefix)) assert (encoding.is_valid_bitcoin_address( addr_uncompressed, allowable_prefixes=my_pubaddr_prefix)) print() ## Uncompressed public key pubkey_bytes = encoding.public_pair_to_sec(pubkey_pair, False) # uncompressed pubkey_b58 = encoding.b2a_base58(pubkey_bytes) assert (pubkey_bytes == encoding.a2b_base58(pubkey_b58)) btc_addr = CBitcoinAddress.from_bytes( pubkey_bytes, bitcoin.params.BASE58_PREFIXES['PUBKEY_ADDR']) assert (b2h(cec_key.get_pubkey()) == b2h(btc_addr.to_bytes())) assert (hexlify(cec_key.get_pubkey()) == hexlify(pubkey_bytes)) #print("Uncompressed public key") c_pubkey = CPubKey(pubkey_bytes) #print("Is public key valid? ", c_pubkey.is_valid, ", compressed? ", c_pubkey.is_compressed) assert (c_pubkey.is_compressed == False) assert (c_pubkey.is_valid == True)
def transaction (tx_info, encoding='auto', fee_per_kb=config.DEFAULT_FEE_PER_KB, regular_dust_size=config.DEFAULT_REGULAR_DUST_SIZE, multisig_dust_size=config.DEFAULT_MULTISIG_DUST_SIZE, op_return_value=config.DEFAULT_OP_RETURN_VALUE, exact_fee=None, fee_provided=0, public_key_hex=None, allow_unconfirmed_inputs=False, armory=False): (source, destination_outputs, data) = tx_info # Data encoding methods. if data: if encoding == 'auto': if len(data) <= 40: # encoding = 'opreturn' encoding = 'multisig' # BTCGuild isn’t mining OP_RETURN?! else: encoding = 'multisig' if encoding not in ('pubkeyhash', 'multisig', 'opreturn'): raise exceptions.TransactionError('Unknown encoding‐scheme.') if exact_fee and not isinstance(exact_fee, int): raise exceptions.TransactionError('Exact fees must be in satoshis.') if not isinstance(fee_provided, int): raise exceptions.TransactionError('Fee provided must be in satoshis.') # If public key is necessary for construction of (unsigned) transaction, # either use the public key provided, or derive it from a private key # retrieved from wallet. public_key = None if encoding in ('multisig', 'pubkeyhash'): # If no public key was provided, derive from private key. if not public_key_hex: # Get private key. if config.UNITTEST: private_key_wif = config.UNITTEST_PRIVKEY[source] else: private_key_wif = rpc('dumpprivkey', [source]) # Derive public key. public_key_hex = private_key_to_public_key(private_key_wif) pubkeypair = bitcoin_utils.parse_as_public_pair(public_key_hex) if not pubkeypair: raise exceptions.InputError('Invalid private key.') public_key = public_pair_to_sec(pubkeypair, compressed=True) # Protocol change. if encoding == 'pubkeyhash' and get_block_count() < 293000 and not config.TESTNET: raise exceptions.TransactionError('pubkeyhash encoding unsupported before block 293000') # Validate source and all destination addresses. destinations = [address for address, value in destination_outputs] for address in destinations + [source]: if address: try: base58_decode(address, config.ADDRESSVERSION) except Exception: # TODO raise exceptions.AddressError('Invalid Bitcoin address:', address) # Check that the source is in wallet. if not config.UNITTEST and encoding in ('multisig') and not public_key: if not rpc('validateaddress', [source])['ismine']: raise exceptions.AddressError('Not one of your Bitcoin addresses:', source) # Check that the destination output isn't a dust output. # Set null values to dust size. new_destination_outputs = [] for address, value in destination_outputs: if encoding == 'multisig': if value == None: value = multisig_dust_size if not value >= multisig_dust_size: raise exceptions.TransactionError('Destination output is below the dust target value.') else: if value == None: value = regular_dust_size if not value >= regular_dust_size: raise exceptions.TransactionError('Destination output is below the dust target value.') new_destination_outputs.append((address, value)) destination_outputs = new_destination_outputs # Divide data into chunks. if data: def chunks(l, n): """ Yield successive n‐sized chunks from l. """ for i in range(0, len(l), n): yield l[i:i+n] if encoding == 'pubkeyhash': data_array = list(chunks(data + config.PREFIX, 20 - 1)) # Prefix is also a suffix here. elif encoding == 'multisig': data_array = list(chunks(data, 33 - 1)) elif encoding == 'opreturn': data_array = list(chunks(data, config.OP_RETURN_MAX_SIZE)) assert len(data_array) == 1 # Only one OP_RETURN output currently supported (OP_RETURN messages should all be shorter than 40 bytes, at the moment). else: data_array = [] # Calculate total BTC to be sent. btc_out = 0 if encoding == 'multisig': data_value = multisig_dust_size elif encoding == 'opreturn': data_value = op_return_value else: data_value = regular_dust_size # Pay‐to‐PubKeyHash btc_out = sum([data_value for data_chunk in data_array]) btc_out += sum([value for address, value in destination_outputs]) # Get size of outputs. if encoding == 'multisig': data_output_size = 81 # 71 for the data elif encoding == 'opreturn': data_output_size = 90 # 80 for the data else: data_output_size = 25 + 9 # Pay‐to‐PubKeyHash (25 for the data?) outputs_size = ((25 + 9) * len(destination_outputs)) + (len(data_array) * data_output_size) # Get inputs. unspent = get_unspent_txouts(source, normalize=True) unspent = sort_unspent_txouts(unspent, allow_unconfirmed_inputs) logging.debug('Sorted UTXOs: {}'.format([print_coin(coin) for coin in unspent])) inputs, btc_in = [], 0 change_quantity = 0 sufficient_funds = False final_fee = fee_per_kb for coin in unspent: logging.debug('New input: {}'.format(print_coin(coin))) inputs.append(coin) btc_in += round(coin['amount'] * config.UNIT) # If exact fee is specified, use that. Otherwise, calculate size of tx and base fee on that (plus provide a minimum fee for selling BTC). if exact_fee: final_fee = exact_fee else: size = 181 * len(inputs) + outputs_size + 10 necessary_fee = (int(size / 1000) + 1) * fee_per_kb final_fee = max(fee_provided, necessary_fee) assert final_fee >= 1 * fee_per_kb # Check if good. change_quantity = btc_in - (btc_out + final_fee) logging.debug('Change quantity: {} BTC'.format(change_quantity / config.UNIT)) if change_quantity == 0 or change_quantity >= regular_dust_size: # If change is necessary, must not be a dust output. sufficient_funds = True break if not sufficient_funds: # Approximate needed change, fee by with most recently calculated quantities. total_btc_out = btc_out + max(change_quantity, 0) + final_fee raise exceptions.BalanceError('Insufficient bitcoins at address {}. (Need approximately {} {}.) To spend unconfirmed coins, use the flag `--unconfirmed`.'.format(source, total_btc_out / config.UNIT, config.BTC)) # Construct outputs. if data: data_output = (data_array, data_value) else: data_output = None if change_quantity: change_output = (source, change_quantity) else: change_output = None # Serialise inputs and outputs. unsigned_tx = serialise(encoding, inputs, destination_outputs, data_output, change_output, source=source, public_key=public_key) unsigned_tx_hex = binascii.hexlify(unsigned_tx).decode('utf-8') # bip-0010 try: if armory: txdp = [] dpid = base58_encode(hashlib.sha256(unsigned_tx).digest())[:8] txdp.append(('-----BEGIN-TRANSACTION-' + dpid + '-----').ljust(80,'-')) magic_bytes = binascii.hexlify(config.MAGIC_BYTES).decode('utf-8') varIntTxSize = binascii.hexlify(len(unsigned_tx).to_bytes(2, byteorder='big')).decode('utf-8') txdp.append('_TXDIST_{}_{}_{}'.format(magic_bytes, dpid, varIntTxSize)) tx_list = unsigned_tx_hex for coin in inputs: tx_list += get_raw_transaction(coin['txid'], json=False) for byte in range(0,len(tx_list),80): txdp.append(tx_list[byte:byte+80] ) for index, coin in enumerate(inputs): index_fill = str(index).zfill(2) value = '{0:.8f}'.format(coin['amount']) txdp.append('_TXINPUT_{}_{}'.format(index_fill, value)) txdp.append(('-----END-TRANSACTION-' + dpid + '-----').ljust(80,'-')) unsigned_tx_hex = '\n'.join(txdp) except Exception as e: print(e) return unsigned_tx_hex
def serialise(inputs, destination_output=None, data_output=None, change_output=None, source=None, multisig=False): s = (1).to_bytes(4, byteorder='little') # Version # Number of inputs. s += var_int(int(len(inputs))) # List of Inputs. for i in range(len(inputs)): txin = inputs[i] s += binascii.unhexlify(bytes(txin['txid'], 'utf-8'))[::-1] # TxOutHash s += txin['vout'].to_bytes(4, byteorder='little') # TxOutIndex script = str.encode(txin['scriptPubKey']) s += var_int(int(len(script))) # Script length s += script # Script s += b'\xff' * 4 # Sequence # Number of outputs. n = 0 if destination_output: n += 1 if data_output: data_array, value = data_output for data_chunk in data_array: n += 1 else: data_array = [] if change_output: n += 1 s += var_int(n) # Destination output. if destination_output: address, value = destination_output pubkeyhash = base58_decode(address, config.ADDRESSVERSION) s += value.to_bytes(8, byteorder='little') # Value script = OP_DUP # OP_DUP script += OP_HASH160 # OP_HASH160 script += op_push(20) # Push 0x14 bytes script += pubkeyhash # pubKeyHash script += OP_EQUALVERIFY # OP_EQUALVERIFY script += OP_CHECKSIG # OP_CHECKSIG s += var_int(int(len(script))) # Script length s += script # Data output. for data_chunk in data_array: data_array, value = data_output # DUPE s += value.to_bytes(8, byteorder='little') # Value if multisig: # Get source public key (either provided as a string or derived from a private key in the wallet). if isinstance(multisig, str): pubkeypair = bitcoin_utils.parse_as_public_pair(multisig) source_pubkey = public_pair_to_sec(pubkeypair, compressed=True) else: if config.PREFIX == config.UNITTEST_PREFIX: private_key_wif = 'cPdUqd5EbBWsjcG9xiL1hz8bEyGFiz4SW99maU9JgpL9TEcxUf3j' else: private_key_wif = rpc('dumpprivkey', [source]) if private_key_wif[0] == 'c': testnet = True else: testnet = False secret_exponent, compressed = wif_to_tuple_of_secret_exponent_compressed( private_key_wif, is_test=testnet) public_pair = public_pair_for_secret_exponent( generator_secp256k1, secret_exponent) source_pubkey = public_pair_to_sec(public_pair, compressed=compressed) # Get data (fake) public key. pad_length = 33 - 1 - len(data_chunk) assert pad_length >= 0 data_pubkey = bytes([len(data_chunk) ]) + data_chunk + (pad_length * b'\x00') script = OP_1 # OP_1 script += op_push( len(source_pubkey)) # Push bytes of source public key script += source_pubkey # Source public key script += op_push( len(data_pubkey)) # Push bytes of data chunk (fake) public key script += data_pubkey # Data chunk (fake) public key script += OP_2 # OP_2 script += OP_CHECKMULTISIG # OP_CHECKMULTISIG else: script = OP_RETURN # OP_RETURN script += op_push(len( data_chunk)) # Push bytes of data chunk (NOTE: OP_SMALLDATA?) script += data_chunk # Data chunk s += var_int(int(len(script))) # Script length s += script # Change output. if change_output: address, value = change_output pubkeyhash = base58_decode(address, config.ADDRESSVERSION) s += value.to_bytes(8, byteorder='little') # Value script = OP_DUP # OP_DUP script += OP_HASH160 # OP_HASH160 script += op_push(20) # Push 0x14 bytes script += pubkeyhash # pubKeyHash script += OP_EQUALVERIFY # OP_EQUALVERIFY script += OP_CHECKSIG # OP_CHECKSIG s += var_int(int(len(script))) # Script length s += script s += (0).to_bytes(4, byteorder='little') # LockTime return s
def serialise(inputs, destination_output=None, data_output=None, change_output=None, multisig=False, source=None): s = (1).to_bytes(4, byteorder='little') # Version # Number of inputs. s += var_int(int(len(inputs))) # List of Inputs. for i in range(len(inputs)): txin = inputs[i] s += binascii.unhexlify(bytes(txin['txid'], 'utf-8'))[::-1] # TxOutHash s += txin['vout'].to_bytes(4, byteorder='little') # TxOutIndex # No signature. script = b'' s += var_int(int(len(script))) # Script length s += script # Script s += b'\xff' * 4 # Sequence # Number of outputs. n = 0 if destination_output: n += 1 if data_output: data_array, value = data_output for data_chunk in data_array: n += 1 else: data_array = [] if change_output: n += 1 s += var_int(n) # Destination output. if destination_output: address, value = destination_output pubkeyhash = base58_decode(address, config.ADDRESSVERSION) s += value.to_bytes(8, byteorder='little') # Value script = OP_DUP # OP_DUP script += OP_HASH160 # OP_HASH160 script += op_push(20) # Push 0x14 bytes script += pubkeyhash # pubKeyHash script += OP_EQUALVERIFY # OP_EQUALVERIFY script += OP_CHECKSIG # OP_CHECKSIG s += var_int(int(len(script))) # Script length s += script # Data output. for data_chunk in data_array: data_array, value = data_output # DUPE s += (value).to_bytes(8, byteorder='little') # Value if multisig: # Get source public key. from pycoin.ecdsa import generator_secp256k1, public_pair_for_secret_exponent from pycoin.encoding import wif_to_tuple_of_secret_exponent_compressed, public_pair_to_sec private_key_wif = rpc('dumpprivkey', [source]) secret_exponent, compressed = wif_to_tuple_of_secret_exponent_compressed( private_key_wif) public_pair = public_pair_for_secret_exponent( generator_secp256k1, secret_exponent) source_pubkey = public_pair_to_sec(public_pair, compressed=compressed) # Get data (fake) public key. pad_length = 33 - 1 - len(data_chunk) assert pad_length >= 0 data_pubkey = bytes([len(data_chunk) ]) + data_chunk + (pad_length * b'\x00') script = OP_1 # OP_1 script += op_push( len(source_pubkey)) # Push bytes of source public key script += source_pubkey # Source public key script += op_push( len(data_pubkey)) # Push bytes of data chunk (fake) public key script += data_pubkey # Data chunk (fake) public key script += OP_2 # OP_2 script += OP_CHECKMULTISIG # OP_CHECKMULTISIG else: script = OP_RETURN # OP_RETURN script += op_push(len( data_chunk)) # Push bytes of data chunk (NOTE: OP_SMALLDATA?) script += data_chunk # Data chunk s += var_int(int(len(script))) # Script length s += script # Change output. if change_output: address, value = change_output pubkeyhash = base58_decode(address, config.ADDRESSVERSION) s += value.to_bytes(8, byteorder='little') # Value script = OP_DUP # OP_DUP script += OP_HASH160 # OP_HASH160 script += op_push(20) # Push 0x14 bytes script += pubkeyhash # pubKeyHash script += OP_EQUALVERIFY # OP_EQUALVERIFY script += OP_CHECKSIG # OP_CHECKSIG s += var_int(int(len(script))) # Script length s += script s += (0).to_bytes(4, byteorder='little') # LockTime return s
def serialise (inputs, destination_output=None, data_output=None, change_output=None, multisig=False, source=None): s = (1).to_bytes(4, byteorder='little') # Version # Number of inputs. s += var_int(int(len(inputs))) # List of Inputs. for i in range(len(inputs)): txin = inputs[i] s += binascii.unhexlify(bytes(txin['txid'], 'utf-8'))[::-1] # TxOutHash s += txin['vout'].to_bytes(4, byteorder='little') # TxOutIndex # No signature. script = b'' s += var_int(int(len(script))) # Script length s += script # Script s += b'\xff' * 4 # Sequence # Number of outputs. n = 0 if destination_output: n += 1 if data_output: data_array, value = data_output for data_chunk in data_array: n += 1 else: data_array = [] if change_output: n += 1 s += var_int(n) # Destination output. if destination_output: address, value = destination_output pubkeyhash = base58_decode(address, config.ADDRESSVERSION) s += value.to_bytes(8, byteorder='little') # Value script = OP_DUP # OP_DUP script += OP_HASH160 # OP_HASH160 script += op_push(20) # Push 0x14 bytes script += pubkeyhash # pubKeyHash script += OP_EQUALVERIFY # OP_EQUALVERIFY script += OP_CHECKSIG # OP_CHECKSIG s += var_int(int(len(script))) # Script length s += script # Data output. for data_chunk in data_array: data_array, value = data_output # DUPE s += (value).to_bytes(8, byteorder='little') # Value if multisig: # Get source public key. from pycoin.ecdsa import generator_secp256k1, public_pair_for_secret_exponent from pycoin.encoding import wif_to_tuple_of_secret_exponent_compressed, public_pair_to_sec private_key_wif = rpc('dumpprivkey', [source]) secret_exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(private_key_wif) public_pair = public_pair_for_secret_exponent(generator_secp256k1, secret_exponent) source_pubkey = public_pair_to_sec(public_pair, compressed=compressed) # Get data (fake) public key. pad_length = 33 - 1 - len(data_chunk) assert pad_length >= 0 data_pubkey = bytes([len(data_chunk)]) + data_chunk + (pad_length * b'\x00') script = OP_1 # OP_1 script += op_push(len(source_pubkey)) # Push bytes of source public key script += source_pubkey # Source public key script += op_push(len(data_pubkey)) # Push bytes of data chunk (fake) public key script += data_pubkey # Data chunk (fake) public key script += OP_2 # OP_2 script += OP_CHECKMULTISIG # OP_CHECKMULTISIG else: script = OP_RETURN # OP_RETURN script += op_push(len(data_chunk)) # Push bytes of data chunk (NOTE: OP_SMALLDATA?) script += data_chunk # Data chunk s += var_int(int(len(script))) # Script length s += script # Change output. if change_output: address, value = change_output pubkeyhash = base58_decode(address, config.ADDRESSVERSION) s += value.to_bytes(8, byteorder='little') # Value script = OP_DUP # OP_DUP script += OP_HASH160 # OP_HASH160 script += op_push(20) # Push 0x14 bytes script += pubkeyhash # pubKeyHash script += OP_EQUALVERIFY # OP_EQUALVERIFY script += OP_CHECKSIG # OP_CHECKSIG s += var_int(int(len(script))) # Script length s += script s += (0).to_bytes(4, byteorder='little') # LockTime return s
def serialise (inputs, destination_output=None, data_output=None, change_output=None, multisig=False, source=None, unsigned=False): assert not (multisig and unsigned is True) s = (1).to_bytes(4, byteorder='little') # Version # Number of inputs. s += var_int(int(len(inputs))) # List of Inputs. for i in range(len(inputs)): txin = inputs[i] s += binascii.unhexlify(bytes(txin['txid'], 'utf-8'))[::-1] # TxOutHash s += txin['vout'].to_bytes(4, byteorder='little') # TxOutIndex if not unsigned: # No signature. script = b'' else: #pubkeyhash = base58_decode(source, config.ADDRESSVERSION) #script = OP_DUP # OP_DUP #script += OP_HASH160 # OP_HASH160 #script += op_push(20) # Push 0x14 bytes #script += pubkeyhash # pubKeyHash #script += OP_EQUALVERIFY # OP_EQUALVERIFY #script += OP_CHECKSIG # OP_CHECKSIG script = str.encode(txin['scriptPubKey']) s += var_int(int(len(script))) # Script length s += script # Script s += b'\xff' * 4 # Sequence # Number of outputs. n = 0 if destination_output: n += 1 if data_output: data_array, value = data_output for data_chunk in data_array: n += 1 else: data_array = [] if change_output: n += 1 s += var_int(n) # Destination output. if destination_output: address, value = destination_output pubkeyhash = base58_decode(address, config.ADDRESSVERSION) s += value.to_bytes(8, byteorder='little') # Value script = OP_DUP # OP_DUP script += OP_HASH160 # OP_HASH160 script += op_push(20) # Push 0x14 bytes script += pubkeyhash # pubKeyHash script += OP_EQUALVERIFY # OP_EQUALVERIFY script += OP_CHECKSIG # OP_CHECKSIG s += var_int(int(len(script))) # Script length s += script # Data output. for data_chunk in data_array: data_array, value = data_output # DUPE s += (value).to_bytes(8, byteorder='little') # Value if multisig: # Get source public key. if unsigned: assert isinstance(unsigned, str) pubkeypair = bitcoin_utils.parse_as_public_pair(unsigned) source_pubkey = public_pair_to_sec(pubkeypair, compressed=True) else: if config.PREFIX == config.UNITTEST_PREFIX: private_key_wif = 'cPdUqd5EbBWsjcG9xiL1hz8bEyGFiz4SW99maU9JgpL9TEcxUf3j' else: private_key_wif = rpc('dumpprivkey', [source]) if private_key_wif[0] == 'c': testnet = True else: testnet = False secret_exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(private_key_wif, is_test=testnet) public_pair = public_pair_for_secret_exponent(generator_secp256k1, secret_exponent) source_pubkey = public_pair_to_sec(public_pair, compressed=compressed) # Get data (fake) public key. pad_length = 33 - 1 - len(data_chunk) assert pad_length >= 0 data_pubkey = bytes([len(data_chunk)]) + data_chunk + (pad_length * b'\x00') script = OP_1 # OP_1 script += op_push(len(source_pubkey)) # Push bytes of source public key script += source_pubkey # Source public key script += op_push(len(data_pubkey)) # Push bytes of data chunk (fake) public key script += data_pubkey # Data chunk (fake) public key script += OP_2 # OP_2 script += OP_CHECKMULTISIG # OP_CHECKMULTISIG else: script = OP_RETURN # OP_RETURN script += op_push(len(data_chunk)) # Push bytes of data chunk (NOTE: OP_SMALLDATA?) script += data_chunk # Data chunk s += var_int(int(len(script))) # Script length s += script # Change output. if change_output: address, value = change_output pubkeyhash = base58_decode(address, config.ADDRESSVERSION) s += value.to_bytes(8, byteorder='little') # Value script = OP_DUP # OP_DUP script += OP_HASH160 # OP_HASH160 script += op_push(20) # Push 0x14 bytes script += pubkeyhash # pubKeyHash script += OP_EQUALVERIFY # OP_EQUALVERIFY script += OP_CHECKSIG # OP_CHECKSIG s += var_int(int(len(script))) # Script length s += script s += (0).to_bytes(4, byteorder='little') # LockTime return s
def main(): parser = argparse.ArgumentParser(description="Bitcoin utilities.") parser.add_argument("-a", "--address", help="show as Bitcoin address", action="store_true") parser.add_argument("-1", "--hash160", help="show as hash 160", action="store_true") parser.add_argument("-v", "--verbose", help="dump all information available", action="store_true") parser.add_argument("-w", "--wif", help="show as Bitcoin WIF", action="store_true") parser.add_argument("-n", "--uncompressed", help="show in uncompressed form", action="store_true") parser.add_argument("-j", "--json", help="output in JSON format", action="store_true") parser.add_argument("-t", help="interpret fields as test values", action="store_true") parser.add_argument("-ltc", help="generate LTC address", action="store_true") parser.add_argument("-doge", help="generate DOGE address", action="store_true") parser.add_argument( "item", help="a WIF, secret exponent, X/Y public pair, SEC (as hex), hash160 (as hex), Bitcoin address", nargs="+", ) args = parser.parse_args() if args.ltc: args.t = "litecoin" else: if args.doge: if args.t: args.t = "dogetest" else: args.t = "doge" if args.json: print("{") argcount = len(args.item) i = 0 for c in args.item: i = i + 1 print(' "%s" : { ' % c) secret_exponent = parse_as_private_key(c) if secret_exponent: public_pair = ecdsa.public_pair_for_secret_exponent(secp256k1.generator_secp256k1, secret_exponent) print(' "secret_exponent" : "%d",' % secret_exponent) print(' "secret_exponent_hex" : "%x",' % secret_exponent) print( ' "wif" : "%s",' % encoding.secret_exponent_to_wif(secret_exponent, compressed=True, is_test=args.t) ) print( ' "wif_uncompressed" : "%s",' % encoding.secret_exponent_to_wif(secret_exponent, compressed=False, is_test=args.t) ) else: public_pair = parse_as_public_pair(c) if public_pair: bitcoin_address_uncompressed = encoding.public_pair_to_bitcoin_address( public_pair, compressed=False, is_test=args.t ) bitcoin_address_compressed = encoding.public_pair_to_bitcoin_address( public_pair, compressed=True, is_test=args.t ) print(' "public_pair_x" : "%d",' % public_pair[0]) print(' "public_pair_y" : "%d",' % public_pair[1]) print(' "x_as_hex" : "%x",' % public_pair[0]) print(' "y_as_hex" : "%x",' % public_pair[1]) if public_pair[1] & 1: print(' "y_parity" : "odd",') else: print(' "y_parity" : "even",') print(' "key_pair_as_sec" : "%s",' % b2h(encoding.public_pair_to_sec(public_pair, compressed=True))) hash160 = encoding.public_pair_to_hash160_sec(public_pair, compressed=True) hash160_unc = encoding.public_pair_to_hash160_sec(public_pair, compressed=False) else: hash160 = parse_as_address(c) hash160_unc = None if not hash160: print(' "error" : "cannot decode input %s",' % c) print(' "hash160" : "%s",' % b2h(hash160)) if hash160_unc: print(' "hash160_uncompressed" : "%s",' % b2h(hash160_unc)) if hash160_unc: # print(' "bitcoind_addr_uncompressed" : "%s",' % encoding.hash160_sec_to_bitcoin_address(hash160_unc)) print(' "bitcoind_addr_uncompressed" : "%s",' % bitcoin_address_uncompressed) # print(' "bitcoin_addr" : "%s"' % encoding.hash160_sec_to_bitcoin_address(hash160)) print(' "bitcoin_addr" : "%s"' % bitcoin_address_compressed) if i == argcount: print(" }") else: print(" },") print("}") else: for c in args.item: # figure out what it is: # - secret exponent # - WIF # - X/Y public key (base 10 or hex) # - sec # - hash160 # - Bitcoin address secret_exponent = parse_as_private_key(c) if secret_exponent: public_pair = ecdsa.public_pair_for_secret_exponent(secp256k1.generator_secp256k1, secret_exponent) print("secret exponent: %d" % secret_exponent) print(" hex: %x" % secret_exponent) print("WIF: %s" % encoding.secret_exponent_to_wif(secret_exponent, compressed=True)) print(" uncompressed: %s" % encoding.secret_exponent_to_wif(secret_exponent, compressed=False)) else: public_pair = parse_as_public_pair(c) if public_pair: bitcoin_address_uncompressed = encoding.public_pair_to_bitcoin_address(public_pair, compressed=False) bitcoin_address_compressed = encoding.public_pair_to_bitcoin_address(public_pair, compressed=True) print("public pair x: %d" % public_pair[0]) print("public pair y: %d" % public_pair[1]) print(" x as hex: %x" % public_pair[0]) print(" y as hex: %x" % public_pair[1]) print("y parity: %s" % "odd" if (public_pair[1] & 1) else "even") print("key pair as sec: %s" % b2h(encoding.public_pair_to_sec(public_pair, compressed=True))) s = b2h(encoding.public_pair_to_sec(public_pair, compressed=False)) print(" uncompressed: %s\\\n %s" % (s[:66], s[66:])) hash160 = encoding.public_pair_to_hash160_sec(public_pair, compressed=True) hash160_unc = encoding.public_pair_to_hash160_sec(public_pair, compressed=False) else: hash160 = parse_as_address(c) hash160_unc = None if not hash160: sys.stderr.write("can't decode input %s\n" % c) sys.exit(1) print("hash160: %s" % b2h(hash160)) if hash160_unc: print(" uncompressed: %s" % b2h(hash160_unc)) print("Bitcoin address: %s" % encoding.hash160_sec_to_bitcoin_address(hash160)) if hash160_unc: print(" uncompressed: %s" % encoding.hash160_sec_to_bitcoin_address(hash160_unc))
def private_key_to_public_key (private_key_wif): secret_exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(private_key_wif, is_test=config.TESTNET) public_pair = public_pair_for_secret_exponent(generator_secp256k1, secret_exponent) public_key = public_pair_to_sec(public_pair, compressed=compressed) public_key_hex = binascii.hexlify(public_key).decode('utf-8') return public_key_hex
def test_sign(self, keynums_satoshi, out_addr, out_satoshi, change_keynum, change_satoshi, prevtx_keynums, prevtx_outputs, prevtx_inputs): """ Performs a tx signing test, comparing Polly's signed tx against the reference wallet. Basic tx signing parameters: keynums_satoshi - list of tuples (keynum, satoshis) with key indices and their unspent value to use as tx inputs. Funding above out_satoshi + change_satoshi will be fees. out_addr - output address in bitcoin address format. out_satoshi - output amount in satoshis. change_keynum - change key index in the wallet, use None for no change. change_satoshi - change amount in satoshis, use 0 for no change. Supporting (previous) txs will be created to fund keynums and are controlled by these parameters: prevtx_keynums - keynums will show up as outputs of previous txs. A number randomly picked from this list controls how many keynums are chosen to include per prev tx. prevtx_outputs - in addition to previous tx outputs funding keynums, other outputs may be present. A number randomly picked from this list controls how many ignored outputs are injected per keynum. prevtx_inputs - previous txs need inputs too. A number randomly picked from this list controls how many inputs are chosen per previous tx. """ total_in_satoshi = sum(satoshi for _, satoshi in keynums_satoshi) fee_satoshi = total_in_satoshi - out_satoshi - change_satoshi chain0 = self.wallet.subkey(0, is_hardened=True).subkey(0) chain1 = self.wallet.subkey(0, is_hardened=True).subkey(1) assert total_in_satoshi >= out_satoshi + change_satoshi assert len(keynums_satoshi) <= 32 # # Step 1: send the inputs and outputs to use in the signed tx # # Create the (key num, compressed public key) tuple, input keys assume an m/0h/0/keynum path for now. keys = [ (keynum, encoding.public_pair_to_sec(chain0.subkey(keynum).public_pair)) for (keynum, _) in keynums_satoshi ] # Convert base58 address to raw hex address out_addr_160 = encoding.bitcoin_address_to_hash160_sec(out_addr) print() print("Sign tx parameters:", "") for i, (keynum, satoshi) in enumerate(keynums_satoshi): print("{:<10}{:16.8f} btc < key {}".format( " inputs" if 0 == i else "", satoshi / 100000000, keynum)) print("{:<10}{:16.8f} btc > {}".format(" output", out_satoshi / 100000000, self.hexstr(out_addr_160))) print("{:<10}{:16.8f} btc > key {}".format(" change", change_satoshi / 100000000, change_keynum)) print("{:<10}{:16.8f} btc".format(" fee", fee_satoshi / 100000000)) print("{:<10}{:16.8f} btc".format(" total", total_in_satoshi / 100000000)) print() print(self.PAD.format("Send tx parameters"), end='') # ---> send to Polly self.polly.send_sign_tx(keys, out_addr_160, out_satoshi, change_keynum, change_satoshi) print(self.__outok()) # # Step 2: send previous txs to fund the inputs # print() cur = 0 prevtx_info = [] while cur < len(keynums_satoshi): prevtx_outputs_satoshi = [] # Calculate how many keynums will be associated with this prev tx end = min(cur + random.choice(prevtx_keynums), len(keynums_satoshi)) # Create the prev tx output list for keynum, satoshi in keynums_satoshi[cur:end]: # Inject a random number of outputs not associated with tx input keynums for _ in range(0, random.choice(prevtx_outputs)): prevtx_outputs_satoshi.append( (random.randint(0, 0x7FFFFFFF), random.randint(0, 2099999997690000))) # Add the outputs funding the tx input keynums prevtx_outputs_satoshi.append((keynum, satoshi)) # Create output script addr = chain0.subkey(keynum, as_private=True).bitcoin_address() script = standard_tx_out_script(addr) # Capture some info we'll use later to verify the signed tx prevtx_info.append(( keynum, satoshi, script, 0, # This is the hash and will be replaced later len(prevtx_outputs_satoshi) - 1)) # Index of the valid output print("{:30}{}".format( "Make prev tx for keys", " ".join( str(keynum) for (keynum, _, _, _, _) in prevtx_info[cur:]))) # Create the prev tx prevtx = self.create_prev_tx( win=Wallet.from_master_secret( bytes(0)), # create a dummy wallet in_keynum=list(range(0, random.choice(prevtx_inputs))), sources_per_input=1, wout=chain0, out_keynum_satoshi=prevtx_outputs_satoshi, fees_satoshi=random.randint(100, 1000)) # We have built the prev tx, calculate its hash (and reverse the bytes) prevtx_hash = encoding.double_sha256(prevtx)[::-1] # Update the hashes now that we have a full prev tx for i, (keynum, satoshi, script, _, outidx) in enumerate(prevtx_info[cur:]): prevtx_info[i + cur] = (keynum, satoshi, script, prevtx_hash, outidx) # Create the index table that matches a keynum index with an ouput index in this prev tx idx_table = [ (keynum_idx + cur, outidx) for keynum_idx, (_, _, _, _, outidx) in enumerate(prevtx_info[cur:]) ] print(self.PAD.format("Send prev tx "), end='') # ---> send to Polly self.polly.send_prev_tx(idx_table, prevtx) print(self.__outok()) cur = end # # Step 3: generate a signed tx with the reference wallet and compare against Polly's # spendables = [] wifs = [] # Make sure that the inputs add up correctly, and prep the input_sources for reference wallet signing for (keynum, satoshi, script, prevtx_hash, outidx) in prevtx_info: spendables.append(Spendable(satoshi, script, prevtx_hash, outidx)) wifs.append(chain0.subkey(keynum, as_private=True).wif()) change_addr = chain1.subkey(change_keynum).bitcoin_address() payables = [(out_addr, out_satoshi), (change_addr, change_satoshi)] print() print(self.PAD.format("Make reference signature")) signed_tx = create_signed_tx(spendables, payables, wifs, fee_satoshi) signed_tx = self.get_tx_bytes(signed_tx) print(self.PAD.format("Get signed tx"), end='', flush=True) # <--- get the signed tx from Polly polly_signed_tx = self.polly.send_get_signed_tx() #print(self.txstr(polly_signed_tx)) #print(self.txstr(signed_tx)) print(self.__outok()) # Compare reference wallet signed tx with polly's assert signed_tx == polly_signed_tx, "test_sign: signature mismatch\nExpected:\n" + self.hexstr( signed_tx) + "\n\nActual:\n" + self.hexstr(polly_signed_tx)
def transaction (tx_info, encoding, unittest=False, public_key_hex=None, allow_unconfirmed_inputs=False): if len(tx_info) == 3: source, destination_outputs, data = tx_info fee_provided = 0 else: source, destination_outputs, data, fee_provided = tx_info if not isinstance(fee_provided, int): raise exceptions.TransactionError('Fee provided must be in satoshis') if encoding not in ('pubkeyhash', 'multisig', 'opreturn'): raise exceptions.TransactionError('Unknown encoding‐scheme.') # If public key is necessary for construction of (unsigned) transaction, # either use the public key provided, or derive it from a private key # retrieved from wallet. public_key = None if encoding in ('multisig', 'pubkeyhash'): # If no public key was provided, derive from private key. if not public_key_hex: # Get private key. if unittest: private_key_wif = 'cPdUqd5EbBWsjcG9xiL1hz8bEyGFiz4SW99maU9JgpL9TEcxUf3j' else: private_key_wif = rpc('dumpprivkey', [source]) # Derive public key. public_key_hex = private_key_to_public_key(private_key_wif) pubkeypair = bitcoin_utils.parse_as_public_pair(public_key_hex) public_key = public_pair_to_sec(pubkeypair, compressed=True) # Protocol change. if encoding == 'pubkeyhash' and get_block_count() < 293000 and not config.TESTNET: raise exceptions.TransactionError('pubkeyhash encoding unsupported before block 293000') if config.PREFIX == config.UNITTEST_PREFIX: unittest = True # Validate source and all destination addresses. destinations = [address for address, value in destination_outputs] for address in destinations + [source]: if address: try: base58_decode(address, config.ADDRESSVERSION) except Exception: # TODO raise exceptions.AddressError('Invalid Bitcoin address:', address) # Check that the source is in wallet. if not unittest and encoding in ('multisig') and not public_key: if not rpc('validateaddress', [source])['ismine']: raise exceptions.AddressError('Not one of your Bitcoin addresses:', source) # Check that the destination output isn't a dust output. # Set null values to dust size. new_destination_outputs = [] for address, value in destination_outputs: if encoding == 'multisig': if value == None: value = config.MULTISIG_DUST_SIZE if not value >= config.MULTISIG_DUST_SIZE: raise exceptions.TransactionError('Destination output is below the dust target value.') else: if value == None: value = config.REGULAR_DUST_SIZE if not value >= config.REGULAR_DUST_SIZE: raise exceptions.TransactionError('Destination output is below the dust target value.') new_destination_outputs.append((address, value)) destination_outputs = new_destination_outputs # Divide data into chunks. if data: def chunks(l, n): """ Yield successive n‐sized chunks from l. """ for i in range(0, len(l), n): yield l[i:i+n] if encoding == 'pubkeyhash': data_array = list(chunks(data + config.PREFIX, 20 - 1)) # Prefix is also a suffix here. elif encoding == 'multisig': data_array = list(chunks(data, 33 - 1)) elif encoding == 'opreturn': data_array = list(chunks(data, 80)) assert len(data_array) == 1 # Only one OP_RETURN output currently supported (messages should all be shorter than 80 bytes, at the moment). else: data_array = [] # Calculate total BTC to be sent. btc_out = 0 if encoding == 'multisig': data_value = config.MULTISIG_DUST_SIZE elif encoding == 'opreturn': data_value = config.OP_RETURN_VALUE else: data_value = config.REGULAR_DUST_SIZE # Pay‐to‐PubKeyHash btc_out = sum([data_value for data_chunk in data_array]) btc_out += sum([value for address, value in destination_outputs]) # Get size of outputs. if encoding == 'multisig': data_output_size = 81 # 71 for the data elif encoding == 'opreturn': data_output_size = 90 # 80 for the data else: data_output_size = 25 + 9 # Pay‐to‐PubKeyHash (25 for the data?) outputs_size = ((25 + 9) * len(destination_outputs)) + (len(data_array) * data_output_size) # Get inputs. unspent = get_unspent_txouts(source, normalize=True, unittest=unittest, allow_unconfirmed_inputs=allow_unconfirmed_inputs) inputs, btc_in = [], 0 change_quantity = 0 sufficient_funds = False final_fee = config.FEE_PER_KB for coin in sorted(unspent,key=lambda x:input_value_weight(x['amount'])): inputs.append(coin) btc_in += round(coin['amount'] * config.UNIT) size = 181 * len(inputs) + outputs_size + 10 necessary_fee = (int(size / 10000) + 1) * config.FEE_PER_KB final_fee = max(fee_provided, necessary_fee) change_quantity = btc_in - (btc_out + final_fee) if change_quantity == 0 or change_quantity >= config.REGULAR_DUST_SIZE: # If change is necessary, must not be a dust output. sufficient_funds = True break if not sufficient_funds: # Approximate needed change, fee by with most recently calculated quantities. total_btc_out = btc_out + max(change_quantity, 0) + final_fee raise exceptions.BalanceError('Insufficient bitcoins at address {}. (Need approximately {} BTC.)'.format(source, total_btc_out / config.UNIT)) # Construct outputs. if data: data_output = (data_array, data_value) else: data_output = None if change_quantity: change_output = (source, change_quantity) else: change_output = None # Serialise inputs and outputs. transaction = serialise(encoding, inputs, destination_outputs, data_output, change_output, source=source, public_key=public_key) unsigned_tx_hex = binascii.hexlify(transaction).decode('utf-8') return unsigned_tx_hex
def sec(self): """Take a point on the curve and encode it into sec format (33 bytes) """ return public_pair_to_sec((self.x(), self.y()))
def serialise (encoding, inputs, destination_outputs, data_output=None, change_output=None, source=None, pubkey=None): s = (1).to_bytes(4, byteorder='little') # Version # Number of inputs. s += var_int(int(len(inputs))) # List of Inputs. for i in range(len(inputs)): txin = inputs[i] s += binascii.unhexlify(bytes(txin['txid'], 'utf-8'))[::-1] # TxOutHash s += txin['vout'].to_bytes(4, byteorder='little') # TxOutIndex script = binascii.unhexlify(bytes(txin['scriptPubKey'], 'utf-8')) s += var_int(int(len(script))) # Script length s += script # Script s += b'\xff' * 4 # Sequence # Number of outputs. n = 0 n += len(destination_outputs) if data_output: data_array, value = data_output for data_chunk in data_array: n += 1 else: data_array = [] if change_output: n += 1 s += var_int(n) # Destination output. for address, value in destination_outputs: pubkeyhash = base58_decode(address, config.ADDRESSVERSION) s += value.to_bytes(8, byteorder='little') # Value script = OP_DUP # OP_DUP script += OP_HASH160 # OP_HASH160 script += op_push(20) # Push 0x14 bytes script += pubkeyhash # pubKeyHash script += OP_EQUALVERIFY # OP_EQUALVERIFY script += OP_CHECKSIG # OP_CHECKSIG s += var_int(int(len(script))) # Script length s += script # Data output. for data_chunk in data_array: data_array, value = data_output # DUPE s += value.to_bytes(8, byteorder='little') # Value # Get source public key (either provided as a string or derived from a private key in the wallet). if encoding in ('multisig', 'pubkeyhash'): if pubkey: pubkeypair = bitcoin_utils.parse_as_public_pair(pubkey) source_pubkey = public_pair_to_sec(pubkeypair, compressed=True) else: if config.PREFIX == config.UNITTEST_PREFIX: private_key_wif = 'cPdUqd5EbBWsjcG9xiL1hz8bEyGFiz4SW99maU9JgpL9TEcxUf3j' else: private_key_wif = rpc('dumpprivkey', [source]) if private_key_wif[0] == 'c': testnet = True else: testnet = False secret_exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(private_key_wif, is_test=testnet) public_pair = public_pair_for_secret_exponent(generator_secp256k1, secret_exponent) source_pubkey = public_pair_to_sec(public_pair, compressed=compressed) if encoding == 'multisig': # Get data (fake) public key. pad_length = 33 - 1 - len(data_chunk) assert pad_length >= 0 data_pubkey = bytes([len(data_chunk)]) + data_chunk + (pad_length * b'\x00') # Construct script. script = OP_1 # OP_1 script += op_push(len(source_pubkey)) # Push bytes of source public key script += source_pubkey # Source public key script += op_push(len(data_pubkey)) # Push bytes of data chunk (fake) public key script += data_pubkey # Data chunk (fake) public key script += OP_2 # OP_2 script += OP_CHECKMULTISIG # OP_CHECKMULTISIG elif encoding == 'opreturn': script = OP_RETURN # OP_RETURN script += op_push(len(data_chunk)) # Push bytes of data chunk (NOTE: OP_SMALLDATA?) script += data_chunk # Data chunk elif encoding == 'pubkeyhash': pad_length = 20 - 1 - len(data_chunk) assert pad_length >= 0 obj1 = ARC4.new(binascii.unhexlify(inputs[0]['txid'])) # Arbitrary, easy‐to‐find, unique key. pubkeyhash = bytes([len(data_chunk)]) + data_chunk + (pad_length * b'\x00') pubkeyhash_encrypted = obj1.encrypt(pubkeyhash) # Construct script. script = OP_DUP # OP_DUP script += OP_HASH160 # OP_HASH160 script += op_push(20) # Push 0x14 bytes script += pubkeyhash_encrypted # pubKeyHash script += OP_EQUALVERIFY # OP_EQUALVERIFY script += OP_CHECKSIG # OP_CHECKSIG else: raise exceptions.TransactionError('Unknown encoding‐scheme.') s += var_int(int(len(script))) # Script length s += script # Change output. if change_output: address, value = change_output pubkeyhash = base58_decode(address, config.ADDRESSVERSION) s += value.to_bytes(8, byteorder='little') # Value script = OP_DUP # OP_DUP script += OP_HASH160 # OP_HASH160 script += op_push(20) # Push 0x14 bytes script += pubkeyhash # pubKeyHash script += OP_EQUALVERIFY # OP_EQUALVERIFY script += OP_CHECKSIG # OP_CHECKSIG s += var_int(int(len(script))) # Script length s += script s += (0).to_bytes(4, byteorder='little') # LockTime return s