跳转到主要内容

比特币/代币商户工具

项目描述

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

Bitmerchant

Bitmerchant是一个正在开发的Python库,用于常见的比特币/代币商户使用。

Bitmerchant目前支持

  1. 易于使用的BIP32钱包,用于将用户支付与其账户链接。

以下功能计划(或正在开发中)

  1. 常规和M-of-N交易(开发中)

  2. 一个监控系统,当您跟踪的地址收到支付时发出信号。

  3. 自动转发交易


如果您认为这个库很有用,请考虑小额捐赠。捐款将用于奖励开发人员修复错误。

BTC

Doge

19jSqVd8bpevi3qacBedkAdDqEXtGAn5t7

DQ4b7RJfoniVwFsnrMJr6vi6n6UFeubdiv

Donate BTC

Donate DOGE

注意

** 错误通知 **

在0.1.8之前的bitmerchant版本中存在一个缓存错误,可能导致对bip32.Wallet.get_child的调用返回不正确的结果。重现错误的步骤不太可能,并且不匹配bitmerchant的典型使用模式。

到目前为止,没有用户受到影响。

如果您受到影响并需要帮助恢复任何丢失或错位的硬币,请直接联系我steven.buss+bitmerchant@gmail.com

受影响的bitmerchant版本已从pypi中删除。它们在git中没有取消标记。

可能出现的两种失败场景是:错位的硬币和被盗的硬币

错位的硬币

这种情况仍然不太可能,但比你的硬币被盗的可能性略高。

为了使硬币丢失成为此漏洞的结果,以下所有点都必须为真

  1. 您的主私钥必须可供您的代码加载,而不是在安全的离线备份中

  2. 您直接调用 get_child,而不是 create_new_address_for_user

  3. 您调用 get_child(n, is_prime=False)get_child(n, is_prime=True)
    1. 在同一个 Python 进程中

    2. 在同一个钱包对象上

    3. 您显示第二个 get_child 调用的公钥(无论顺序如何)

在这种情况下,漏洞会导致显示第一个 get_child 的地址。您可以通过升级到 bitmerchant>=0.1.8,重新生成您意外发送硬币的地址,并将它们移至正确目的地来轻松恢复这些丢失的硬币。"分层确定性钱包"的“确定性”部分确实对您有利。

被盗硬币

首先,您的代码满足所有受此漏洞影响的要求的可能性极低。如果您对以下所有点都能回答“是”,那么您应该升级到 bitmerchant>=0.1.8,尽快生成新的主私钥,并将所有硬币移至新钱包。

为了使硬币被盗成为此漏洞的结果,以下所有点都必须为真

  1. 您向公众公开了您的私钥

  2. 您的主私钥必须可供您的代码加载,而不是在安全的离线备份中

  3. 您直接调用 get_child,而不是 create_new_address_for_user

  4. 您调用 get_child(n, is_prime=False)get_child(n, is_prime=True)
    1. 按此顺序

    2. 在同一个 Python 进程中

    3. 在同一个钱包对象上

    4. 意图只向用户提供质子钱包

  5. 您向用户提供了子钱包的公钥和私钥

安装

bitmerchant 在 pypi 上,所以只需使用 pip 即可

pip install bitmerchant

然后验证其是否正常工作

from bitmerchant.wallet import Wallet

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

BIP32 钱包

BIP32 钱包是分层确定性钱包。它们允许您在不暴露您的私钥给可能不安全的服务器的情况下生成比特币/替代币地址。

要为用户关联一个新的比特币地址,您只需将用户的 ID 传递给 create_new_address_for_user 方法

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()

安全警告

BIP32 钱包有一个漏洞/错误,允许攻击者在给定主公钥和公开导出的私钥子钱包的情况下恢复主私钥。换句话说

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)

给定 master_public_keyprivate_child_key,恢复秘密主私钥 (w) 的步骤就像椭圆曲线上的减法一样简单。这已经作为 Wallet.crack_private_key 实现,因为如果能够这样做,那么任何人都可以这样做,所以攻击是众所周知的

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  # :(

可以通过以下简单步骤减轻这种攻击

  1. 永远不要泄露您的根主公钥。

  2. 当将主公钥上传到 Web 服务器时,始终使用主根的质子子钱包。

  3. 除非您给的用户已经控制了父私钥(例如,对于用户拥有的钱包),否则永远不要泄露私钥子钱包。

为什么步骤 2 中要“始终使用主根的质子子钱包”?因为质子子钱包使用私钥派生,这意味着它们不能用于恢复父私钥(无论如何,都不会比暴力破解更容易)。

创建新钱包

如果您还没有创建钱包,请按照以下方式创建

重要 您必须备份您的钱包私钥,否则您将无法检索发送到您的公钥地址的硬币。

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

请注意,为以防你的伪随机数生成器(PRNG)被破坏,向 new_random_wallet 提供一些额外的熵是一个好主意。你可以通过敲击键盘轻松实现这一点。以下是一个例子,你的应该更长。

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钱包(或分层确定性钱包)允许你创建子钱包,这些子钱包只能生成公钥,不会将私钥暴露给不安全的服务器。你应该为每个你运行的网站创建一个新的质子子钱包(或全新的钱包),也许为每个用户创建一个新的质子子钱包(尽管这需要在离线状态下预先生成大量的质子子钱包,因为你需要私钥)。尽可能使用质子子钱包(参见 安全)。

为在你的网站上使用,创建至少 一个 质子子钱包是一个好主意。其思路是,如果你的网站钱包以某种方式被破坏,你并没有完全失去控制,因为你的主钱包在离线机器上得到保护。你可以使用主钱包将受损害子钱包中的任何资金转移到新的子钱包,并且你会没事的。

让我们为你第一个网站生成一个新的子钱包!

# 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)

只要你不泄露任何私钥,你就可以将你的公钥存储在你的应用程序的源代码中。参见上面的 安全 通知。

请注意,如果有人得到了你的公钥,他们就可以生成你所有后续的子地址,这意味着他们会知道你有多少个币。然而,攻击者除非能够恢复私钥(参见 安全),否则无法花费任何硬币。

生成新的公钥地址

BIP32钱包允许你生成公钥而不暴露你的私钥。只需传递需要钱包的用户ID

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()

这假设 user_id 是一个唯一的正整数,并且在用户的一生中不会改变(并且小于2,147,483,648)。现在收到的任何支付都应归因于由 user_id 标识的用户。

保持安全

公钥

公钥大部分可以在公共服务器上安全保存。然而,尽管公钥不允许攻击者花费你的任何硬币,你仍然应该尝试保护公钥免受黑客或好奇之人的侵害。知道公钥允许攻击者生成所有可能的子钱包并确切知道你有多少个币。这并不是什么大问题,但没有人喜欢自己的账本这样被公开。

如前所述,知道主公钥和该密钥的非质子子密钥足以能够恢复主私钥。除非用户已经拥有主私钥的父密钥,否则不要向用户泄露私钥。

你的主公钥可以用来生成几乎无限数量的子公钥。你的用户不会支付给你的主公钥,而是你将使用主公钥为每个用户生成一个新的钱包。

私钥

你必须拥有私钥才能花费你的任何硬币。如果你的私钥被盗,那么黑客也将控制你的所有硬币。在BIP32钱包中,生成新的主钱包是你唯一需要变得偏执的时候(如果你真的 真心想要得到你,你不会变得偏执)。这里的偏执是好的,因为如果任何人控制了你的主钱包,他们就可以花费所有子钱包中的所有资金。

您应该在未连接到互联网的计算机上创建您的钱包。理想情况下,这台计算机在您生成私钥后 永远 不要连接到互联网。最安全的方法是在livecd上运行Ubuntu,安装python和bitmerchant,然后生成一个新的钱包。

一旦生成了新的钱包,您应该在一张纸上(或打印出来……但您真的能信任您的打印机吗?)写下私钥,并将其存放在安全的地方。

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

pip install bitmerchant
pip install ipython

# Then launch the ipython shell
ipython

一旦进入ipython shell,生成一个新的钱包

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.

主私钥

您的主私钥允许您花费发送到您任何公钥地址的硬币。请像保护生命一样保护它,永远不要将其放在连接到互联网的计算机上。

主私钥绝不能放在互联网上。它们绝不能位于甚至 连接 到互联网的计算机上。唯一应该在网络上的是您的 公钥。您的私钥应该写在纸上(是的,写在纸上),并存储在安全的地方,或者存储在从未连接到互联网的计算机上。

从安全的角度来看,这是生成安全的公钥支付地址最重要的部分。主私钥是检索发送到公钥地址的资金的唯一方法。您可以使用主私钥生成任何子钱包的私钥,然后根据需要将它们转移到联网计算机上,如果您想稍微减小攻击面的话。

bitmerchant的后续版本将允许您生成可以安全传输到联网计算机的离线交易,这样您就可以在不将私钥放在联网机器上的情况下花费子资金。

开发

我非常希望您为bitmerchant做出贡献!如果您不能编写代码,那么请为功能请求或您发现的错误打开一个工单!

如果您可以编写代码并想提交拉取请求,请确保包含测试。这个库已经得到了很好的测试,我打算无限期地保持覆盖率在95%以上。

根据发现的/修补的错误严重程度,可能会对开发者发放奖励。本文件顶部提到的捐赠地址将用于资助奖励。

测试

我使用tox和travis-ci对所有大于等于2.5的python版本进行测试。在本地,您可以使用make test目标,它只针对python-2.7进行测试。当然,您可以直接调用tox。

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

请注意,在py-{2.5..3.4}上的完整测试套件大约需要5分钟才能运行。由于未优化的加密操作,pypy和pypy3要慢得多,大约需要25分钟。

打包

请参阅PACKAGING

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪一个,请了解更多关于安装包的信息。

源分布

bitmerchant-0.1.8.tar.gz (23.4 kB 查看哈希值)

上传时间

构建分布

bitmerchant-0.1.8-py2.py3-none-any.whl (28.6 kB 查看哈希)

上传时间 Python 2 Python 3

由以下支持