TL;DR: creating a Bitcoin wallet for yourself is very easy - which should not be surprising. I’ll show you how. The code is on Github.
A year and a half ago, things were on the up. The COVID crisis was fading and my business had just completed a successful contract. More excitingly, we were developing a close relationship with our biggest partner: FTX.
What happened then is worth recalling carefully. If the news in those days had been about a federally licensed and protected US domiciled bank collapsing (like SVB did months later), we would have been less shocked. If we’d have been on our guard appropriately - given the unlicensed and off-shore nature of FTX, the warning signs would have been enough to get our money out well in time. Even SBF’s outrageous “it’s a box” interview with Matt Levine should’ve been enough to convince us to keep our funds off-exchange.
But we were in good company with our myopia. Our losses were dwarfed by others who were themselves federally regulated and US domiciled. Replete as they were with financially licensed risk-managers with top business school educations, they were as flumoxed as we were. I was ashamed - but double shame on them. How did it happen?
Call it SBF’s reality distortion field. I have a strong personal grudge with him as the result of that. I’ll be watching his sentencing closely. But it’s happened. The truth is that the mantra of “not your keys, not your coins” has always been true. I’d previously rolled my eyes at that expression, scoffing at the paranoid crypto maximalists who would spout it. Not any more. But what do you - in practice - do about it?
Out of the Frying Pan?
A whole sub-industry has sprouted around securing your crypto. Large, successful corporations provide “hardware wallets” in USB sticks which you can put on your key-rings. You know your crypto is safe - you can feel it in your pocket. For corporations, there is Copper and Coinbase Custody and Fireblocks. They will integrate with your crypto back-end and protect you from various crypto specific financial risks.
But isn’t crypto supposed to be less risky than traditional finance? Aren’t your funds safe on the blockchain? Also, who are these companies offering protection from losses? I was wrong to trust FTX - should I trust them?
I wondered about this so I thought I’d find out for myself. I therefore resolved to 1) create my own Bitcoin wallet 2) automate it for practical use in Python. I’ll lay that out below.
You don’t even need an internet connection
What you need is a random number. That, basically, is your Bitcoin wallet. No really!
import os
private_key = os.urandom(32)
You’ll need to do some arithmetic on this number to make it useful on the Bitcoin network, however you’ve just created the item that allows you to safely store funds without fear they can be lost in an FTX style event.
But… a first warning here. That is “your key” - you mustn’t lose that. I’ll show you how to easily store it later. For the moment you should at least write it to disk:
with open('private_key.txt', 'wb') as private_key_file:
private_key_file.write(private_key)
One thing that’s remarkable about this is that this simple use of standard Python, which can be done entirely disconnected from the internet now gives you those “keys” - it’s done.
There are Python Bitcoin libraries that can get through the next steps in a couple of lines, but I’ll do them using libraries that have no connection with crypto to make the point:
import ecdsa
sk = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.SECP256k1)
vk = sk.verifying_key
public_key = b'\x04' + vk.to_string()
Having generated a public key from the private key, there’s just a little more arithmetic to create a useable Bitcoin address.
import hashlib
import base58
sha256 = hashlib.sha256(public_key).digest()
ripemd160 = hashlib.new('ripemd160', sha256).digest()
network_byte = b'\x00' # Mainnet
network_bitcoin_public_key = network_byte + ripemd160
checksum = hashlib.sha256(hashlib.sha256(network_bitcoin_public_key).digest()).digest()[:4]
bitcoin_address = base58.b58encode(network_bitcoin_public_key + checksum)
print(f"Bitcoin address: {bitcoin_address}")
Now you have your Bitcoin address. It can be provided to anyone and Bitcoin can be deposited to it.
How it hangs together
ecdsa is a pure-Python implementation of Elliptic Curve Cryptography. ECC was chosen for Bitcoin to create public keys in a way that guarantees the private keys can’t be reverse engineered. Note this is what justifies the term “crypto currency”: the currency is protected through mathematical cryptography rather than by anti-counterfeiting technology or though the trust of banks. This is the great innovation underpinning the digital asset industry.
hashlib is part of standard Python. Suffice it to say that it is a standard way of referring reliably to some data. In this case that data are your Bitcoin keys. The steps shown are the ones used for Bitcoin. They are very standard and reasonable.
The production of the final Bitcoin address uses base58. There’s nothing remarkable about this. It’s function is “written on the tin”. It provides functionality to manipulate numbers in base 58 - “octapentadecimal” if you wish. We normally use decimal - base 10. In computer science, binary (base 2), octal (base 8) and hexadecimal (base 16) are often used. Hexadecimal extends the numerical digits with ABCDEF. Base 64 is also often used to compress numbers using all letters and numbers (including lower-case and two punctuations) to extend the digit set. However when those numbers need to be manually copied, there can be visual confusion between some of the letters. Hence base 58, which excludes e.g. “0 (zero), O (capital o), I (capital i) and l (lower case L)”
Note that all the above techniques existed prior to Bitcoin and have no dependance on it. It can all be executed detached from the internet.
At this point you have what you need to protect yourself from reliance on other parties. Once you’ve built up enough Bitcoin somewhere to worry you - just transfer it to your new address.
BTW, I’m not using the term “wallet”. As you can see it doesn’t really mean anything. What you have is a number which is referred to as an address. Since it receives funds, that seems a reasonable name.
But - so disconnected
What’s going on here? There has been nothing like a “registration” with the Bitcoin network. How is it that funds can be transferred to some number that I dreamed up, literally, at random?
When you send funds to your new address via the Bitcoin network, the transaction is placed in a block that is then verified by Bitcoin miners (you can look up the exact process). After that, your address is encoded in the ledger on the blockchain as having those funds added to its balance.
(It is possible that someone else could have, or might later generate an identical random number - but that is vanishingly unlikely)
Now I want to get my Bitcoin out
Anyone can transfer Bitcoin to your address - even if you don’t want them to. That is similar to a bank account. I suppose it is assumed that no-one objects to more money. It is when you want to transfer money out - say, in order to pay someone - that you need those “keys”.
So let’s get back that private key from before:
with open('private_key.txt', 'rb') as private_key_file:
private_key = private_key_file.read()
There are a few simple steps to take to get it in the right form. It requires a prefix to indicate it is “mainnet” not “testnet” and a checksum.
versioned_private_key = b'\x80' + private_key
checksum = hashlib.sha256(hashlib.sha256(versioned_private_key).digest()).digest()[:4]
versioned_private_key_checksummed = versioned_private_key + checksum
Now we use this to put the private key in Wallet Import Format (WIF).
wif_private_key = base58.b58encode(versioned_private_key_checksummed)
print(f"Private Key in WIF: {wif_private_key}")
By using Base 58 the key can be written down by the user and kept securely. This, finally, is “your key”. You can print it or write it on a peice of paper. An actual example is 5J7tAWUsKaWuafymdbXDVpUFkm3Za2d6PYg1CWPjWgv8pvgrKG9
.
It’s only at this stage that we need to connect with the Bitcoin network and therefore need an internet connection.
For example you can check your balance:
from bit import Key
key = Key(wif_private_key.decode())
balance = key.get_balance('btc')
print(f"BTC Balance = {balance}")
You can also send funds from the address to another address. By default it will deduct the fees specified at https://mempool.space/api/v1/fees/recommended - though you can also specify a particular fee if you wish.
recipient = "1BmSP9UCfmXKnGmf7aYt28DTAVG63AjVSd"
amount = 0.3 # In BTC
key.send([(recipient, amount, "btc")])
This is the basic code that allows you to automatically make your funds safe and swing them to other locations. All this can be run as part of your algorithmic strategy in ProfitView. To book a call with us click the button below and we can walk you through some examples.
There’s a bit more work with, for instance, USDT
since you’d want to move it on cheaper and faster blockchains than Ethereum, which adds a bit of complexity. It is very much doable however - but must be the subject of another blog.
This blog is also available on Github as a Jupyter notebook where the code is also executable.