(以下是个人思考结果,并无真是搞过交易网站开发)

问题如题:如何实现交易网站的比特币充值

主要需要解决的问题

1: 如何给每一个用户生成一个账号, 以及这么多账号该如何管理
2: 如何知道用户往这个地址转币了(需要跑一个全节点)

第一问: 如何给每一个用户生成一个账号, 以及这么多账号该如何管理

如果给每一个账号单独生成一个比特币公私钥对,这个肯定不现实,因为自身也需要收集到所有的币,对每个账号多做一次转账,也多费一次矿工费

首先需要了解bip39,是关于比特币助记词的,用多个单词来代替用户来记录公私钥对, 助记词的安全性应该是和私钥相当的

助记词的优点不止如此, 助记词还用于生成有关连的比特币账号(参见《mastering bitcoin》第96页) 这些生成方式可以查看bip32,bip44,bip49,bip84,bip141等 hdwallet{width=70%}

seed(bip39)是从助记词生成,然后生成一对Master Key,然后可以生成无限(总的比特币私钥也是有限的,2^256,这里的自节点也可以说成无限的)个子节点, 转账签名时,然后这些子节点都可以用同一个私钥进行签名.

import mnemonic
i = ['mention', 'wish', 'know', 'payment', 'dune', 'picture', 'level', 'catalog', 'parade', 'flock', 'hawk', 'depend']
a = mnemonic.Mnemonic('english’)
entropy = a.to_seed(i)    # (注意下面虽然是BIP32Key.fromEntropy,但这里输出还是得to_seed)

# 获得某一个xpub (是否testnet)
# 生成的xprv 和 xpub都能作为bip32的输入, 用xpub去生成的地址,拿不到私钥,所以可以放心的给某一部门用
c = BIP32Key.fromEntropy(entropy, testnet=1).ChildKey(0 + BIP32_HARDEN).ChildKey(1).ChildKey(2).ChildKey(3)
c.dump()   # 从输出中获取xpub, 这个库的dump函数,python3会报错,需要自己修改

从xpub去生成地址
bip32 = BIP32Key.fromExtendedKey('xpub6F4nfP15Zd4755DYXpkJeHw8WydnhJxa7qo5BGzUWTbT1sULndMnVgRSmRqdfAEaWBFTfUYf6SK6pZjFdG12qcfjxDuQcTsJ7gY2F71yd1U')
print(bip32.ChildKey(0).Address())     # (以后每次变数字就行了。。)

这里代码生成的数据也可以在这里验证

第二问 如何知道用户往这个地址转币了

用户要往某个地址转币,会先生成一笔交易(transaction),然后广播出去,这里的话,得拿到广播通知.当刚拿到的时候,交易并没有确认(刚收到广播,确认数肯定为0), 而且交易并不是广告出去就不可修改,或者撤回的.一半都得达到某一确认数之后,才能算交易成功了

所以,这里主要解决两个问题: 1:如何获取到通知 2:如何拿到确认数 首先,你需要在本地运行全节点, 我尝试过bitcoin(+electrumx)和bitcore, 推荐bitcore,它的一整套功能,其他地方可以用的到

节点收到广播后,会通过zeromq推送事件过来,实现代码在bitcoin源码里有,下面是我修改完的代码

import binascii
import asyncio
import zmq
import zmq.asyncio
import signal
import struct
import sys

from pycoin.tx.Tx import Tx

port = 28332

class ZMQHandler():
    def __init__(self):
        self.loop = zmq.asyncio.install()
        self.zmqContext = zmq.asyncio.Context()

        self.zmqSubSocket = self.zmqContext.socket(zmq.SUB)
        self.zmqSubSocket.setsockopt_string(zmq.SUBSCRIBE, "hashblock")
        self.zmqSubSocket.setsockopt_string(zmq.SUBSCRIBE, "hashtx")
        self.zmqSubSocket.setsockopt_string(zmq.SUBSCRIBE, "rawblock")
        self.zmqSubSocket.setsockopt_string(zmq.SUBSCRIBE, "rawtx")
        self.zmqSubSocket.connect("tcp://127.0.0.1:%i" % port)

    def rec_rawtx(self, body):
        '''
        收到交易数据后,将数据parse出来, 如果收款地址里是我们自己的地址时,再做充值(数据库里+n)
        解析数据,我用了pycoin的库
        '''
        tx = Tx.from_bin(body)
        addrs = get_our_addrs()   # 获取所有用户的比特币充值地址
        for i in tx.txs_out:
            addr = i.address()
            if addr not in addrs:
                continue

            # 您的充值已受理
            # 然后开启定时轮训查这笔交易状态,有一个confirmation字段
        
    async def handle(self) :
        msg = await self.zmqSubSocket.recv_multipart()
        topic = msg[0]
        body = msg[1]
        sequence = "Unknown"
        if len(msg[-1]) == 4:
          msgSequence = struct.unpack('<I', msg[-1])[-1]
          sequence = str(msgSequence)
        if topic == b"hashblock":
            print('- HASH BLOCK ('+sequence+') -')
            print(binascii.hexlify(body))
        elif topic == b"hashtx":
            print('- HASH TX  ('+sequence+') -')
            print(binascii.hexlify(body))
        elif topic == b"rawblock":
            print('- RAW BLOCK HEADER ('+sequence+') -')
            print(binascii.hexlify(body[:80]))
        elif topic == b"rawtx":
            print('- RAW TX ('+sequence+') -')
            print(binascii.hexlify(body))

            self.rec_rawtx(body)
            
        # schedule ourselves to receive the next message
        asyncio.ensure_future(self.handle())

    def start(self):
        self.loop.add_signal_handler(signal.SIGINT, self.stop)
        self.loop.create_task(self.handle())
        self.loop.run_forever()

    def stop(self):
        self.loop.stop()
        self.zmqContext.destroy()

daemon = ZMQHandler()
daemon.start()

关于查询交易的确认数,可以掉比特币的json rpc接口,getrawtransaction(代码待补充) 也能自己手动调一下试试bitcore call getRawTransaction [transaction_id] 1

确认数达到一定量时,就可以确认交易成功了,可以给用户加上这个充值的数了