Sure, to be fair, most of this code was from Ken Sherrif's bitcoins the hard way blog and the linked repo. I did some modifications myself to make it generate both the compressed and compressed versions and I started modifying it to work on testnet. Here's the entire script I've been playing with. You can see that if you just run it it's going to generate a random bitcoin address. However, if you do as we discussed before, calling it with python -i then you can call the methods you're interested in one-at-time. Or, you could make your own script at the bottom. Or, you could go ahead and convert it into a python module. In any case, here's all the methods I've been using/playing around with.
The method you're looking for are privateKeyToPublicKey, see the bottom of the script for how I call it.
#!/usr/bin/env python2.7
# for my education, following along with bitcoins the hard way blog post:
# http://www.righto.com/2014/02/bitcoins-hard-way-using-raw-bitcoin.html
import random
import hashlib
import ecdsa
import struct
b58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
# Input is a hex-encoded, DER-encoded signature
# Output is a 64-byte hex-encoded signature
def derSigToHexSig(s):
s, junk = ecdsa.der.remove_sequence(s.decode('hex'))
if junk != '':
print 'JUNK', junk.encode('hex')
assert(junk == '')
x, s = ecdsa.der.remove_integer(s)
y, s = ecdsa.der.remove_integer(s)
return '%064x%064x' % (x, y)
# Substitutes the scriptPubKey into the transaction, appends SIGN_ALL to make
# the version of the transaction that can be signed
def getSignableTxn(parsed):
first, sig, pub, rest = parsed
inputAddr = base58CheckDecode(pubKeyToAddr(pub))
return first + "1976a914" + inputAddr.encode('hex') + "88ac" + rest + "01000000"
# Returns [first, sig, pub, rest]
def parseTxn(txn):
first = txn[0:41*2]
scriptLen = int(txn[41*2:42*2], 16)
script = txn[42*2:42*2+2*scriptLen]
sigLen = int(script[0:2], 16)
sig = script[2:2+sigLen*2]
pubLen = int(script[2+sigLen*2:2+sigLen*2+2], 16)
pub = script[2+sigLen*2+2:]
assert(len(pub) == pubLen*2)
rest = txn[42*2+2*scriptLen:]
return [first, sig, pub, rest]
# Verifies that a transaction is properly signed, assuming the generated scriptPubKey matches
# the one in the previous transaction's output
def verifyTxnSignature(txn):
parsed = parseTxn(txn)
signableTxn = getSignableTxn(parsed)
hashToSign = hashlib.sha256(hashlib.sha256(signableTxn.decode('hex')).digest()).digest().encode('hex')
assert(parsed[1][-2:] == '01') # hashtype
sig = keyUtils.derSigToHexSig(parsed[1][:-2])
public_key = parsed[2]
vk = ecdsa.VerifyingKey.from_string(public_key[2:].decode('hex'), curve=ecdsa.SECP256k1)
assert(vk.verify_digest(sig.decode('hex'), hashToSign.decode('hex')))
# Verifies that a transaction is properly signed, assuming the generated scriptPubKey matches
# the one in the previous transaction's output
def verifyTxnSignature(txn):
parsed = parseTxn(txn)
signableTxn = getSignableTxn(parsed)
hashToSign = hashlib.sha256(hashlib.sha256(signableTxn.decode('hex')).digest()).digest().encode('hex')
assert(parsed[1][-2:] == '01') # hashtype
sig = derSigToHexSig(parsed[1][:-2])
public_key = parsed[2]
vk = ecdsa.VerifyingKey.from_string(public_key[2:].decode('hex'), curve=ecdsa.SECP256k1)
assert(vk.verify_digest(sig.decode('hex'), hashToSign.decode('hex')))
# Takes and returns byte string value, not hex string
def varstr(s):
return varint(len(s)) + s
# Returns byte string value, not hex string
def varint(n):
if n < 0xfd: return struct.pack(' elif n < 0xffff: return struct.pack(' elif n < 0xffffffff: return struct.pack(' else: return struct.pack('
def base58encode(n):
result = ''
while n > 0:
result = b58[n%58] + result
n /= 58
return result
def base58decode(s):
result = 0
for i in range(0, len(s)):
result = result * 58 + b58.index(s[i])
return result
def base256encode(n):
result = ''
while n > 0:
result = chr(n % 256) + result
n /= 256
return result
def base256decode(s):
result = 0
for c in s:
result = result * 256 + ord(c)
return result
def countLeadingChars(s, ch):
count = 0
for c in s:
if c == ch:
count += 1
else:
break
return count
# https://en.bitcoin.it/wiki/Base58Check_encoding
def base58CheckEncode(version, payload):
s = chr(version) + payload
checksum = hashlib.sha256(hashlib.sha256(s).digest()).digest()[0:4]
result = s + checksum
leadingZeros = countLeadingChars(result, '\0')
return '1' * leadingZeros + base58encode(base256decode(result))
def base58CheckDecode(s):
leadingOnes = countLeadingChars(s, '1')
s = base256encode(base58decode(s))
result = '\0' * leadingOnes + s[:-4]
chk = s[-4:]
checksum = hashlib.sha256(hashlib.sha256(result).digest()).digest()[0:4]
assert(chk == checksum)
version = result[0]
return result[1:]
def privateKeyToWif(key_hex, compressed=False):
if compressed:
key_hex=key_hex+'01'
return base58CheckEncode(0x80, key_hex.decode('hex'))
def wifToPrivateKey(s):
b = base58CheckDecode(s)
return b.encode('hex')
def privateKeyToPublicKey(s, compressed=False):
sk = ecdsa.SigningKey.from_string(s.decode('hex'), curve=ecdsa.SECP256k1)
vk = sk.verifying_key
if compressed:
from ecdsa.util import number_to_string
order = vk.pubkey.order
# print "order", order
x_str = number_to_string(vk.pubkey.point.x(), order).encode('hex')
# print "x_str", x_str
sign = '02' if vk.pubkey.point.y() % 2 == 0 else '03'
# print "sign", sign
return (sign+x_str)
else:
return ('\04' + vk.to_string()).encode('hex')
def pubKeyToAddr(s,testnet=False):
ripemd160 = hashlib.new('ripemd160')
ripemd160.update(hashlib.sha256(s.decode('hex')).digest())
if testnet:
return base58CheckEncode(0x6F, ripemd160.digest())
return base58CheckEncode(0, ripemd160.digest())
def makeRawTransaction(outputTransactionHash, sourceIndex, scriptSig, outputs):
def makeOutput(data):
redemptionSatoshis, outputScript = data
return (struct.pack(" '%02x' % len(outputScript.decode('hex')) + outputScript)
formattedOutputs = ''.join(map(makeOutput, outputs))
return (
"01000000" + # 4 bytes version
"01" + # variant for number of inputs
outputTransactionHash.decode('hex')[::-1].encode('hex') + # reverse OutputTransactionHash
struct.pack(' '%02x' % len(scriptSig.decode('hex')) + scriptSig +
"ffffffff" + # sequence
"%02x" % len(outputs) + # number of outputs
formattedOutputs +
"00000000" # lockTime
)
def makeSignedTransaction(privateKey, outputTransactionHash, sourceIndex, scriptPubKey, outputs, compressed=False):
myTxn_forSig = (makeRawTransaction(outputTransactionHash, sourceIndex, scriptPubKey, outputs) + "01000000") # hash code
s256 = hashlib.sha256(hashlib.sha256(myTxn_forSig.decode('hex')).digest()).digest()
sk = ecdsa.SigningKey.from_string(privateKey.decode('hex'), curve=ecdsa.SECP256k1)
sig = sk.sign_digest(s256, sigencode=ecdsa.util.sigencode_der) + '\01' # 01 is hashtype
pubKey = privateKeyToPublicKey(privateKey,compressed)
scriptSig = varstr(sig).encode('hex') + varstr(pubKey.decode('hex')).encode('hex')
signed_txn = makeRawTransaction(outputTransactionHash, sourceIndex, scriptSig, outputs)
# verifyTxnSignature(signed_txn)
return signed_txn
def addrHashToScriptPubKey(b58str):
assert(len(b58str) == 34)
# 76 A9 14 (20 bytes) 88 AC
return '76a914' + base58CheckDecode(b58str).encode('hex') + '88ac'
# private_key = hex(i)
#for i in range(1,100):
# private_key = hex(i)[2:].zfill(64)
# print privateKeyToWif(private_key), "2011-09-04T00:00:01Z"
import sys
import getopt
private_key = None
try:
opts, args = getopt.gnu_getopt(sys.argv[1:], "dtr:", ["dec","testnet","mrt"])
except getopt.GetoptError as err:
print(err)
sys.exit(2)
testnet=False
deckey=False
mrt=False
for o,a in opts:
if o in ("-t", "--testnet"):
testnet=True
if o in ("-d","--dec"):
deckey=True
if o in ("-r","--mrt"):
mrt=True
if len(args)>0:
if deckey:
private_key = args[0].zfill(64)
else:
private_key = '%064x' % int(args[0],16)
else: private_key = ''.join(['%x' % random.randrange(16) for x in range(0,64)])
print "A private key: ", private_key
print "The uncompressed WIF: ",privateKeyToWif(private_key)
print "The WIF: ",privateKeyToWif(private_key, compressed=True)
public_key = privateKeyToPublicKey(private_key)
cpublic_key = privateKeyToPublicKey(private_key,compressed=True)
print "The uncompressed bitcoin pubkey: ", public_key
print "The bitcoin pubkey: ", cpublic_key
print "The uncompressed bitcoin address: ", pubKeyToAddr(public_key,testnet=testnet)
print "The bitcoin address: ", pubKeyToAddr(cpublic_key,testnet=testnet)