比特币--网络架构

比特币网络是按照比特币P2P协议运行的一系列节点的集合。

除了比特币P2P协议,网络中还运行着其他协议,如Stratum协议:应用于挖矿节点和轻量级或移动端比特币钱包之中。

网关路由服务器使用P2P协议接入比特币网络,并把网络拓展到运行其他协议的各个节点。如Stratum服务器通过Stratum协议将所有的Stratum挖矿节点连接至比特币主网络、并将Stratum协议桥接(bridge)至比特币P2P协议之上。

比特币网络节点

虽然说节点是对等的,但是根据节点所提供功能的不同,会有不同的分工。

image

运行比特币P2P协议的比特币主网络由大约7000-10000个运行着不同版本比特币核心客户端(Bitcoin Core)的监听节点、以及几百个运行着各类比特币P2P协议的应用(例如BitcoinJ、Libbitcoin、btcd等)的节点组成。

许多连接到比特币网络的大型公司运行着基于Bitcoin核心客户端的全节点客户端,它们具有区块链的完整拷贝及网络节点,但不具备挖矿及钱包功能。这些节点是网络中的边缘路由器(edge routers),通过它们可以搭建其他服务,例如交易所、钱包、区块浏览器、商家支付处理(merchant payment processing)等。

下图描述了扩展比特币网络,它包括了多种类型的节点、网关服务器、边缘路由器、钱包客户端以及它们相互连接所需的各类协议。
image

网络发现

比特币节点采用TCP协议,一般监听在8333端口。

启动时

新的网络节点在启动时,至少要连接到一个网络中存在的节点并建立连接,建立连接时,新节点会发送一个包含基本认证信息的version消息开始通讯,被连接的对等节点会响应一个verack消息以确认建立连接。对等节点如需互换连接,也会传回version消息。如下图所示:

image

  • version消息内容
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    ▷ PROTOCOL_VERSION 
    常量,定义了客户端所“说出”的比特币P2P协议所采用的版本(例如:70002)。

    ▷ nLocalServices
    一组该节点支持的本地服务列表,当前仅支持NODE_NETWORK

    ▷ nTime
    当前时间

    ▷ addrYou
    当前节点可见的远程节点的IP地址

    ▷ addrMe
    本地节点所发现的本机IP地址

    ▷ subver
    指示当前节点运行的软件类型的子版本号(例如:”/Satoshi:0.9.2.1/”)

    ▷ BaseHeight
    当前节点区块链的区块高度

    种子节点:长期稳定运行的节点,比特币客户端会维护一个种子节点列表。

在比特币核心客户端中,是否使用种子节点是通过“-dnsseed”控制的。默认情况下,该选项设为1,即意味着使用种子节点。

启动后

当建立一个或多个连接后,新节点将一条包含自身IP地址的addr消息发送给其相邻节点。相邻节点再将此条addr消息依次转发给它们各自的相邻节点,从而保证新节点信息被多个节点所接收、保证连接更稳定。另外,新接入的节点可以向它的相邻节点发送getaddr消息,要求它们返回其已知对等节点的IP地址列表。通过这种方式,节点可以找到需连接到的对等节点,并向网络发布它的消息以便其他节点查找。

image

如果已建立的连接没有数据通信,所在的节点会定期发送信息以维持连接。如果节点持续某个连接长达90分钟没有任何通信,它会被认为已经从网络中断开,网络将开始查找一个新的对等节点。因此,比特币网络会随时根据变化的节点及网络问题进行动态调整,不需经过中心化的控制即可进行规模增、减的有机调整。

全节点:交换库存清单(inventory)

新加入的节点可能是想作为全节点,也可能是作为轻量级钱包。如果是全节点则需要从比特币网络中同步完整的区块链数据。

同步区块链数据从version消息开始,因为该消息中有BaseHeight字段,标示了它拥有的区块数量,接收消息的对等节点会和自己本地区块数量比较,然后相互发送一个getBlocks消息,该消息包含发送节点的本地区块链最顶端的区块的哈希值,接收方的对等节点拿到后和自己本地区块链中比较,如果发现这是一个自己本地已经包含的区块的哈希值,则说明自己本地链比对等节点的更长。

发现自己本地链较长的节点能知道比自己短的对等节点缺哪些区块,然后整理出第一批可供分享的500个区块的哈希值,通过发送inv(inventory,库存的意思)消息将其传播出去,缺少这些区块的节点则可以通过发送getData消息来请求全区块信息,用包含再inv消息中的哈希值来确认是否为正确请求的区块。

当区块较少的节点收到来自其他节点的inv库存消息后,会向所有与之连接的对等节点发送getData消息请求获取区块,该节点还会跟踪每个对等节点上正在传输的区块的数量,并检查该数量是否超过最大限制值(MAX_BLOCKS_IN_TRANSIT_PER_PEER),从而控制更新速度,以至于不压垮网络,每收到一个块,就会加入自己的本地链上。

每当一个节点离线,不管离线时间多长,只要连接上对等节点,就会与对等节点比较本地区块链并恢复缺失区块的过程就会被触发。如果离线几分钟,可能就几个区块,长达一个月,则会缺失上千个区块,而每次都是从交换getBlocks消息开始,收到inv响应,然后就开始下载缺失的区块。

image

简单支付验证节点(SPV)

SPV节点只需下载区块头,而不用下载包含在每个区块中的交易信息,所以大小只有完整区块链的1/1000.

  1. 简易支付验证是通过验证交易在区块链中的深度来验证它们,而不是高度,当该交易所在的区块之后又生成了6个区块,根据代理网关协议,就可以证明该交易不是双重支付。

  2. 而拥有完整区块链的全节点会构造一条验证链,这条链是从当前区块追溯到创世区块及交易组成,建立一个UTXO数据库,通过验证该交易的UTXO是否被支付来证实交易有效性。SPV节点不能构建所有可用消费的UTXO的全貌,这是由于它们不知道完整的交易信息,它采用了按需获取局部视图的方法。

SPV节点可以通过merkle路径来证明一个交易存在,但不能证明某个交易不存在。

SPV节点使用的是一条getheaders消息,而不是getblocks消息来获得区块头。发出响应的对等节点将用一条headers消息发送多达2000个区块头。这一过程和全节点获取所有区块的过程没什么区别。SPV节点还在与对等节点的连接上设置了过滤器,用以过滤从对等节点发来的未来区块和交易数据流。

image

任何目标交易都是通过一条getdata的请求来读取的。对等节点生成一条包含交易信息的tx消息作为响应。
由于SPV节点需要读取特定交易从而选择性地验证交易,这样就又产生了隐私风险。与全区块链节点收集每一个区块内的全部交易所不同的是,SPV节点对特定数据的请求可能无意中透露了钱包里的地址信息。例如,监控网络的第三方可以跟踪某个SPV节点上的钱包所请求的全部交易信息,并且利用这些交易信息把比特币地址和钱包的用户关联起来,从而损害了用户的隐私。

在引入SPV节点/轻量级节点后不久,比特币开发人员就添加了一个新功能:Bloom过滤器,用以解决SPV节点的隐私风险问题。Bloom过滤器通过一个采用概率而不是固定模式的过滤机制,允许SPV节点只接收交易信息的子集,同时不会精确泄露哪些是它们感兴趣的地址。