Barebones Python 3.6+ implementation (no dependencies/standard lib only) of some common cryptographic functions for educational purposes. Feel free to fork the repo and play around with it. Performance is ..abysmal but otherwise it works fine. Please do not use this for anything serious because I am not a security expert.
HD Wallets
from btctools import Xprv
>>> m = Xprv.from_mnemonic('impulse prize erode winner pupil fun off addict ...')
>>> m.encode()
'xprv9s21ZrQH143K38bNJiHY54kkjio8o6aw3bRjCbzi8KgRxNy98avUribz1wk85ToSUV2VwVuc73NJWc2YGwpMtqz7bBFUh9Q77RtJeuh2zvy'
>>> m/44/0/0/0
Xprv(path=m/44/0/0/0, key=L1WKXyMwKnp8wPwAtjwiKWunACY5RSUXAzmS6jDRRHcHnDbeRiKu)
>>> m/0./123/5. # Use floats for hardened path, alternative is // e.g m//0/123//5
Xprv(path=m/0h/123/5h, key=L3qskbdzgNu4kwjx2QU63q59khpEHVaSbqd2Pc268Jngiha6mbfQ)
>>> M = m.to_xpub()
>>> (m/123/456).to_xpub() == M/123/456
True
>>> (m/44./0./0./0/0).address('P2PKH') # bip44
'1BTYXdyrBh1yRCDpqyDhoQG896bnzqtaPz'
>>> (m/84./0./0./0/0).address('P2WPKH') # bip84
'bc1qjnx8cq32z2t72tsmuwql3wz22lywlpcm3w52lk'
Sign/Verify message:
Verify a transaction:
Create a transaction and submit it automatically
import os
os.environ['CRYPTOTOOLS_NETWORK'] = 'test'
from btctools import PrivateKey, send
key = PrivateKey.from_hex('mysupersecretkey')
>>> send(source='n4SbPWR6EmQMsWaQVYYFXiJgjweGKE4XnQ', to={'n2NGrooSecJaiD6ssp4YqFoj9eZ7GrCJ66': 0.46}, fee=0.01, private=key)
'907b92969cb3a16ddb45591bf2530f177b7f10cef4e62c331596a84f66c3b8c3' # txid
Create and broadcast manually
import os
os.environ['CRYPTOTOOLS_NETWORK'] = 'test'
from btctools import PrivateKey, Address
private = PrivateKey.from_hex('mysupersecretkey')
address = Address('n2NGrooSecJaiD6ssp4YqFoj9eZ7GrCJ66')
>>> address.balance()
0.55
send_to = {'n4SbPWR6EmQMsWaQVYYFXiJgjweGKE4XnQ': 0.1, 'n2NGrooSecJaiD6ssp4YqFoj9eZ7GrCJ66': 0.4}
tx = address.send(to=send_to, fee=0.05, private=private)
>>> tx
Transaction(inputs=1, outputs=2)
>>> tx.inputs[0].is_signed()
True
>>> tx.verify()
True
>>> tx.broadcast()
'Transaction Submitted'
Create keys/addresses (including segwit)
>>> from btctools import generate_keypair, push, script_to_address, OP
>>> private, public = generate_keypair()
>>> private.hex()
'de4f177274d29f88a5805333e10525f5dd41634455dfadc8849b977802481ccd'
>>> private.wif(compressed=False)
'5KWCAYLo35uZ9ibPTzTUDXESTE6ne8p1eXviYMHwaoS4tpvYCAp'
>>> public.hex()
'047e30fd478b44869850352daef8f5f7a7b5233044018d465431afdc0b436c973e8df1244189d25ae73d90c90cc0f998eb9784adecaecc46e8c536d7d6845fa26e'
>>> public.to_address('P2PKH')
'19dFXDxiD4KrUTNFfcgeekFpQmUC553GzW'
# Simple <key> <OP_CHECKSIG> script
>>> script = push(public.encode(compressed=True)) + OP.CHECKSIG.byte
>>> script_to_address(script, 'P2WSH')
'bc1q8yh8l8ft3220q328hlapqhflpzy6xvkq6u36mctk8gq5pyxm3rwqv5h5dg'
# nested P2WSH into P2SH
>>> script_to_address(script, 'P2WSH-P2SH')
'34eBzenHJEdk5PK9ojuuBZvCRtNhvvysYZ'
>>> from ECDSA.secp256k1 import CURVE, PrivateKey
>>> private = PrivateKey.random()
>>> private.int()
8034465994996476238286561766373949549982328752707977290709076444881813294372
>>> public = private.to_public()
>>> public
PublicKey(102868560361119050321154887315228169307787313299675114268359376451780341556078, 83001804479408277471207716276761041184203185393579361784723900699449806360826)
>>> public.point in CURVE
True
>>> public.to_address('P2WPKH')
'bc1qh2egksgfejqpktc3kkdtuqqrukrpzzp9lr0phn'
Vanity address generator
RSA
>>> import RSA
>>> private, public = RSA.generate_keypair(512)
>>> txt = 'deadbeef'
>>> message = RSA.Message.from_hex(txt)
>>> message
b'\xde\xad\xbe\xef'
>>> message.encrypt(public)
>>> message
b'\x05\xe3q\x92\x1c=)\xaev\xe8\x8d\x8c\x9f\x8d\xde\x17\xdc\x95y\x1e\x90N\xf1A\x816\xb7|z\x83...'
>>> message.decrypt(private)
>>> message.hex() == txt
True
>>> message.encrypt(private)
>>> message.decrypt(public)
>>> message.hex() == txt
True
to run tests
from the project directory