Skip to main content

Bitcoin/altcoin merchant tools

Project description

https://travis-ci.org/sbuss/bitmerchant.svg?branch=master http://codecov.io/github/sbuss/bitmerchant/coverage.svg?branch=master

Bitmerchant

Bitmerchant is a work-in-progress python library for common bitcoin/altcoin merchant uses.

Bitmerchant currently supports:

  1. Easy to use BIP32 wallet for linking user payments with their accounts.

These features are planned (or in development where marked):

  1. Regular and M-of-N transactions (under development)

  2. A system that monitors the blockchain and sends out a signal when a payment is received at an address you’re tracking.

  3. Automatic forwarding transactions


If you find this library useful, please consider a small donation. Donations will be used to reward developers for bugfixes.

BTC

Doge

19jSqVd8bpevi3qacBedkAdDqEXtGAn5t7

DQ4b7RJfoniVwFsnrMJr6vi6n6UFeubdiv

Donate BTC

Donate DOGE

NOTICE

** BUG NOTICE **

Versions of bitmerchant prior to 0.1.8 contained a caching bug that may have resulted in calls to bip32.Wallet.get_child to return incorrect results. The steps to reproduce the bug are unlikely and do not match the typical usage patterns of bitmerchant.

At this time, no users are known to have been affected by this bug.

If you have been affected by this bug and need help recovering any lost or misplaced coins, please contact me directly at steven.buss+bitmerchant@gmail.com.

The affected versions of bitmerchant have been removed from pypi. They have not been untagged in git.

The two possible failure scenarios are: misplaced coins and stolen coins

Misplaced Coins

This is still unlikely, but slightly more likely than having your coins stolen.

In order to have misplaced coins as a result of the bug, all of the below points must be true:

  1. Your master private key must be available for your code to load, rather than in a secure offline backup

  2. You call get_child directly, rather than create_new_address_for_user

  3. You call get_child(n, is_prime=False) and get_child(n, is_prime=True)
    1. in the same python process

    2. on the same wallet object

    3. you display the public address of the second get_child call (in whichever order)

In this case, the bug would have resulted in the first get_child’s address being shown. You can easily recover these misplaced coins by updating to bitmerchant>=0.1.8, regenerating the address you accidentally sent coins to, and moving them to a corrected destination. The “deterministic” part of “hierarchical deterministic wallets” really works to your advantage here.

Stolen Coins

First, it is extremely unlikely that your code met all of the requirements to be affected by this bug. If you can answer “yes” to every one of the points below, then you should upgrade to bitmerchant>=0.1.8, generate a new master private key, and move all coins to the new wallet as soon as possible.

In order to have coins stolen as a result of the bug, all of the below points must be true:

  1. You expose your master public key to the public

  2. Your master private key must be available for your code to load, rather than in a secure offline backup

  3. You call get_child directly, rather than create_new_address_for_user

  4. You call get_child(n, is_prime=False) and get_child(n, is_prime=True)
    1. in that order

    2. in the same python process

    3. on the same wallet object

    4. with the intention of only giving the prime child to the user

  5. You give the public and private keys of child wallets to users

Installation

bitmerchant is on pypi, so just use pip:

pip install bitmerchant

Then to verify it’s working:

from bitmerchant.wallet import Wallet

w = Wallet.from_master_secret("correct horse battery staple")
assert w.to_address() == "1AJ7EDxyRwyGNcL4scXfUU7XqYkmVcwHqe"

BIP32 wallets

BIP32 wallets are hierarchical deterministic wallets. They allow you to generate bitcoin/altcoin addresses without exposing your private key to a potentially insecure server.

To link a user with a new bitcoin address, you just need to provide the user’s ID to the create_new_address_for_user method:

TL;DR

## DO THIS ON AN OFFLINE MACHINE, NOT YOUR WEBSERVER
from bitmerchant.wallet import Wallet

# Create a wallet, and a primary child wallet for your app
my_wallet = Wallet.new_random_wallet()
print(my_wallet.serialize_b58(private=True))  # Write this down or print it out and keep in a secure location
project_0_wallet = my_wallet.get_child(0, is_prime=True)
project_0_public = project_0_wallet.public_copy()
print(project_0_public.serialize_b58(private=False))  # Put this in your app's settings file


## THINGS BELOW ARE PUBLIC FOR YOUR WEBSERVER

# In your app's settings file, declare your public wallet:
WALLET_PUBKEY = "<public output from above>"

# Create a payment address for a user as needed:
from bitmerchant.wallet import Wallet
from myapp.settings import WALLET_PUBKEY

def get_payment_address_for_user(user):
    user_id = user.id
    assert isinstance(user_id, (int, long))
    wallet = Wallet.deserialize(WALLET_PUBKEY)
    wallet_for_user = wallet.create_new_address_for_user(user.id)
    return wallet_for_user.to_address()

Security warning

BIP32 wallets have a vulnerability/bug that allows an attacker to recover the master private key when given a master public key and a publicly-derived private child. In other words:

from bitmerchant.wallet import Wallet

w = Wallet.new_random_wallet()
child = w.get_child(0, is_prime=False)  # public derivation of a private child
w_pub = w.public_copy()
master_public_key = w_pub.serialize_b58(private=False)
private_child_key = child.serialize_b58(private=True)

Given master_public_key and private_child_key, the steps to recover the secret master private key (w) are as simple as a subtraction on the elliptic curve. This has been implemented as Wallet.crack_private_key, because if it’s possible to do this, then anyone should be able to do it so the attack is well known:

public_master = Wallet.deserialize(master_public_key)
private_child = Wallet.deserialize(private_child_key)
private_master = public_master.crack_private_key(private_child)
assert private_master == w  # :(

This attack can be mitigated by these simple steps:

  1. NEVER give out your root master public key.

  2. When uploading a master public key to a webserver, always use a prime child of your master root.

  3. Never give out a private child key unless the user you’re giving it to already has control of the parent private key (eg, for user-owned wallets).

Why “always use a prime child of your master root” in step 2? Because prime children use private derivation, which means they cannot be used to recover the parent private key (no easier than brute force, anyway).

Create a new wallet

If you haven’t created a wallet yet, do so like this:

IMPORTANT You must back up your wallet’s private key, otherwise you won’t be able to retrieve the coins sent to your public addresses.

from bitmerchant.wallet import Wallet

my_wallet = Wallet.new_random_wallet()

# Then back up your private key

private_key = my_wallet.serialize()
print(private_key)
# Make sure that you can load your wallet successfully from this key
wallet_test = Wallet.deserialize(private_key)
assert my_wallet == wallet_test
# If that assertion fails then open a ticket!
# NOW WRITE DOWN THE PRIVATE KEY AND STORE IT IN A SECURE LOCATION

Note that it’s a good idea to supply some extra entropy to new_random_wallet in case your PRNG is compromised. You can accomplish this easily by banging on the keyboard. Here’s an example, yours should be much longer:

from bitmerchant.wallet import Wallet

wallet1 = Wallet.new_random_wallet('asdfasdfasdf')
wallet2 = Wallet.new_random_wallet('asdfasdfasdf')
assert(wallet1.get_private_key_hex() != wallet2.get_private_key_hex())

# They're completely different

BIP32 wallets (or hierarchical deterministic wallets) allow you to create child wallets which can only generate public keys and don’t expose a private key to an insecure server. You should create a new prime child wallet for every website you run (or a new wallet entirely), and perhaps a new prime child for each user (though that requires pre-generating a bunch of prime children offline, since you need the private key). Try to use prime children where possible (see security).

It’s a good idea to create at least one prime child wallet for use on your website. The thinking being that if your website’s wallet gets compromised somehow, you haven’t completely lost control because your master wallet is secured on an offline machine. You can use your master wallet to move any funds in compromised child wallets to new child wallets and you’ll be ok.

Let’s generate a new child wallet for your first website!

# Lets assume you're loading a wallet from your safe private key backup
my_wallet = Wallet.deserialize(private_key)

# Create a new, public-only prime child wallet. Since you have the master
# private key, you can recreate this child at any time in the future and don't
# need to securely store its private key.
# Remember to generate this as a prime child! See the security notice above.
child = my_wallet.get_child(0, is_prime=True, as_private=False)

# And lets export this child key
public_key = child.serialize_b58(private=False)
print(public_key)

You can store your public key in your app’s source code, as long as you never reveal any private keys. See the security notice above.

Be aware that if someone gets a hold of your public key then they can generate all of your subsequent child addresses, which means they’ll know exactly how many coins you have. The attacker cannot spend any coins, however, unless they are able to recover the private key (see security).

Generating new public addresses

BIP32 wallets allow you to generate public addresses without revealing your private key. Just pass in the user ID that needs a wallet:

from bitmerchant.wallet import Wallet
from myapp.settings import WALLET_PUBKEY  # Created above

master_wallet = Wallet.deserialize(WALLET_PUBKEY)
user_wallet = master_wallet.create_new_address_for_user(user_id)
payment_address = user_wallet.to_address()

This assumes that user_id is a unique positive integer and does not change for the life of the user (and is less than 2,147,483,648). Now any payments received at payment_address should be credited to the user identified by user_id.

Staying secure

Public Keys

Public keys are mostly safe to keep on a public webserver. However, even though a public key does not allow an attacker to spend any of your coins, you should still try to protect the public key from hackers or curious eyes. Knowing the public key allows an attacker to generate all possible child wallets and know exactly how many coins you have. This isn’t terrible, but nobody likes having their books opened up like this.

As mentioned earlier, knowledge of a master public key and a non-prime private child of that key is enough to be able to recover the master private key. Never reveal private keys to users unless they already own the master private parent.

Your master public key can be used to generate a virtually unlimited number of child public keys. Your users won’t pay to your master public key, but instead you’ll use your master public key to generate a new wallet for each user.

Private Keys

You must have the private key to spend any of your coins. If your private key is stolen then the hacker also has control of all of your coins. With a BIP32 Wallet, generating a new master wallet is one of the only times that you need to be paranoid (and you’re not being paranoid if they really are out to get you). Paranoia here is good because if anyone gets control of your master wallet they can spend all funds in all child wallets.

You should create your wallet on a computer that is not connected to the internet. Ideally, this computer will never be connected to the internet after you generate your private key. The safest way to do this is to run Ubuntu on a livecd, install python and bitmerchant, and generate a new wallet.

Once you generate a new wallet you should write down the private key on a piece of paper (or print it out …but can you really trust your printer?) and store it in a secure location.

sudo apt-get install python
sudo apt-get install pip

pip install bitmerchant
pip install ipython

# Then launch the ipython shell
ipython

Once inside your ipython shell, generate a new wallet:

from bitmerchant.wallet import Wallet

my_wallet = Wallet.new_random_wallet()

# Then back up your private key

private_key = my_wallet.serialize()
print(private_key)
# Write down this private key.
# Double check it.
# Then shut down the computer without connecting to the internet.

Master private key

Your master private key allows you to spend coins sent to any of your public addresses. Guard this with your life, and never put it on a computer that’s connected to the internet.

Master private keys must NEVER be put on the internet. They must NEVER be located on a computer that is even connected to the internet. The only key that should be online is your PUBLIC key. Your private key should be written down (yes, on paper) and stored in a safe location, or on a computer that is never connected to the internet.

Security wise, this is the most important part of generating secure public payment addresses. A master private key is the only way to retrieve the funds paid to a public address. You can use your master private key to generate the private keys of any child wallets, and then transfer those to a networked computer as necessary, if you want slightly smaller surface area for attacks.

Forthcoming versions of bitmerchant will allow you to generate transactions offline that you can safely transfer to a networked computer, allowing you to spend your child funds without ever putting a private key on a networked machine.

Development

I’d love for you to contribute to bitmerchant! If you can’t write code, then please open a ticket for feature requests or bugs you find!

If you can code and you’d like to submit a pull request, please be sure to include tests. This library is quite well tested and I intend to keep coverage above 95% indefinitely.

Rewards may be given out to developers depending on the severity of bugs found/patched. The donation addresses mentioned at the top of this document will be used to fund rewards.

Testing

I use tox & travis-ci to test against all python versions >= 2.5. Locally, you can use the make test target, which will only test against python-2.7. You can, of course, call tox directly:

make setup
tox
tox -e py34
tox -- tests.test_bip32:TestWallet

Note that the full test suite on py-{2.5..3.4} takes about 5 minutes to run. pypy and pypy3 are considerably slower at about 25 minutes, due to unoptimized crypto operations.

Packaging

See PACKAGING

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bitmerchant-0.1.8.tar.gz (23.4 kB view details)

Uploaded Source

Built Distribution

bitmerchant-0.1.8-py2.py3-none-any.whl (28.6 kB view details)

Uploaded Python 2 Python 3

File details

Details for the file bitmerchant-0.1.8.tar.gz.

File metadata

  • Download URL: bitmerchant-0.1.8.tar.gz
  • Upload date:
  • Size: 23.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No

File hashes

Hashes for bitmerchant-0.1.8.tar.gz
Algorithm Hash digest
SHA256 fba4c2091084ed0b4f9faac0ebbba8d255c5ccd3a18e7f877ce13aab71649ddc
MD5 70d175987b137f2728d57ecb277e4786
BLAKE2b-256 a03c3cc4b1f447cf0ea27c21a0e2e55adaf0064a8cd4b3294fb3c27ca27f6dd3

See more details on using hashes here.

File details

Details for the file bitmerchant-0.1.8-py2.py3-none-any.whl.

File metadata

File hashes

Hashes for bitmerchant-0.1.8-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 01f5584625e7988ce4d8a32527e1160eaa00265042e6db1f29b04398dafd88e0
MD5 c2d0cb4a158fd0de1d8e61314d7ece0f
BLAKE2b-256 f0fd840712bffaa449023e9665d46c38541de509ae5b25ab865d75877437d3c7

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page