知识库详情
BSV知识库

以“词条释义”的形式对比特币区块链技术中经常涉及的术语及基本概念进行专业化的阐释和解读。

点对点协议
返回上级
BSV区块链编辑 2022-04-14 18:01 781

 本文介绍运行BitcoinSV(BSV)节点客户端的节点使用的比特币网络协议,节点之间使用该协议互相传递交易和区块信息。这是目前节点之间交流账本信息(包括有效交易、区块发现消息等)的标准方式。

挖矿使用的协议参见Getminingcandidate

消息结构

已知的 magic值:

可变长度的整数

整数可以根据要表示的值进行编码以节省空间。可变长度的整数始终位于长度可变的数据类型的数组/向量之前。这样一来,较长的数字就可以以简短的方式编码。

在BitcoinQT中这种编码称为“CompactSize”。现代版本的BitcoinQT也有 CVarInt类,它使整数更为紧凑,被用于本地存储。它与此处描述的“CompactSize”不兼容,而且CVarInt也不是比特币协议的一部分。

可变长度的字符串

可变长字符串可以将该字符串本身跟随在可变长度的整数后来存储。

网络地址

当我们需要使用一个网络地址时,就会使用以下这样的结构。网络地址在版本消息中没有以时间戳为前缀。

网络地址结构的十六进制转储示例:

0000   01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
0010   00 00 FF FF 0A 00 00 01  20 8D                    ........ .

Network address:
 01 00 00 00 00 00 00 00                         - 1 (NODE_NETWORK: see services listed under version command)
 00 00 00 00 00 00 00 00 00 00 FF FF 0A 00 00 01 - IPv6: ::ffff:a00:1 or IPv4: 10.0.0.1
 20 8D                                           - Port 8333

Inventory向量

节点使用Inventory向量通知其它节点他们拥有的数据,或想要请求的数据。库存向量由以下数据格式组成:


当前,对象类型被定义为以下可能性之一:

其它数据类型的值是考虑到将来的需求而预留的。

区块头消息

节点在响应getheaders消息时,会将区块头放在区块头数据包里发送出去。区块头的数据结构为:

差分编码

以下CompactSize的几种用法是“差分编码”的。这些用法之中,并没有使用原始索引,编码的数字是当前索引和前一个索引之间的差减去一。例如,第一个索引为0表示实际索引为0,第二个索引为0表示实际索引为1,依此类推。

PrefillTransaction消息

以下是在HeaderAndShortIDs中使用PrefillTransaction 结构来显式提供一些交易的列表。

详细信息请参阅BIP 152:https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki

HeaderAndShortIDs消息

以下的HeaderAndShortIDs结构用于传播区块头、匹配已可用交易的短交易ID,以及我们估计对等方可能会丢失的少数交易等场景中。由于内容较为复杂,表格保留了英文原版内容:

BlockTransactionsRequest消息

BlockTransactionsRequest 结构被用来列出被请求的区块中的交易索引。

BlockTransactions消息

BlockTransactions结构用于根据请求提供区块中的一些交易。

短交易ID消息

短交易ID也是用来指代一笔交易,但是它并不是一个256位的交易ID哈希值,而是6字节。短交易ID的计算方法为:

  1. 对区块头进行单次SHA256哈希运算,并附加随机数(小端)
  2. 运行SipHash-2-4,输入为交易ID,密钥 (k0/k1)分别设置为上述哈希值的前两个小端的64位整数。
  3. 从SipHash的输出中删除2个最大的有效字节,将长度缩减至6字节。

 

消息类型

协议版本消息

当节点创建输出连接时,节点会告知其他节点它所使用的版本。远程节点将根据其版本进行响应。只有当双方交换了他们的版本号,他们才能进行进一步的通信。

有效荷载:


如果版本包被对方节点接受,对方节点将发送回一个“verack”包。

目前有以下服务:

Verack消息

verack消息是用来回复版本号信息的。此消息仅包含带有命令字符串的“verack”的消息头。

verack 消息的十六进制转储如下:

0000   F9 BE B4 D9 76 65 72 61  63 6B 00 00 00 00 00 00   ....verack......
0010   00 00 00 00 5D F6 E0 E2                            ........

Message header:
 F9 BE B4 D9                          - Main network magic bytes
 76 65 72 61  63 6B 00 00 00 00 00 00 - "verack" command
 00 00 00 00                          - Payload is 0 bytes long
 5D F6 E0 E2                          - Checksum (little endian)

addr消息

提供有关网络里已知节点的信息。通常情况下,3小时内无通知的节点会被忽略掉。

注意:从版本 31402 开始,地址以时间戳为前缀。如果不存在时间戳,除非确实确认它们已启动,否则节将不会将地址传播给对方节点。

addr消息的十六进制转储示例:

0000   F9 BE B4 D9 61 64 64 72  00 00 00 00 00 00 00 00   ....addr........
0010   1F 00 00 00 ED 52 39 9B  01 E2 15 10 4D 01 00 00   .....R9.....M...
0020   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 FF   ................
0030   FF 0A 00 00 01 20 8D                               ..... .

Message Header:
 F9 BE B4 D9                                     - Main network magic bytes
 61 64 64 72  00 00 00 00 00 00 00 00            - "addr"
 1F 00 00 00                                     - payload is 31 bytes long
 ED 52 39 9B                                     - payload checksum (little endian)

Payload:
 01                                              - 1 address in this message

Address:
 E2 15 10 4D                                     - Mon Dec 20 21:50:10 EST 2010 (only when version is >= 31402)
 01 00 00 00 00 00 00 00                         - 1 (NODE_NETWORK service - see version message)
 00 00 00 00 00 00 00 00 00 00 FF FF 0A 00 00 01 - IPv4: 10.0.0.1, IPv6: ::ffff:10.0.0.1 (IPv4-mapped IPv6 address)
 20 8D                                           - port 8333

Inv消息

Inv允许节点公告他们其对一个或多个事项的了解情况。此类型的消息可以不请自来,也可以作为对 getblocks 消息的回复。

有效负载(最多50,000个条目,将将超过1.8MB):

 getdata消息

getdata消息用于响应inv消息,可以检索特定对象的内容,通常在收到inv数据包后,过滤已知元素后发送。它可用于检索交易,但前提是该交易在内存池或中继集中,所以不允许对链中的交易进行任意访问,这样是为了避免客户端开始依赖具有完整交易索引的节点(现代节点则不会)。

有效负载(最多50,000个条目,刚好超过1.8 兆字节):

notfound消息

notfound消息是对getdata 消息的响应,当任何请求的数据项无法中继时,则发送本类型消息,例如,请求的交易不在内存池或中继集中时。

getblocks消息

将返回一个inv数据包,其中包含在区块定位器的对象中的最后一个已知哈希之后开始的区块列表,以先到者为准,最多数量为hash_stop或500个区块。

定位器哈希由节点按照它们在消息中出现的顺序进行处理。如果在节点的主链中找到区块哈希,无论是否达到请求的限制,都通过inv消息返回其子节点列表,同时忽略剩余的定位器。

若要接收下一个区块的哈希值,则需要用新的区块定位器对象再次发出getblocks消息。请记住,有些客户端可能会提供无效的区块,比如区块定位器包含了无效链分支的区块哈希。

下表为有效载荷:

要创建区块定位器哈希,需要不断推送区哈希值,直到回到创世区块。推回10 个哈希值后,后续的步骤将在每个循环时加倍区块数量:

// From libbitcoin which is under AGPL
std::vector block_locator_indexes(size_t top_height)
{
    std::vector indexes;

    // Modify the step in the iteration.
    int64_t step = 1;

    // Start at the top of the chain and work backwards.
    for (auto index = (int64_t)top_height; index > 0; index -= step)
    {
        // Push top 10 indexes first, then back off exponentially.
        if (indexes.size() >= 10)
            step *= 2;

        indexes.push_back((size_t)index);
    }

    //  Push the genesis block index.
    indexes.push_back(0);
    return indexes;
}

请注意,允许发送一个区块哈希,这是最小的数量。然而,区块定位器对象的目的是检测调用者主链中的错误分支。如果对等方检测到您脱离了主链,它将发送早于您的最后一个已知区块的块哈希。因此,如果您只发送最后一个已知的哈希值,并且它不在主链上,则对方节点将从区块高度为1的区块开始检测。

Getheaders消息

返回一个headers数据包,其中包含在区块定位器对象中最后一个已知哈希之后开始的区块头,以先到者为准,最多为hash_stop或2000个区块。要接收下一个区块头,需要使用新的区块定位器对象再次发出getheaders。请记住,有些客户端可能会提供无效的区块,比如区块定位器包含了无效链分支的区块哈希。

有效载荷:

对于此数据包中的区块定位器对象,与getblocks 数据包的规则相同。

Tx消息

tx消息描述了比特币交易,是对getdata的回复。当应用布隆过滤器时,tx 对象会自动发送以匹配merkleblock之后的交易。

TxIn 由以下字段组成:

OutPoint 结构由以下字段组成:

Script结构由一系列与交易价值相关的信息和操作组成。

TxOut结构由以下字段组成:

Block消息

发送block消息是为了响应getdata消息,该消息从区块哈希中请求交易信息。

每个区块都可以由一个哈希值指代,该哈希值是根据区块结构的前6个字段(版本、prev_block、merkle_root、时间戳、bits、随机数),经过SHA256算法计算出来的,而不是根据完整的区块信息计算出来的。

Headers消息

Headers消息是对getheaders的响应,返回区块头数据。

有效荷载:

请注意,此数据包中的区块头数据还包括交易计数器(一个var_int,因此每个区块头的大小可能超过 81 个字节),和这与矿工之间交换的区块头数据不同。

getaddr消息

getaddr消息是向节点发送请求,询问进行有关已知活动的对等方的信息,以查找网络中的潜在节点。接收到此消息后,节点进行的响应是,从已知活动对等方的数据库中,向一个或多个对等方发送一个或多个addr消息。一般我们推测,如果一个节点在过去三个小时内一直在发送消息,那么它很可能处于活动状态。注意,此消息不传输附加数据。

mempool消息

mempool消息用于向节点发送请求,询问节点已验证但尚未确认的相关交易的信息。收到此消息后,节点做出的响应是发送一条inv消息,这之中包含节点内存池中所有交易的交易哈希。此消息不传输附加数据。

mempool消息的规范在BIP 35中。从BIP 37开始,如果加载了布隆过滤器,则仅回复与过滤器匹配的交易。

checkorder消息

checkorder消息用于IP 交易。由于IP交易已被比特币网络弃用,因此该类型的消息已不再使用。

submitorder消息

submitorder消息用于IP 交易。由于IP交易已被比特币网络弃用,因此该类型的消息已不再使用。

reply消息

reply消息用于IP 交易。由于IP交易已被比特币网络弃用,因此该类型的消息已不再使用。

ping消息

发送ping消息主要是为了确认TCP/IP连接是否仍然有效。如果传输的过程中出现了错误,则该错误就将被假定为连接已关闭,并且该地址不再是对等节点。

有效载荷:

pong消息

通过发送pong消息来响应ping消息。在现代协议版本中,使用ping中包含的随机数来生成pong响应。

有效载荷:

reject消息

当消息被拒绝时发送reject消息。

有效载荷:

C语言代码如下:

 filterload、filteradd、filterclear、merkleblock消息

这些消息与它们所连接的布隆过滤有关,并在BIP 0037中进行了定义。

filterload命令的定义如下:

下文描述了布隆过滤器的算法,以及如何根据所需的误报率来选择 nHashFuncs 和过滤器大小。

收到 filterload 命令后,远端节点会立即将限制其广播的交易(在inv数据包中),使其与过滤器相匹配,其中匹配算法如下述。这些标志控制着匹配算法的更新。

filteradd 命令的定义如下:

这段数据的大小必须小于或等于520字节(任何可能匹配的对象的最大大小)。

给定的数据元素将被添加到布隆过滤器。在数据添加之前,必须使用filterload提供一个过滤器。如果一个新的密钥或脚本被添加到客户端钱包,同时它与网络存在连接,则此命令是有效的,它避免了重新计算,并向每个对等方发送全新的过滤器的需求(尽管这样做通常可以有效的维持匿名状态)。

filterclear命令本身并没有任何参数。

设置过滤器后,节点不仅停止广播不匹配的交易,它们还可以提供过滤后的区块。过滤后的区块由merkleblock消息定义,它定义如下:

在一个 merkleblock 之后,匹配布隆过滤器的交易会自动在tx消息中发送。

你可以在以下开发人员示例中找到创建布隆过滤器、加载默克尔块和解析部分默克尔块树的指导:https://bitcoin.org/en/developer-examples#creating-a-bloom-filter

Sendheaders消息

这个消息用于直接对外请求区块头。收到此消息后,允许但不强制要求节点通过headers命令(而不是inv命令)广播新区块。

需要协议版本>= 70012 或比特币核心版本>= 0.12.0 支持此消息。

此消息不传输附加数据。详细信息,请参阅 BIP 130

feefilter消息

有效载荷的长度为8字节,它编码费率为 64 位整数值(LSB/小端)。该值代表最低的交易手续费率,用“每1000字节需要多少聪”来表示。

收到feefilter消息后,允许但不强制要求节点过滤掉低于自身费率的交易。

费用过滤器附加在用于交易的布隆过滤器上,因此如果SPV客户端想要加载布隆过滤器并发送费用过滤器消息,那么,只有两个过滤器都通过的交易才会被传输出去。

从内存池消息生成的Inv消息也受费用过滤器的约束(如果存在)。

更多详细信息,请参阅BIP 133 。

sendcmpct消息

  1. sendcmpct消息定义为包含1字节整数,并且紧接着为8字节整数的消息,其中pchCommand == “sendcmpct”。
  2. 第一个整数被解释为布尔值(并且其值必须是1或0)
  3. 第二个整数被解释为小端版本号。发送sendcmpct消息的节点当前必须将此值设置为1。
  4. 在收到第一个整数和第二个整数被设置为1的“sendcmpct”消息后,节点应该通过发送cmpctblock消息来广播新区块。
  5. 在收到第一个整数被设置为0的“sendcmpct”消息后,节点不应该通过发送cmpctblock消息来广播新区块,而是应该通过发送invs或headers 来广播新区块,就如BIP130所定义的那样。
  6. 在收到第二个整数被设置为非1的“sendcmpct”消息时,节点必须认为对等点没有收到消息(因为它表明该对等端将提供以外的代码在cmpctblock或/和其它消息当中)。这允许未来的版本下可以发送重复的sendcmpct消息,作为比特币版本握手(version handshake)的一部分。
  7. 节点应该在发送sendcmpct消息之前检查协议版本号是否>= 70014。
  8. 节点在收到来自对等方的 sendcmpct 消息之前,不得向对等方发送对 MSG_CMPCT_BLOCK 对象的请求。

仅>= 70014的协议版本支持此消息。更多详细信息,请参阅BIP 152

cmpctblock消息

  1. cmpctblock 消息被定义为包含序列化的HeaderAndShortIDs 消息和pchCommand== “cmpctblock”的消息。
  2. 在发送sendcmpct消息后,又收到cmpctblock消息后,节点应该为他们可用的(即在他们的内存池中)每个未确认交易计算短交易ID,并将每个交易ID与cmpctblock 消息中的每个短交易ID进行比较。
  3. 在找到已可用的交易后,没有所有可用交易来重建完整区块的节点应该使用 getblocktxn消息来请求丢失的交易。
  4. 节点不得发送cmpctblock消息,除非它们能够响应请求区块中每个交易的getblocktxn消息。
  5. 在未验证区块头已正确的提交了区块中的每个交易,并已经正确构建在现有链的顶部,同时具有有效的工作量证明的情况下,节点不得发送cmpctblock消息。节点可以在验证块中的每个交易是否有效的花费了现有 UTXO 集条目之前发送cmpctblock消息。

仅>= 70014的协议版本支持此消息。详细信息,请参阅 BIP 152

getblocktxn消息

  1. getblocktxn消息定义被为包含序列化的BlockTransactionsRequest消息和pchCommand == “getblocktxn” 消息。
  2. 在收到正确格式的getblocktxnmessage后,近期为该消息的发送方提供了cmpctblock消息(用于识别的区块哈希)的节点必须响应一个适当的blocktxn消息。这样的blocktxn消息必须非常准确的包含在getblocktxn索引列表中的每个交易,这些交易,在指定的索引处的区块中,且要按照请求的顺序排序。

仅>= 70014的协议版本支持此消息。详细信息,请参阅 BIP 152

blocktxn消息

  1. blocktxn消息被定义为包含序列化的BlockTransactions消息和pchCommand == “blocktxn” 的消息。
  2. 在接收到格式正确的请求blocktxn消息后,节点应该尝试通过以下方式重建完整块:
  3. 从原始cmpctblock中取出prefilltxn交易并将它们放置在标记位置。
  4. 对于来自原始cmpctblock的每个短交易ID,依次从blocktxn 消息或其他来源中找到相应的交易,并将其放置在区块中的第一个可用位置。
  5. 一旦区块被重建,它应该被正常的处置,请注意短交易ID偶尔会出现重复,但无论它们出现在何处,节点不得因此类碰撞而受到惩罚。

仅>= 70014的协议版本支持此消息。详细信息,请参阅 BIP 152

 

脚本消息

详见比特币脚本

网络
创世纪升级
mAPI(商户用API)
比特币测试网
矿工ID(Miner ID)
点对点协议
中本聪愿景
交易池