Cover photo

BTC RPC节点部署

RPC节点参与网络的同步,主要用于查询区块链数据,广播交易等作用;不会参与共识,挖矿。

阅读者建议

  • 有一定的linux基础

  • 熟悉docker

安装前置条件

  • linux系统

  • 机器上有docker服务

部署脚本

Dockerfile

FROM ubuntu:20.04

ARG VERSION=22.0

RUN apt-get update \
  && apt-get install -y net-tools curl \
  && curl -o /tmp/bitcoin.tar.gz https://bitcoincore.org/bin/bitcoin-core-${VERSION}/bitcoin-${VERSION}-x86_64-linux-gnu.tar.gz \
  && tar -zxvf /tmp/bitcoin.tar.gz -C /opt \
  && mv /opt/bitcoin-* /opt/bitcoin \
  && ln -s /opt/bitcoin/bin/* /usr/local/bin \
  && rm -f /tmp/bitcoin.tar.gz

WORKDIR /btc

EXPOSE 8332

ENTRYPOINT ["sh", "-c", "bitcoind -datadir=/btc -rpcbind=0.0.0.0 -rpcallowip=0.0.0.0/0 -txindex -rpcworkqueue=1000 -rpcuser=$RPC_USER -rpcpassword=$RPC_PWD"]

run.sh 脚本

#!/bin/bash

# 如果需要升级版本,修改这里的版本号后,执行一下run.sh脚本即可。
VERSION=22.0

docker build --build-arg VERSION=$VERSION -t btc:$VERSION .
docker stop btc
docker rm btc
docker run -d --name btc -p 8332:8332 -v /data/btc:/btc -e RPC_USER="test" -e RPC_PWD="test" --restart=always btc:$VERSION

对run.sh赋予执行权限,运行脚本

查看日志

docker logs -f --tail 20 btc
节点日志,经过一段时间,追赶上最新区块
节点日志,经过一段时间,追赶上最新区块

RPC接口

常用方法:

  • getblockchaininfo 获取最新区块

  • getblockhash 获取区块高度hash值

  • getblock 获取区块详情

  • getrawtransaction 获取交易详情

参考文档: BTC官方RPC接口

https://developer.bitcoin.org/reference/rpc/

获取最新区块高度

curl -X POST -u test:test -d '{"jsonrpc": "2.0", "method": 
"getblockchaininfo", "params": [], "id": 1}' http://localhost:8332
{
  "result": {
    "chain": "main",
    "blocks": 759822,
    "headers": 759822,
    "bestblockhash": "0000000000000000000096b2e9a0db174eeb7a4c15e8a38398593f36032cfb09",
    "difficulty": 35610794164371.65,
    "time": 1666441676,
    "mediantime": 1666438889,
    "verificationprogress": 0.9999988460882687,
    "initialblockdownload": false,
    "chainwork": "000000000000000000000000000000000000000037b5d76943db7b147d7b83e6",
    "size_on_disk": 492851010520,
    "pruned": false,
    "warnings": ""
  },
  "error": null,
  "id": 1
}
# "blocks": 759822 此字段为当前区块高度

获取区块高度的hash值

curl -X POST -u test:test -d '{"jsonrpc": "2.0", "method": "getblockhash", "params": [759822], "id": 1}' http://localhost:8332 
{
  "result": "0000000000000000000096b2e9a0db174eeb7a4c15e8a38398593f36032cfb09",
  "error": null,
  "id": 1
}

其余接口建议大家自己去尝试

题外话

安全: RPC节点主要会被Wallet, Dapp, Cex等应用使用。自建RPC节点使用起来也会更安全,放心,不用担心你的请求会被他人拦截导致私密信息的泄漏。

地址: 类BTC,ETH的地址都是使用椭圆曲线算法生成,支持离线生成。

离线生成地址Python代码样例

import ecdsa
import base58
import hashlib
import binascii

class Wallet:
    def __init__(self, network='mainnet'):
        # 版本前缀
        self.__pubkey_version_byte = '00' if network == 'mainnet' else '6F'
        self.__wif_version_byte = '80' if network == 'mainnet' else 'ef'

    @property
    def pubkey_version_byte(self):
        return self.__pubkey_version_byte

    @pubkey_version_byte.setter
    def pubkey_version_byte(self, v):
        if isinstance(v, str):
            raise ValueError
        self.__pubkey_version_byte = v
    
    @property
    def wif_version_byte(self):
        return self.__wif_version_byte
    
    @wif_version_byte.setter
    def wif_version_byte(self, v):
        if isinstance(v, str):
            raise ValueError
        self.__wif_version_byte = v

    def __generate_privkey(self):
        privkey = ecdsa.SigningKey.generate(curve=ecdsa.SECP256k1)
        return privkey

    def get_pubkey(self, compress=False, privkey=None):
        if not privkey:
            privkey = self.__generate_privkey()
        # 是否压缩,压缩格式的公钥前缀是0x02或0x03,不压缩格式的公钥前缀是0x04
        s = '02' if compress else '04'
        pubkey = s + privkey.get_verifying_key().to_string().hex()
        return pubkey

    def get_address(self, pubkey):
        """根据公钥获取地址"""
        # 1.处理传入公钥格式
        sha256Key = hashlib.sha256(binascii.unhexlify(pubkey)).hexdigest()
        ridemp160FromHash256 = hashlib.new('ripemd160', binascii.unhexlify(pubkey))
        # 2.将 00 作为网络字节添加ripemd160公钥
        prepend_network_byte = self.__pubkey_version_byte + ridemp160FromHash256.hexdigest()
        # 3.将双重 SHA256 应用于prepend_network_byte以校验和
        hash = prepend_network_byte
        for x in range(1,3):
            hash = hashlib.sha256(binascii.unhexlify(hash)).hexdigest()
        checksum = hash[:8]
        # 4.附加值
        append_checksum = prepend_network_byte + checksum
        # 5.base58编码
        address = base58.b58encode(binascii.unhexlify(append_checksum))
        return address.decode('utf8')

    def generate(self):
        """生成地址"""
        privkey = self.__generate_privkey()
        pubkey = self.get_pubkey(privkey)
        address = self.get_address(pubkey)
        self.privkey = privkey.to_string().hex()
        self.pubkey = pubkey
        self.address = address
        return address

    def privkey_to_wif(self, privkey):
        """私钥格式转钱包导入格式"""
        prepend_network_byte = self.__wif_version_byte + privkey
        hash = prepend_network_byte
        for x in range(1,3):
            hash = hashlib.sha256(binascii.unhexlify(hash)).hexdigest()
        checksum = hash[:8]
        append_checksum = prepend_network_byte + checksum
        privkey = base58.b58encode(binascii.unhexlify(append_checksum))
        return privkey.decode('utf8')

if __name__ == '__main__':
    wallet = Wallet()
    wallet.generate()
    print(wallet.address, wallet.privkey)