主页 > imtoken安卓app > P2P 网络 - 比特币开发指南

P2P 网络 - 比特币开发指南

imtoken安卓app 2023-04-04 07:40:16

P2P 网络 - 比特币开发指南

原始链接:#operating-modes

翻译:terryc007

版本:1.0

比特币开发指南

1. 区块链

2.交易

3.合同

4. 钱包

5. 付款处理

6、工作模式

7.P2P网络

8.挖矿

比特币网络协议允许所有节点共同维护一个p2p网络,实现区块和交易数据的交换。 全节点下载并验证每个区块和交易,然后将其转发给其他节点。 存档节点是存储单个区块链并将历史块提供给其他节点的完整节点。 作物节点是不保存区块链的全节点。 许多 SPV 客户端也使用比特币网络协议连接全节点。

由于共识规则不涉及网络,比特币程序可以选择不同的网络和不同的协议。 比如有的矿工使用高速区块转发网络,有的提供SPV级安全钱包,有的使用专业的交易信息服务器。

为了完善一个实用的比特币P2P网络示例,本章使用Bitcoin Kernel作为典型的全节点,BitcoinJ作为典型的SPV客户端。 这两个程序都很灵活,因此这里只讨论它们的默认行为。 同时考虑到隐私,下面例子中的ip地址已经替换为RFC5737保留的IP地址。

节点发现

当程序第一次启动时,它不知道任何活动节点的 IP 地址。 为了发现一些全节点的ip地址,它们会查询一个或多个硬编码在Bitcoin Kernel或BitCoinJ中的DNS域名,返回的结果应该包含一个或多个DNS A记录,其中一些是可接受的ip地址新连接的全节点。 例如,使用 Unix 命令 dig:

;;QUESTION SECTION: 
;seed.bitcoin.sipa.be. IN A 
;; ANSWER SECTION: 
seed.bitcoin.sipa.be. 60 IN A 192.0.2.113 
seed.bitcoin.sipa.be. 60 IN A 198.51.100.231 
seed.bitcoin.sipa.be. 60 IN A 203.0.113.183 

比特币当前价格行情_当前连接的比特币测试网络信息_当前比特币的价格是多少了

[...]

DNS 种子由比特币社区的成员维护。 有的提供动态DNS种子服务器,通过扫描比特币网络自动获取活跃节点的ip地址; 其他人提供一些静态 DNS 种子,需要手动更新,但他们很可能会提供非活动节点的 ip 地址。 不管是动态的还是静态的DNS种子,如果节点运行在主网的8333端口,或者运行在测试网的18333端口,都会被添加到DNS种子中。

DNS 种子结果未经授权,恶意 DNS 种子运营商或中间人攻击者可以只返回攻击者控制的节点的 ip 地址,隔离攻击者自己网络中的节点,并给他们虚假交易,阻止数据. 因此,程序不应只依赖一个 DNS 种子。

一旦程序连接到比特币网络,它的节点就可以开始向其他节点发送带有网络中其他节点的 IP 地址和端口号的 addr 消息。 这就提供了一个完整的去中心化节点发现方法。 比特币内核会将已知节点的信息保存在本地数据库中。 通常,程序下次启动时,不需要使用DNS种子,直接连接这些节点即可。

然而,节点通常会离开网络或更改 ip 地址,因此当程序启动时,可能需要多次尝试连接到比特币网络。 这增加了连接到比特币网络的延迟,导致用户在发送交易或检查付款状态之前必须等待一段时间。

为了避免这种延迟,BitcoinJ 总是使用动态 DNS 种子来获取那些被确定为活动的节点的 IP 地址。 比特币内核还试图减少延迟并避免使用不必要的 DNS 节点。 如果比特币内核在其节点数据库中有记录,则至少需要 11 秒才能连接到一个节点。 失败后,会使用DNS节点获取ip地址; 如果连接在 11 秒内成功建立,则不会发送到 DNS 种子查找。

Bitcoin Kernel和BitcoinJ在其他特定版本的第一版发布时,都在代码中硬编码了当时一些活跃节点的IP地址和端口号。 比特币内核内置了一个自动回调选项,当 60 秒内没有 DNS 种子服务器响应查询时,比特币内核将开始尝试连接这些节点。

作为回调选项,Bitcoin Core还提供了几种命令行连接选项,包括通过IP地址从指定节点获取节点列表,或者直接通过IP地址与指定节点建立持久连接。 使用 -help 获取命令行详细信息。 BitcoinJ也可以通过编程实现这样的功能。

资源:Bitcoin Seed,一个管理多个比特币内核的程序,BitcoinJ 使用的 DNS 种子。 比特币内核 DNS 种子政策。 BitcoinJ 中的硬编码节点 IP 地址是使用 makeseeds 脚本生成的。

连接到节点

通过向远程节点发送版本消息与其他节点建立连接,该消息包括软件版本号、块和当前时间。 远程节点也返回一个版本信息。 然后,他们向其他节点发送 verack 消息,表明他们已经建立了连接。

一旦建立连接,客户端就可以向远程节点发送getaddr和addr消息,以获取其他节点信息。

为了保持与节点的连接,客户端在下线前30分钟向其他节点发送消息。 如果节点在 90 分钟内没有返回消息,则客户端认为连接已关闭。

初始化块下载

在全节点可以验证未确认的交易之前,最近开采的区块,它必须下载并验证最佳区块链上的所有区块(从创世区块到最顶层区块)。 这称为初始块下载 (IBD) 或初始同步。

虽然“初始化”意味着这个方法只会被使用一次,但是在下载大量区块时可以随时使用,比如之前连接的节点已经离线很长时间了。 在这种情况下,节点可以使用 IBD 方法下载自上次启动以来开采的所有区块。

在任何时候,只要比特币核心本地最佳区块链上最新区块的区块头时间超过24小时,就会使用IBD方式获取新区块。 如果本地最佳区块链比本地最佳区块链多144个区块(也就是说本地最佳区块链24小时没有更新),比特币内核0.10。 将使用IBD方法。

先阻止

Bitcoin Core(最高版本 0.9.3)使用一种简单的初始块下载(IBD)方法,我们称之为块优先。 它的目标是从最好的区块链中顺序下载区块。

当前比特币的价格是多少了_比特币当前价格行情_当前连接的比特币测试网络信息

当一个节点第一次启动时,它在其本地最佳区块链中只有一个块——硬编​​码创世块(块 0)。 该节点会选择一个远程节点(也称为同步节点),并向其发送getblocks消息,如下图所示:

当前比特币的价格是多少了_比特币当前价格行情_当前连接的比特币测试网络信息

在 getblocks 消息的头哈希字段中,新节点发送该块的唯一头哈希 - 创世块(6fe2...0000 内部字节顺序)。 同时将stop hash字段设置为全0,得到最大的返回结果。

一旦同步节点收到此 getblocks 消息,它就会使用第一个哈希头在其本地最佳区块链中搜索具有此哈希头的块。 如果找到块 0 的匹配项,它将从块 1 开始并返回 500 个块的列表(getblocks 消息返回的最大数量)。 它使用 inv 消息发送这些阻止列表。 如下所示:

当前连接的比特币测试网络信息_当前比特币的价格是多少了_比特币当前价格行情

当前比特币的价格是多少了_比特币当前价格行情_当前连接的比特币测试网络信息

库存是比特币网络上唯一的识别信息。 每个清单都包含一个类型,一个对象实例的唯一标识符。 对于一个区块来说,这个唯一标识就是区块头的哈希值。

inv消息中区块库存信息的顺序与区块链中的顺序相同,所以第一个inv消息包含区块1到区块501的库存信息。 (例如,区块 1 的哈希值为 4860...0000,如上所示)

IBD节点使用接收到的清单列表,通过getdata消息从同步节点获取128个区块。 如下所示:

当前比特币的价格是多少了_比特币当前价格行情_当前连接的比特币测试网络信息

对于区块优先节点来说,按顺序请求和发送区块非常重要,因为每个区块头都会引用它之前的区块头。 这意味着 IBD 节点在收到父块之前无法完全验证该块。 区块无法被验证的原因是那些没有收到父区块的区块是孤儿区块; 这将在下一小节中详细介绍。

一旦收到 getdata 消息,同步节点会将请求的块返回给 IBD 节点。 每个块都被序列化为一种块格式,并与它自己的块消息一起发送。 发送的第一个块消息(block1)如下图所示。

当前比特币的价格是多少了_比特币当前价格行情_当前连接的比特币测试网络信息

IBD 节点下载每个区块,验证它,然后获取下一个未请求的区块,并维护一个 128 个区块的下载队列。 当它获得库存中的所有块时,它会向同步节点发送另一个 getblocks 消息以获取最多 500 个块的库存。 第二条getblocks消息包含多个区块头哈希值,如下图所示:

当前比特币的价格是多少了_比特币当前价格行情_当前连接的比特币测试网络信息

一旦同步节点收到第二个 getblocks 消息,它就会在其本地最佳区块链中按照它们收到块头哈希值的顺序一个接一个地查找匹配的块。 如果找到匹配的块,它会返回从该块之后的块开始的 500 个块的清单。 如果它没有找到匹配的哈希值(除了那个截止哈希值),它会假设两个节点只有 block0 是相同的,所以它会从 block1 开始发送一个 inv 消息。

像这样的重复查找允许同步节点发送有用的清单,即使 IDB 节点的本地区块链是从同步节点的本地区块链分叉出来的。 IBD 节点上的区块越接近区块链的顶部区块,分叉检测就越有用。 [?]

当 IBD 节点收到第二个 inv 消息时,它使用 getdata 消息请求这些块。 同步节点会返回区块消息给IBD节点。 然后 IBD 节点将继续使用 getblocks 消息请求更多库存。 重复这个过程,直到 IBD 节点同步整个区块链。 此后,IBD 节点通过普通块广播(在后续部分中描述)接受新块。

block first的优点和缺点

块优先的主要优点是简单。 它的主要缺点是它只依赖于一个同步节点来下载块数据。 这有几个影响:

在 Bitcoin Core 0.10.0 中,所有这些问题都在 head-first IBD 方法中部分或全部解决。

资源:下表总结了本节中提到的消息。 单击消息列中的链接可查看消息的参考页。

消息getblocksinvgetdatablock

从→到

炎症性肠病→同步

同步 → IBD

炎症性肠病→同步

同步 → IBD

内容

一个/多个标头哈希

当前连接的比特币测试网络信息_比特币当前价格行情_当前比特币的价格是多少了

最多 500 个区块库存(唯一 ID)

一个/多个块库存

序列化块

块头优先

Bitcoin Core 0.10.0 使用 header-first IBD 的初始块下载方法。 它的目标是首先下载最好的区块链头部,对其进行部分验证,然后并行下载相应的区块。 这解决了块优先 IBD 方法中的几个问题。

当前比特币的价格是多少了_比特币当前价格行情_当前连接的比特币测试网络信息

当一个节点第一次启动时,它在其本地最佳区块链中只有一个块——硬编​​码创世块(block0)。 它会选择一个远程节点,这里我们称之为同步节点,然后向同步节点发送一个getheaders消息。 如下所示:

当前比特币的价格是多少了_比特币当前价格行情_当前连接的比特币测试网络信息

在 getheaders 消息的块头哈希字段中,新节点仅发送其唯一的本地块头哈希 - 创世块哈希(6fe2…0000 内部字节顺序)。 同时,所有的哈希字段都会被设置为0,以获得最大的哈希值。

一旦同步节点收到 getheaders 消息,它就会获取第一个区块头哈希值,并使用它在本地最佳区块链中搜索区块。 如果 block0 匹配,则同步节点将从 block1 开始并返回 2000 个区块头哈希。 它在标头消息中发送这些块标头。 如下所示:

当前比特币的价格是多少了_比特币当前价格行情_当前连接的比特币测试网络信息

IBD 节点可以对区块头进行部分验证,这是通过确保区块头的所有字段都遵循共识规则,并且区块头的哈希值低于 nBits 字段的目标阈值来实现的。 (要完全验证一个区块,还是需要获取区块中的所有交易)

IBD 节点在对区块头进行部分验证后,可以并行做两件事:

下载更多区块头:IBD 节点可以向同步节点发送另一个 getheaders 消息,以获取最佳区块链上的下一批 2000 个区块头。 可以立即验证这些headers,重复批量发送请求,直到从sync节点收到的headers消息包含的headers少于2000个,说明没有headers了。 在撰写本文时,完成整个区块头同步需要不到 200 次往返,这需要下载大约 32MB 的数据。

一旦 IBD 节点收到少于 2000 个区块头的 headers 消息,它就会向其所有外部节点发送一条 getheaders 消息,以查看它们最好的区块链是什么。 通过比较它们返回的消息,很容易判断它下载的区块头是否属于通过其外联节点的最佳区块链。 这意味着即使没有检查点,也可以非常快速地找到不诚实的节点(只要 IBD 节点连接到至少一个诚实节点,如果没有找到诚实节点,比特币核心将继续提供检查点)。

下载区块:在IBD继续下载区块头的同时,当区块头下载完成后,IBD节点会请求并下载每个区块。 IBD 节点通过对块头链中的块进行散列来创建 getdata 消息。 在block-first中,getdata中的block header hash需要inv消息中的block inventory提供。 但在区块头中,并不需要从同步节点获取区块,可以从任何其他全节点获取。 (尽管并非所有完整节点都存储所有块。)这允许它并行获取块,同时避免下载速度受到单个同步节点带宽速度的限制。

为了从更多节点加载数据,Bitcoin Core 一次从单个节点获取最多 16 个块,最多有 8 个传出连接。 这意味着使用具有区块头优先级的比特币内核,在IBD阶段,最多可以同时发起128个区块请求。 (与区块优先级比特币内核的最大请求数相同)

当前比特币的价格是多少了_比特币当前价格行情_当前连接的比特币测试网络信息

比特币内核采用的区块头优先模式,以1024个区块为移动下载时间窗口,最大限度地提高下载速度。 下载时间窗口中的最低块是下一个要验证的块。 如果轮到区块验证,但还没有下载区块,比特币内核会等待至少2秒,等待区块从停顿节点下载。 如果下载还没有完成,比特币内核会断开与停顿节点的连接,并尝试连接到另一个节点。 例如,如上图所示,如果节点A在2秒后无法发送区块3,则节点A将断开连接。

一旦 IBD 节点同步了整个区块链,它将接收通过普通区块广播的区块,这将在下一节中讨论。

资源:下表总结了本节中提到的消息。 单击消息列中的链接可查看消息的参考页。

消息 getheadersheadersgetdatablock

发送→接收

炎症性肠病→同步

同步 → IBD

当前连接的比特币测试网络信息_比特币当前价格行情_当前比特币的价格是多少了

IBD→许多

许多→IBD

内容

一个/多个区块头哈希

最多 2000 个区块头哈希

从哈希头派生的一个/多个块的清单

序列化块

块广播

当矿工找到一个新区块时,它会使用以下方法将该区块广播到其节点:

默认情况下,Bitcoin Core 直接向那些发送 sendheaders 信号的节点广播区块,并使用标准区块转发到其他节点。 Bitcoin Core 接受以上述所有方式转发的块。

全节点验证接收到的块并使用标准块转发将其转发给其对等方。 下面的简表重点列出了这个过程中用到的消息(Relay、BH、HF、SPV分别对应转发节点、块优先级节点、块头优先级节点、SPV客户端;Any-表示方法节点)

消息 invgetdatageheadersheaders

从→到

继电器→任何

BF→继电器

高频→继电器

继电器→高频

内容

新区块列表

新区块列表

在HF节点最佳区块头链(BHC)上,一个/多个区块头哈希

最多2000个区块头,连接HF节点的BHC和转发节点的BHC

信息

堵塞

默克尔块

当前比特币的价格是多少了_比特币当前价格行情_当前连接的比特币测试网络信息

发送

从→到

继电器→BF/HF

中继→SPV

中继→SPV

内容

新的序列化块

新区块的修改后的 Merkle 区块

来自匹配布隆过滤器的新区块的序列化交易

孤块

区块优先节点可能会下载孤立的区块。 所谓孤块,是指前一个区块头的哈希字段,指向的区块还没有出现过。 也就是说,孤立块没有已知的父块(与陈旧块不同,陈旧块有父块,但它们不属于最新的区块链)。

当前比特币的价格是多少了_比特币当前价格行情_当前连接的比特币测试网络信息

当区块优先级节点下载一个孤块时,该节点不会立即验证,而是向发送孤块的节点(广播节点)发送getblocks消息,广播节点返回一个inv消息,其中包含blocks ,该列表包含该节点丢失的区块信息(最多500个); 下载节点使用getdata消息请求这些块; 然后广播节点将以块消息的形式发送这些块。 然后下载节点将验证这些块。 一旦前一个孤块的父块被下载并验证,下载节点将验证前一个孤块。

为了避免复杂性,区块头优先级节点通常在使用 getdata 消息请求区块之前使用 getheaders 消息请求区块头。 广播节点会向下载节点发送一个headers消息,其中包含它认为下载节点需要到达最佳区块链顶部的所有区块头(最多2000个); 每个块头都会指向它的父块,所以当下载节点收到块消息时,该块不应该是孤块——因为它所有的父块哈希都是已知的(即使它们还没有被验证)。 如果接收到的块是块消息中的孤立块,则头优先节点会立即丢弃它。

然而,丢弃孤立块意味着头优先节点忽略矿工以主动推送方式发送的孤立块。

交易广播

要向另一个节点发送交易,需要发送 inv 消息。 如果节点收到 getdata 消息,它会使用 tx 发送交易。 节点收到交易后,如果是有效交易,则按照同样的方式转发交易。

内存池

全节点可以跟踪可以包含在下一个块中的未确认交易。 这对于挖掘这些或所有交易的矿工来说是非常必要的,但对于任何想要跟踪未确认交易的节点也是有用的,例如为节点的 SPV 节点提供未确认交易信息服务。

因为在比特币中,未确认的交易没有持久状态,比特币内核将它们保存在内存中,也称为内存池。 当节点关闭时,内存池中的所有交易都将丢失,除了那些保存到钱包的交易。 这意味着在节点重启后,那些还没有被挖出的未确认交易会慢慢从网络中被丢弃,或者部分未确认交易会因为内存不足而从内存池中删除。

那些未包含在区块中的交易将成为陈旧的区块,并且它们可能会被添加回内存池。 如果替换区块已经包含这些新添加到内存池中的交易,它们将被立即删除。 在比特币核心中,是这样的,从最好的区块链的顶端(最高的区块)开始,一个一个地删除链上的陈旧区块。 随着每个块被删除,其交易被添加回内存池。 删除过时的块后,在区块链的顶部逐个添加替换块。 添加块时当前连接的比特币测试网络信息,块中确认的交易将从内存中删除。

SPV 客户端没有内存池,因为它们不需要转发交易。 他们无法独立验证一笔交易是否包含在一个区块中,SPV 客户端只能花费 UTXO,因此他们不知道哪笔交易是合格的当前连接的比特币测试网络信息,可以打包到下一个区块中。

作弊节点

需要注意的是,对于区块和交易的两次广播,系统都有惩罚那些作弊节点的机制。 它们会通过发送错误的信息占用带宽和计算资源。 如果节点的 banscore 值大于 -banscore= 中设置的阈值,它将在 -bantime= 中设置的持续时间内被禁止,默认为 86400 秒(24 小时)。

警告

在比特币内核的早期版本中,允许开发者和受信任的社区成员向用户发出比特币警告,以通知用户比特币网络存在严重问题。 该消息系统在比特币内核 0.13.0 中已被弃用; 但是,内部警告、分叉检测警告和 -alertnofity 功能仍然保留。