import hashlib
import ecdsa
from coinbits.encoding import b58encode, b58decode
from coinbits.txns.exceptions import KeyDecodeError
[docs]class PublicKey(object):
"""
This is a representation for Bitcoin public keys. In this
class you'll find methods to import/export keys from multiple
formats. Use a hex string representation to construct a new
public key or use the clas methods to import from another format.
"""
key_prefix = '\x04'
def __init__(self, hexkey):
"""
Initialize a public key object. Requires an existing version
of this key in hex.
Args:
hexkey: The key in hex string format
"""
stringkey = hexkey.decode("hex")[1:]
self.public_key = ecdsa.VerifyingKey.from_string(stringkey,
curve=ecdsa.SECP256k1)
@classmethod
[docs] def from_private_key(klass, private_key):
"""
This class method will create a new PublicKey based on a
PrivateKey.
Args:
private_key: The PrivateKey
Returns:
A new PublicKey
"""
public_key = private_key.private_key.get_verifying_key()
hexkey = (klass.key_prefix + public_key.to_string()).encode("hex")
return klass(hexkey)
[docs] def verify(self, signature, message):
"""
Verify the given signature of the message. Returns True if
verification is successful, False otherwise.
"""
digest = hashlib.sha256(hashlib.sha256(message).digest()).digest()
return self.public_key.verify_digest(signature[:-1], digest, sigdecode=ecdsa.util.sigdecode_der)
[docs] def to_hex(self):
"""
This method will convert the public key to
a hex string representation.
Returns:
A hex string representation of the public key
"""
hexkey = self.public_key.to_string().encode("hex")
return self.key_prefix.encode("hex") + hexkey.upper()
[docs] def to_address(self):
"""
This method will convert the public key to a bitcoin address.
Returns:
A bitcoin address for the public key
"""
sha256digest = hashlib.sha256(str(self)).digest()
ripemd160 = hashlib.new('ripemd160')
ripemd160.update(sha256digest)
ripemd160_digest = ripemd160.digest()
# Prepend the version info
ripemd160_digest = '\x00' + ripemd160_digest
# Calc checksum
checksum = hashlib.sha256(ripemd160_digest).digest()
checksum = hashlib.sha256(checksum).digest()
checksum = checksum[:4]
# Append checksum
address = ripemd160_digest + checksum
address_bignum = int('0x' + address.encode('hex'), 16)
return '1' + b58encode(address_bignum)
def __repr__(self):
return "<PublicKey address=[%s]>" % self.to_address()
def __eq__(self, other):
return self.to_hex() == other.to_hex()
[docs] def __str__(self):
"""
This method will convert the public key to
a string representation.
Returns:
A string representation of the public key
"""
return self.key_prefix + self.public_key.to_string()
[docs]class PrivateKey(object):
"""
This is a representation for Bitcoin private keys. In this
class you'll find methods to import/export keys from multiple
formats. Use a hex string representation to construct a new PublicKey
or use the clas methods to import from another format.
"""
wif_prefix = '\x80'
def __init__(self, hexkey=None):
"""
Construct a new PrivateKey object, based optionally on an existing
hex representation.
Args:
hexkey: The key in hex string format. If one isn't
provided, a new private key will be generated.
"""
if hexkey:
stringkey = hexkey.decode("hex")
self.private_key = \
ecdsa.SigningKey.from_string(stringkey, curve=ecdsa.SECP256k1)
else:
self.private_key = \
ecdsa.SigningKey.generate(curve=ecdsa.SECP256k1)
@classmethod
[docs] def from_string(klass, stringkey):
"""
This method will create a new Private Key using
the specified string data.
Args:
stringkey: The key in string format
Returns:
A new PrivateKey
"""
return klass(stringkey.encode("hex"))
@classmethod
[docs] def from_wif(klass, wifkey):
"""
This method will create a new PrivateKey from a
WIF format string.
Args:
wifkey: The private key in WIF format
Returns:
A new PrivateKey
"""
hexkey = "%x" % b58decode(wifkey)
checksum = hexkey[(-4 * 2):].decode("hex")
key = hexkey[:(-4 * 2)].decode("hex")
shafirst = hashlib.sha256(key).digest()
shasecond = hashlib.sha256(shafirst).digest()
if shasecond[:4] != checksum:
raise KeyDecodeError("Invalid checksum for the address.")
return klass(key[1:].encode("hex"))
[docs] def to_hex(self):
"""
This method will convert the Private Key to
a hex string representation.
Returns:
Hex string representation of this PrivateKey
"""
hexkey = self.private_key.to_string().encode("hex")
return hexkey.upper()
[docs] def to_wif(self):
"""
This method will export the Private Key to
WIF (Wallet Import Format).
Returns:
The PrivateKey in WIF format.
"""
extendedkey = self.wif_prefix + str(self)
shafirst = hashlib.sha256(extendedkey).digest()
shasecond = hashlib.sha256(shafirst).digest()
checksum = shasecond[:4]
extendedkey = extendedkey + checksum
key_bignum = int('0x' + extendedkey.encode('hex'), 16)
return b58encode(key_bignum)
[docs] def to_address(self):
"""
Convert to public key and then get the public address for that key.
"""
return self.get_public_key().to_address()
[docs] def sign(self, data):
"""Digest and then sign the data."""
digest = hashlib.sha256(hashlib.sha256(data).digest()).digest()
sig = self.private_key.sign_digest(digest, sigencode=ecdsa.util.sigencode_der)
# 01 is hashtype
return sig + '\01'
[docs] def get_public_key(self):
"""
This method will create a new PublicKey based on this PrivateKey.
Returns:
A new PublicKey
"""
return PublicKey.from_private_key(self)
def __eq__(self, other):
return self.to_hex() == other.to_hex()
[docs] def __str__(self):
"""
This method will convert the PrivateKey to
a string representation.
"""
return self.private_key.to_string()
def __repr__(self):
return "<PrivateKey hexkey=[%s]>" % self.to_hex()