尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Bitnodes protocol.py详解:从零手写比特币P2P协议,彻底搞懂version/addr/addrv2消息解析

Bitnodes protocol.py详解:从零手写比特币P2P协议,彻底搞懂version/addr/addrv2消息解析 Bitnodes protocol.py详解从零手写比特币P2P协议彻底搞懂version/addr/addrv2消息解析【免费下载链接】bitnodesBitnodes estimates the size of the Bitcoin peer-to-peer network by finding all of its reachable and unreachable nodes.项目地址: https://gitcode.com/gh_mirrors/bi/bitnodesBitnodes 用不到 1200 行纯 Python 代码在 protocol.py 中从零实现了比特币P2P协议不依赖任何第三方库手写 version 握手、addr 与 addr 消息的序列化与解析。本文带你逐层拆解这份比特币P2P协议实现彻底搞懂每一条消息在字节流里长什么样。一、Bitnodes 是什么一个比特币P2P网络节点计数工具 Bitnodes 的目标是估算比特币P2P网络的真实规模从一组种子节点出发递归地向每个节点发送getaddr消息收集所有可达节点最终统计出全网节点数量、地址分布与客户端版本。它的技术亮点在于——整个协议栈是手写的。项目核心文件一览protocol.pyP2P 协议层约 1198 行包含Serializer编解码器与Connection连接管理两个核心类crawl.py基于 gevent 协程的网络爬虫调用协议层完成递归爬取pcap.py用dpkt解析抓包文件.pcap离线缓存节点数据ping.py、seeder.py节点存活探测与 DNS 种子健康检查依赖极简见 requirements.txt核心只有gevent、redis、geoip2、PySocks、dpkt没有任何比特币协议库——这正是学习价值所在。二、比特币P2P消息包结构魔数 命令 载荷 任何 P2P 客户端都要先解决字节流怎么切分成消息的问题。比特币的答案是一个 24 字节的固定头 变长载荷protocol.py文件头部的注释里就画着这张协议蓝图protocol.py[--- 消息包 ---] [ 4] MAGIC_NUMBER \xF9\xBE\xB4\xD9 魔数识别主网 [12] COMMAND 命令名如 version/addr\x00 填充 [ 4] LENGTH 载荷长度小端 [ 4] CHECKSUM SHA256(SHA256(载荷)) 前 4 字节 [..] PAYLOAD 各命令各自的载荷格式源码中对应的关键常量MAGIC_NUMBER b\xF9\xBE\xB4\xD9protocol.pyPROTOCOL_VERSION 70016、MIN_PROTOCOL_VERSION 70001protocol.py服务标志位NODE_NETWORK、NODE_WITNESS、NODE_P2P_V2等protocol.pySerializer类的核心是两个方法方法职责位置serialize_msg()按命令拼出魔数命令长度校验和载荷protocol.pydeserialize_msg()反向拆解并校验校验和、分发给具体载荷解析器protocol.py值得学习的设计细节项目为每一种异常定义了专门的错误类魔数不对、头太短、载荷校验失败、协议版本不兼容……见 protocol.py。爬虫面对的是全网数千个行为各异的节点细粒度的异常类型是稳定性的根基。三、第一步手写 version 握手消息 节点连接成功后默认端口 8333双方必须先交换version消息自报家门。Connection.handshake()方法protocol.py实现了完整的握手时序发送 version本地节点把自己的版本信息打包发出等待回复对方的version 可能的sendaddrv2verack自适应协商若对方版本 ≥ 70016回复sendaddrv2verack启用新地址格式否则只回verackprotocol.pyversion 载荷的字段结构protocol.py[ 4] VERSION 协议版本int32 小端 [ 8] SERVICES 本机支持的服务标志uint64 [ 8] TIMESTAMP Unix 时间戳int64 [..] ADDR_RECV 对端地址addr 或 addrv2 格式无时间戳 [..] ADDR_FROM 本机地址 [ 8] NONCE 64 位随机数防回环 [..] USER_AGENT 客户端标识如 /bitnodes.io:0.3/ [ 4] HEIGHT 当前块高度 [ 1] RELAY 是否中继交易解析端deserialize_version_payload()protocol.py有一个贴心细节老版本节点没有relay字段代码用try/except struct.error兜底为False保证向前兼容。Connection.get_messages()protocol.py还内置了礼貌机制收到ping立即回pong收到getheaders立即回空headers——这是比特币P2P协议里维系连接不被对端踢掉的必要礼仪。四、第二步解析 addr 消息收集全网节点 Bitnodes 的核心数据来自addr消息。爬虫向节点发getaddr()protocol.py节点回一个包含最多 1000 个地址的addr包爬取逻辑在 crawl.py 中递归展开每解析出一个新地址就再连上去问它你还认识谁。addr载荷的结构非常朴素protocol.py[..] COUNT 变长整数varint地址条数 [..] ADDR_LIST COUNT 个网络地址记录其中每条地址在旧格式中固定 26 字节[ 4] TIMESTAMP uint32 [ 8] SERVICES uint64 [16] IP_ADDR 12 字节前缀 4 字节 IPv4 [ 2] PORT 大端 uint16这里藏着两个经典坑手写协议的人都会踩一遍IPv4 地址被塞进 16 字节的 IPv6 槽位10 个0x00 2 个0xFF 4 字节 IPv4IPv4-mapped IPv6见serialize_network_address()protocol.py端序混用时间戳、服务标志是小端Q而端口却是大端H——解析时必须逐字段切换还有一个彩蛋老格式里.onion地址用特殊前缀FD87D87E43EB伪装成 IPv6protocol.py识别后取后 10 字节做 Base32 编码还原出 v2 洋葱地址。五、第三步addr 新格式Tor 与 I2P 地址不再藏猫猫 旧格式的伪装术太别扭了于是比特币社区通过 BIP-151 推出了addrv2消息。addrv2载荷每条地址的结构protocol.py[..] SERVICES 变长整数varint [ 1] NETWORK_ID 网络类型1IPv4 2IPv6 3Tor v2 4Tor v3 5I2P 6CJDNS [..] ADDR 按网络类型定长4/16/10/32/32/16 字节 [ 2] PORT 大端 uint16NETWORK_LENGTHS表定义了每种网络类型的地址长度protocol.py解析端按network_id分派到不同的还原函数addr_to_onion_v2()10 字节 → Base32 →.onion域名protocol.pyaddr_to_onion_v3()32 字节公钥 → SHA3-256 校验 → Base32 → v3 洋葱地址protocol.pyaddr_to_i2p()32 字节 → Base32 →.b32.i2p域名protocol.pydeserialize_network_address()protocol.py是整套解析逻辑的枢纽先读network_id再校验地址长度是否匹配最后填进统一的字典ipv4/ipv6/onion/i2p/cjdns/port让上层爬虫不必关心地址细节。变长整数varint则是 addr 新旧格式共用的基础组件serialize_int()规则protocol.py值范围编码 0xFD1 字节≤ 0xFFFF0xFD 2 字节≤ 0xFFFFFFFF0xFE 4 字节更大0xFF 8 字节一个真实的解析示例tests/test_deserialize_network_address.py 里有一组绝佳的实战用例比如这 13 字节00f15365fd090c010400000000208dSerializer().deserialize_network_address()会把它还原为timestamp1700000000、services3081含 NODE_P2P_V2、ipv40.0.0.0、port8333。对照字节逐位验证一遍比看十遍文档都管用。六、动手验证运行协议模块观察真实握手 ⚡想亲眼看到这一切项目自带一个最小演示入口本地跑一个 Bitcoin Core 节点监听 8333执行protocol.py的main()protocol.py它会自动完成连接 → 握手 →getaddr取地址 →getdata拉取创世块 →ping心跳观察日志中的user_agent、首条地址与块的tx_count离线验证同样方便tests/ 目录准备了真实抓包tests/data/inv.pcap、tests/data/cache.pcap与 BIP144 测试数据运行pytest即可跑通全部协议用例tests/test_pcap.py、tests/test_bip144.py、tests/test_onion_v3.py 等。七、总结这份手写协议实现最值得学的 4 个设计 ✅BytesIO 游标模型所有解析器统一用BytesIO当读取游标顺序read()即解析代码线性、无偏移量计算严格区分端序struct.pack(Q, ...)小端用于时间戳/服务标志H大端用于端口哈希则显式[::-1]翻转protocol.py异常驱动的状态恢复PayloadTooShortError时记录required_len调用方据此继续收包重试protocol.py——TCP 粘包/半包问题一网打尽自适应协商sendaddrv2握手决定后续用 addr 还是 addrv2protocol.py一套代码兼容新旧节点读懂 protocol.py你就拥有了理解任何比特币P2P协议实现甚至 Ethereum、Mastodon 等 TCP 长连接协议的通用方法论魔数切包、varint 计数、小端大端分明、异常粒度拉满。动手写一遍 version/addr/addrv2 的序列化与解析是进入分布式系统世界最快的一扇门 【免费下载链接】bitnodesBitnodes estimates the size of the Bitcoin peer-to-peer network by finding all of its reachable and unreachable nodes.项目地址: https://gitcode.com/gh_mirrors/bi/bitnodes创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表