Compute compressed bitcoin address from private key - python

I am starting to get an overview about bitcoin and wanted to write a very simple programme converting private keys into public, keys, addresses etc. However, for some private keys it suddenly fails when I try to compute the compressed address for them. The reason it fails as far as I can see is that the compressed public key I am passing to the function bitcoin.pubkey_to_address() is one digit too short. As it is one of the most famous bitcoin libraries I assume that there is a fundamental error in my understanding. Thus, the question: what am I doing wrong in computing the compressed bitcoin address from a private key?
I have installed the following library: pip3 install bitcoin in the minimal example below, I am using Python 3.8.5 on Ubuntu 20.04.
import bitcoin
class WalletWithBalance:
def __init__(self, _private_key: str):
self.private_key_uncompressed_hex: str = _private_key
# public keys:
self.public_key_uncompressed_as_x_y_tuple_hex = self.get_private_key_as_x_y_tuple()
self.public_key_compressed_hex = self.get_compressed_public_key_hex()
# addresses:
self.address_compressed = bitcoin.pubkey_to_address(self.public_key_compressed_hex)
def get_public_key_as_raw_hex_str(self):
public_key_as_raw_hex_str = bitcoin.encode_pubkey(self.public_key_uncompressed_as_x_y_tuple_hex, 'hex')
return public_key_as_raw_hex_str
def get_private_key_as_x_y_tuple(self):
private_key_raw_decimal_number = bitcoin.decode_privkey(self.private_key_uncompressed_hex, 'hex')
return bitcoin.fast_multiply(bitcoin.G, private_key_raw_decimal_number)
def get_compressed_public_key_hex(self):
(public_key_x, public_key_y) = self.public_key_uncompressed_as_x_y_tuple_hex
return self.get_compressed_prefix(public_key_y) + bitcoin.encode(public_key_x, 16)
#staticmethod
def get_compressed_prefix(public_key_y):
if public_key_y % 2 == 0:
return "02"
else:
return "03"
if __name__ == "__main__":
wallet = WalletWithBalance(_private_key="0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c")
The stacktrace reads:
/usr/bin/python3 /foo/minimal_example.py
Traceback (most recent call last):
File "/foo/minimal_example.py", line 36, in <module>
wallet = WalletWithBalance(_private_key="0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c")
File "/foo/minimal_example.py", line 13, in __init__
self.address_compressed = bitcoin.pubkey_to_address(self.public_key_compressed_hex)
File "/DoeJohn/.local/lib/python3.8/site-packages/bitcoin/main.py", line 452, in pubkey_to_address
return bin_to_b58check(bin_hash160(pubkey), magicbyte)
File "/DoeJohn/.local/lib/python3.8/site-packages/bitcoin/main.py", line 334, in bin_hash160
intermed = hashlib.sha256(string).digest()
TypeError: Unicode-objects must be encoded before hashing

Related

'bytes' object has no attribute 'oid'

my code has an error in line 14 saying bytes object has no attribute oid. I am not sure why is it giving me this error.
from Crypto.PublicKey import DSA
from Crypto.Signature import DSS
import random
import os
def generate_dsa_key_pair(bits=1024):
"""Generates a DSA key pair with the given number of bits."""
key = DSA.generate(bits)
return key
def sign_text(text, key):
"""Signs the given text using the given DSA private key."""
signer = DSS.new(key, 'fips-186-3')
**signature = signer.sign(text.encode())**
return signature
def create_p2ms_script(n, m):
"""Creates a P2MS script using the given number of public keys and signatures."""
# Generate N DSA key pairs
keys = [generate_dsa_key_pair() for i in range(n)]
# Select M private keys from the N keys
priv_keys = random.sample(keys, m)
# Generate M signatures using the private keys
signatures = [sign_text("help me", priv_key) for priv_key in priv_keys]
# Create scriptPubKey by concatenating the N public keys
scriptPubKey = b''.join([key.publickey().export_key(format='DER') for key in keys])
# Create scriptSig by concatenating the M signatures
scriptSig = b''.join(signatures)
return scriptPubKey, scriptSig
def save_script_to_file(script, filename):
"""Saves the given script to the specified file."""
with open(filename, 'wb') as f:
f.write(script)
def execute_p2ms_script(scriptPubKey, scriptSig):
"""Executes the given P2MS script by verifying the signatures using the public keys."""
# Split scriptPubKey into individual public keys
pub_keys = [DSA.import_key(key) for key in scriptPubKey.split(b'\x00\x02\x81\x81') if key]
# Split scriptSig into individual signatures
signatures = [sig for sig in scriptSig.split(b'\x00\x02\x81\x81') if sig]
# Check if the number of signatures matches the number of public keys
if len(signatures) != len(pub_keys):
return False
# Verify each signature using the corresponding public key
for i, sig in enumerate(signatures):
verifier = DSS.new(pub_keys[i], 'fips-186-3')
if not verifier.verify("help me ".encode(), sig):
return False
return True
if __name__ == '__main__':
n = int(input("Enter the number of public keys (N): "))
m = int(input("Enter the number of signatures (M): "))
# Create P2MS script
scriptPubKey, scriptSig = create_p2ms_script(n, m)
# Save script
I have tried to hash the object but then my code wouldn't work. I have bolded the line, can someone please explain to me?
for context, my code is meant to replicate a P2MS function.
edit: the full traceback is as follows:
Exception has occurred: AttributeError
'bytes' object has no attribute 'oid'
File ", line 14, in sign_text signature = signer.sign(text.encode())
File , line 26, in <listcomp> signatures = [sign_text("help me", priv_key) for priv_key in priv_keys]
File , line 26, in create_p2ms_script signatures = [sign_text("help me", priv_key) for priv_key in priv_keys]
File , line 66, in <module> scriptPubKey, scriptSig = create_p2ms_script(n, m)
AttributeError: 'bytes' object has no attribute 'oid'
The bytes object is what's returned by text.encode(). It's a UTF-8 encoded string and it does not have an attribute oid.
That is not what DSS.sign(msg_hash) expects. It expects a hash object (which conveniently has an attribute oid), see also PyCryptoDome doc on sign. So a Crypto.Hash object has to be created first as described here:
from Crypto.Hash import SHA256
# ... line 14
hash_object = SHA256.new(data=text.encode())
signature = signer.sign(hash_object)
In your question, you said that passing a hash object did not work. However, that's the proper way to do it. You cannot make a wish on an API and expect things to work. See the API as a contract. It specifies what you need to give and what you get in return.
Make your code run without throwing exceptions. Once you're there and you still encounter errors after passing a hash, please post another question. A thorough explanation of expected and actual behavior beyond "would not work" would also be helpful.

Python fastecdsa/ecdsa signing error, cannot convert integer to bits

I am trying to implement ScroogeCoin using the fastecdsa library. I currently run into an error that happens when my create_coins function is called. The error points to the signing function (tx["signature"]) and says that it cannot convert an integer type to a byte.
import hashlib
import json
from fastecdsa import keys, curve, ecdsa
class ScroogeCoin(object):
def __init__(self):
self.private_key, self.public_key = keys.gen_keypair(curve.secp256k1)
self.address = hashlib.sha256(json.dumps(self.public_key.x).encode()).hexdigest()
self.chain = []
self.current_transactions = []
def create_coins(self, receivers: dict):
"""
Scrooge adds value to some coins
:param receivers: {account:amount, account:amount, ...}
"""
tx = {
"sender" : self.address, # address,
# coins that are created do not come from anywhere
"location": {"block": -1, "tx": -1},
"receivers" : receivers,
}
tx["hash"] = hashlib.sha256(json.dumps(tx).encode()).hexdigest()# hash of tx
tx["signature"] = ecdsa.sign(self.private_key, tx["hash"])# signed hash of tx
self.current_transactions.append(tx)
...
When this function is ran in the main function:
...
Scrooge = ScroogeCoin()
users = [User(Scrooge) for i in range(10)]
Scrooge.create_coins({users[0].address:10, users[1].address:20, users[3].address:50})
...
It produces this error:
Traceback (most recent call last):
File "D:\Scrooge_coin_assignmnet.py", line 216, in <module>
main()
File "D:\Scrooge_coin_assignmnet.py", line 197, in main
Scrooge.create_coins({users[0].address:10, users[1].address:20, users[3].address:50})
File "D:\Scrooge_coin_assignmnet.py", line 27, in create_coins
tx["signature"] = ecdsa.sign(self.private_key, tx["hash"])# signed hash of tx
File "C:\Users\d\AppData\Local\Programs\Python\Python311\Lib\site-packages\fastecdsa\ecdsa.py", line 36, in sign
rfc6979 = RFC6979(msg, d, curve.q, hashfunc, prehashed=prehashed)
File "C:\Users\d\AppData\Local\Programs\Python\Python311\Lib\site-packages\fastecdsa\util.py", line 25, in __init__
self.msg = msg_bytes(msg)
File "C:\Users\d\AppData\Local\Programs\Python\Python311\Lib\site-packages\fastecdsa\util.py", line 153, in msg_bytes
raise ValueError('Msg "{}" of type {} cannot be converted to bytes'.format(
ValueError: Msg "21783419755125685845542189331366569080312572314742637241373298325693730090205" of type <class 'int'> cannot be converted to bytes
I've tried to play around and change it to a byte by using encode on the tx["hash"] as well things like bytes.fromhex() but it still gives the same error. I wanted to ask others who are more skilled and see if they can see how I am messing up.

ecdsa signing key format

I am trying to create a public/private key pair using python.
I have created a private key using the following method:
private_key = ''.join(['%x' % random.randrange(16) for x in range(0, 64)])
using this private key I have attempted to use a ecdsa graph to generate to corresponding public key
def privateKeyToPublicKey(s):
sk = ecdsa.SigningKey.from_string(s, curve=ecdsa.SECP256k1)
vk = sk.verifying_key
return ('\04' + sk.verifying_key.to_string())
I have not been able to create the signing key (sk) due to a formatting error where my string is in the wrong format. But I am not sure how/what format the string s should be for SigningKey to work.
I get the following error when running the script:
Traceback (most recent call last):
File "address.py", line 23, in <module>
privateKeyToPublicKey(private_key)
File "address.py", line 20, in privateKeyToPublicKey
sk = ecdsa.SigningKey.from_string(s, curve=ecdsa.SECP256k1)
File "/usr/local/lib/python3.6/dist-packages/ecdsa/keys.py", line
149, in from_string
assert len(string) == curve.baselen, (len(string), curve.baselen)
AssertionError: (64, 32)
Here's a more complete code sample for clarity about the answer. Python3.
from ecdsa import SigningKey, SECP256k1
import sha3, random, binascii
private_key = ''.join(['%x' % random.randrange(16) for x in range(0, 64)])
private_key = bytes(private_key, 'utf-8')
private_key = binascii.unhexlify(private_key)
priv = SigningKey.from_string(private_key, curve=SECP256k1)
pub = priv.get_verifying_key().to_string()
keccak = sha3.keccak_256()
keccak.update(pub)
address = keccak.hexdigest()[24:]
print(address, priv.to_string().hex())
I have realised my error, the input must be in bytes, the private key in hex format. In python2 you may use:
private_key.decode('hex')
or you may use
binascii.unhexlify
in python3

Get artist name

I'm trying to get the names of my top 3 artists of last week with pylast (https://github.com/pylast/pylast) but I run into an error or get I get None as a result and I don't see what I'm doing wrong. pylast is a Python interface to Last.fm.
My code:
import pylast
API_KEY = ""
API_SECRET = ""
username = ""
password_hash = pylast.md5("")
network = pylast.LastFMNetwork(api_key=API_KEY, api_secret=API_SECRET, username=username, password_hash=password_hash)
user = network.get_authenticated_user();
weekly_artists = user.get_weekly_artist_charts();
# Keep the first three artists.
del weekly_artists[3:]
# Print the artist name and number of songs(weight).
for weekly_artist in weekly_artists:
artist,weight = weekly_artist
print (artist.get_name())
print (artist.get_correction())
artist.get_name() returns
None
artist.get_correction() returns
Traceback (most recent call last):
File "G:\projects\python\lastfm_weekly\lastfm-weekly.py", line 28, in <module>
print (artist.get_correction())
File "C:\Users\..\Python\Python36-32\lib\site-packages\pylast\__init__.py", line 1585, in get_correction
self._request(self.ws_prefix + ".getCorrection"), "name")
File "C:\Users\..\Python\Python36-32\lib\site-packages\pylast\__init__.py", line 1029, in _request
return _Request(self.network, method_name, params).execute(cacheable)
File "C:\Users\..\Python\Python36-32\lib\site-packages\pylast\__init__.py", line 744, in __init__
network._get_ws_auth()
AttributeError: 'str' object has no attribute '_get_ws_auth'
What am I doing wrong?
Here is a quick and dirty solution, i'm sure someone will provide something better but i just installed the package to test and it works.
network = pylast.LastFMNetwork(api_key=API_KEY, api_secret=API_SECRET)
artists = network.get_top_artists()
del artists[:3]
for i in artists:
artist, weight = i
print('Artist = {}. Weight = {}'.format(artist, weight))
I'm not really familiar with the package, I just installed it to help out with this but I do wonder what "get_name()" and "get_correction()" are as they're not in your provided code.
If they're not functions you created / are defined within your code then I'd look there for the problem.
Also, you're authenticating the user but the documentation explicitly states you don't need to unless you're writing data.

PyCrypto : AssertionError("PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()")

I am creating various processes that do different tasks. One of them and only one of them, has a security module that creates the PyCrypto objects.
So my program starts, creates the various processes, the process that handles messages uses the security module to decrypt and I get the following errors:
firstSymKeybin = self.cipher.decrypt(encFirstSymKeybin, '')
File "/usr/local/lib/python2.7/dist-packages/Crypto/Cipher/PKCS1_v1_5.py", line 206, in decrypt
m = self._key.decrypt(ct)
File "/usr/local/lib/python2.7/dist-packages/Crypto/PublicKey/RSA.py", line 174, in decrypt
return pubkey.pubkey.decrypt(self, ciphertext)
File "/usr/local/lib/python2.7/dist-packages/Crypto/PublicKey/pubkey.py", line 93, in decrypt
plaintext=self._decrypt(ciphertext)
File "/usr/local/lib/python2.7/dist-packages/Crypto/PublicKey/RSA.py", line 235, in _decrypt
r = getRandomRange(1, self.key.n-1, randfunc=self._randfunc)
File "/usr/local/lib/python2.7/dist-packages/Crypto/Util/number.py", line 123, in getRandomRange
value = getRandomInteger(bits, randfunc)
File "/usr/local/lib/python2.7/dist-packages/Crypto/Util/number.py", line 104, in getRandomInteger
S = randfunc(N>>3)
File "/usr/local/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 187, in read
return self._singleton.read(bytes)
File "/usr/local/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 163, in read
return _UserFriendlyRNG.read(self, bytes)
File "/usr/local/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 122, in read
self._check_pid()
File "/usr/local/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 138, in _check_pid
raise AssertionError("PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()")
AssertionError: PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()
Decrypting works well on interactive, when not called from a process.
My security module looks like this:
'''
Created on 25 Apr 2013
#author: max
'''
import base64, ast, binascii
from Crypto.Cipher import AES
from Crypto.Cipher import PKCS1_v1_5
from Crypto.PublicKey import RSA
import br_consts
class SecurityMod(object):
'''
classdocs
'''
def __init__(self):
'''
Constructor
'''
super(SecurityMod,self).__init__()
self.privkey = RSA.importKey(open('./privkeyBR.pem', 'r').read())
self.cipher = PKCS1_v1_5.new(self.privkey)
self.ridToKeySalt = {}
#depending on the type of message, encryption format is different
def encrypt(self, msg, rqId, rid):
##code
return encMsg
#return string of object so it can be parse by JSON
def decrypt(self, encMsg, rqId, rid):
#code
return msgObjStr
def pad_data(self,data):
if len(data) == 0:
return data
if len(data) % 16 == 0:
padding_required = 15
else:
padding_required = 15 - (len(data) % 16)
data = '%s\x80' % data
data = '%s%s' % (data, '\x00' * padding_required)
return data
def unpad_data(self,data):
if not data:
return data
data = data.rstrip('\x00')
if data[-1] == '\x80':
return data[:-1]
else:
return data
You need to call Crypto.Random.atfork() after os.fork()
I just put the __init__() in the security module before the other ones
Installing the pycryptodome package fixed this issue for me on Mac and Linux. It is a fork of the Pycrypto package.
pip install pycryptodome
Here is the link to their docs: https://pycryptodome.readthedocs.io/en/latest/index.html

Categories

Resources