在之前的文章中提到了SPV节点,一直说要写文章说明是什么是比特币SPV节点。网上有很多文章来描述这个问题,我之前也写过相关的文章,有兴趣的话可以回去查阅相关问题。简单来说,SPV节点最主要的特点就是:只存储头信息(BlockHeader)。所以他做到了存储数据规模大幅减少,只有完整数据的千分之一的水平。所以SPV节点适合在存储有限的设备上运行,比如手机客户端。但是本文的重点不是重复讲述SPV节点的概念的,而是换一个角度,从比特币网络协议入手,描述如何从全节点下载SPV所需的数据到节点上(SPV节点上)。这个所需的的信息,就是merkerblock。
本文的关键字是:”SPV节点”,”比特币网络协议”,”BIP47”
交易的基本问题
在探讨如何和比特币网络进行信息交互之前先解释一个基本问题
实现比特币交易的形式有哪些?
虽然如此描述这个问题并不准确,但我还是按照自己的理解去解答这个问题,同时参照以上的图片来辅助说明。实现交易的形式有两类
- 借助于比特币全节点
- 借助于比特币的SPV节点
对于1,很明确。比特全节点有区块链完整的信息。获取任何交易信息,发起交易和广播不在话下。
重点需要解释的是2。借助于比特币的SPV节点。之前的文章提到过,SPV节点需要根据merkel root来验证交易的存在(注意用词,是验证交易的存在,反之是不行的)。那把这个问题提前一步,变成SPV节点如何获取自己所需的merkle root数据,这个图就是来描述这个问题的
一个SPV的体系如图所示,wallet连接一个SPV的节点,因为SPV节点不存在完整的数据,所以SPV必然连接其他的完整的节点以获取自己所需要的数据。节点Peers 指代的就是其他的Full Node。
实际情况中,如果要实现一个用户意义上的钱包(这个意义指:用户用这样一个App就可以实现比特币交易)wallet 和 SPV节点必然紧密相连。这里为了说明情况,所以特意分开。SPV节点和其他的Full Node之间是建立P2P网络的,他们直接交互就需要借助比特币网络协议。
本文假定SPV Node已经发现到了其他的Full Node(指已经知道了其他节点的IP),节点发现和建立连接也是比特币网络协议中的专门话题,本文为了简化,暂不对其进行探讨。以后有机会再出一篇文章。当节点建立连接之后,SPV节点将向节点发送LoadFilter 消息,这条消息会设置和布隆过滤器有关的参数,比如filter,需要的的Hash Function个数等。Full Node会根据设置调整自己的布隆过滤器。接下来,SPV节点发送getdata消息给Full Node。因为第一次设置了布隆过滤器,所以全节点会利用布隆过滤器过滤掉无关信息,把SPV感兴趣的信息发送回来。这样就形成了一次完整的信息交互。
以上的描述中,需要明确一个小的问题
Bloom Filtter在哪里?
Bloom Filtter在全节点上。一个支持布隆过滤器的节点,需要自己实现和Bloom Filtter 有关的方法。这样它就可以根据其他节点回传回来的loadfiltter 消息来设置。等到SPV节点需要getdata的时刻,利用之前已经设置好的Bloom filtter来过滤信息。发送merker root回去。
以上的内容,希望参考bip47的文档来理解
如何从比特币P2P网络中获取需要的数据
以上的内容介绍过之后,其实其实如何从比特币P2P网络中获取信息就已经说明了,不过为了文章的完整性,按照之前的惯例列出步骤。以下步骤假定已经确定对等节点的ip,且我们需要的数据为 merkerblock
- SPV节点向Full Node发送 version 消息
- 等待对等节点发送 verack 消息之后,SPV节点向Full Node发送 verack 消息
- SPV节点发送 loadfilter 消息给Full Node
- SPV节点发送 getdata 消息给Full Node
- Full Node 回传 merkerblock 消息给SPV节点
其中前两个节点为握手的过程,必须有前两个步骤才可以正常的发送消息。比特币网络信息的交互全部依赖这种Message的传递。至于消息具体该怎么样请参考比特币网络协议的官方wiki和bitcoin-refrence。因为实际发送的数据和比特币网络协议wiki数据有出入,所以这两份材料要对照进行。另外比特币节点之间建立的是TCP链接。如果需要自己写代码的话,可以使用tokios这样的库(parity-bitcoin也借助这个库)。因为rust写起来相对麻烦,下面给出一个python的实例,体现这个过程
1 | from time import sleep |
简单说明,网络的协议中的数据为hex小端编码。消息遵循的形式为messageHeader+payload。其中payload为具体消息的形式。比特币网络协议中的任何消息都遵循这个格式。
1 | python get-merkle.py | nc localhost 8333 | hd |
按以上格式运行即可。(假定节点搭建在本地,采用默认端口)。nc为netcat,hd为hexdump工具。
给出参考的资料