区块链技术

NetWork Messages Type(一)

2018-05-05  本文已影响23人  wolf4j

Data Messages

下面网络消息的请求提供了与交易(tx)和块(block)有关的数据

image.png

很多 data messages 使用 inventories 作为交易和块的唯一标识符,inventories 有一个简单的36字节的结构:

Bytes Name Data Type Description
4 类型标识符 uint32_t hash对象的类型,具体参照下表的类型识别码
32 hash char[32] 对象的SHA256(SHA256())hash 以内部字节顺序排列

当前可用的类型标识符如下:

类型标识符 Name Description
1 MSG_TX hash是一个TXID
2 MSG_BLOCK hash是block header的hash值
3 MSG_FILTERED_BLOCK hash是block header的hash值; 与MSG_BLOCK相同。在使用getdata消息时,作出响应的应该是merkleblock消息而不是block消息(但只有在先前配置了布隆过滤器时才有效)。仅用于getdata 消息中。
4 MSG_CMPCT_BLOCK hash是block header的hash值; 与MSG_BLOCK相同。当在getdata消息时,作出响应的应该是cmpctblock消息。仅用于getdata 消息中。
5 MSG_WITNESS_BLOCK hash是block header的hash值; 与MSG_BLOCK相同。在getdata消息中使用时,响应这个消息的应该是一个阻塞消息,其中包含使用witness序列化的witness的交易。仅用于getdata 消息中。
6 MSG_WITNESS_TX hash是一个TXID。在使用 getdata消息时,应该是一个交易消息响应,如果 witness 结构不是空的,将使用witness序列化。仅用于getdata 消息中。
7 MSG_FILTERED_WITNESS_BLOCK 保留供将来使用,不用于协议版本70015。

Block

块消息以序列化 blcok 部分中描述的格式传输单个序列化 block ,它可能因为如下两个原因被发送:

  1. GetData Response: 为了响应一个getdata消息,节点将会发送它(Block消息),该消息请求 inventory 的类型为MSG_BLOCK的块(假定节点具有可用于中继的该块)。
  2. Unsolicited:一些矿工可能会发送未经请求的block消息,向其他矿工广播新打包的block。 许多矿池也会做同样的事情,有可能是因为错误的配置导致从多个节点来发送block消息,而且可能不止一次地将同一个块发送给一些peers。

GetBlocks

getblocks消息会去请求一个 inv 消息,该消息提供从块链中的特定点开始的块头hash。它允许第一次断开连接或启动的一个 peer 获取它所需要的数据来请求它过去没有看到的块。

已经被断开的 peers 可能在其本地存储的块链中具有原先旧的块,所以getblocks消息允许请求peer在其本地链上的各个高度处向接收peer提供多个头哈希值。这允许接收方在该列表中查找它们共同拥有的最后一个头部hash,并回复所有后续头部hash。

注意:接收方的 peer 本身可以用包含原先旧块的头部hash的inv消息进行响应。由请求方同意轮询其所有 peers 以找到最佳分组链。

如果接收端在列表中找不到共同的头部hash,它将假定最后一个公共块是生成块(块0),因此它将在包含头部 hash 的 inv 消息中进行回复,头部 hash 以块1开头(第一个块在创世区块之后)。

Bytes Name Data Type Description
4 version uint32_t 协议版本号;与version信息中发送的一样。
Varies hash count CompactSize uint 提供的头部hash数量不包括停止哈希。除了整个消息的字节大小必须低于MAX_SIZE限制外,没有别的额外的限制;通常发送1到200次hash。
Varies block header hashs char[32] 一个或多个块头hash(每个32字节)以内部字节顺序排列。hash应该以块高度相反的顺序提供,因此最高高度hash列出最后一个,最低高度哈希列出最后一个。
32 Stop hash char[32] 请求最后一个头部hash的头部hash值;设置为全零以请求包含所有后续头部哈希的inv消息(最多500个将作为对此消息的回复发送;如果您需要超过500个,则需要发送另一个具有更高高度头部的getblocks消息hash作为块头hash字段中的第一个条目)。

以下带注释的hexdump显示了一个getblocks消息。 (消息头已被省略。)

71110100 ........................... Protocol version: 70001
02 ................................. Hash count: 2

d39f608a7775b537729884d4e6633bb2
105e55a16a14d31b0000000000000000 ... Hash #1

5c3e6403d40837110a2e8afb602b1c01
714bda7ce23bea0a0000000000000000 ... Hash #2

00000000000000000000000000000000
00000000000000000000000000000000 ... Stop hash

GetData

getdata 消息会从另一个节点一个或多个数据对象。这些对象由inventory请求,请求节点一般会通过inv消息的方式接收之前的这些对象。

getdata消息的响应可以是tx消息,block消息,merkleblock消息,cmpctblock消息或notfound的消息。

此消息不能用于请求任意数据,例如历史交易、不再存在于内存池或relay的集合中。如果全节点已经从其block数据库中修改旧的交易,则可能无法提供较早的块。由于这个问题,getdata消息通常只能用于通过发送inv消息向之前通告它的节点请求数据。

getdata消息的格式和最大大小限制与inv消息相同;只有消息header不同。

GetHeaders

在协议版本为31800的时候被添加

getheaders消息请求headers消息,该消息提供从块链中的特定点开始的block headers。它允许断开连接或第一次启动的peer去获取尚未看到的headers。

getheaders消息与getblocks消息基本相同,唯一的区别是:对getblocks消息的inv回复将包含不超过500个块头hash; headers回复getheaders消息将包含多达2000个block headers。

Headers

在协议版本为31800的时候被添加。

headers消息将block headers发送到先前用getheaders消息请求特定headers的节点。一个headers消息可能为空。

Bytes Name Data Type Description
Varies count compactSize uint block headers 的数量最多可达200​​0个。注意:headers-first sync假定发送节点将尽可能发送最大数量的 headers
Varies headers block_header Block headers:每个80字节的block header采用block headers 部分中描述的格式,并附加一个0x00后缀。这个0x00被称为交易的数量,但由于头部消息不包含任何交易,因此交易数量始终为零。

以下带注释的hexdump显示headers消息。 (消息头已被省略。)

01 ................................. Header count: 1

02000000 ........................... Block version: 2
b6ff0b1b1680a2862a30ca44d346d9e8
910d334beb48ca0c0000000000000000 ... Hash of previous block's header
9d10aa52ee949386ca9385695f04ede2
70dda20810decd12bc9b048aaab31471 ... Merkle root
24d95a54 ........................... Unix time: 1415239972
30c31b18 ........................... Target (bits)
fe9f0864 ........................... Nonce

00 ................................. Transaction count (0x00)

Inv

inv消息(inventory消息)传输一个或多个发送对象已知的对象清单。它可以被主动发送来宣布新的交易或块,或者它可以被发送以回复getblocks消息或mempool消息。

接收方可以比较inv消息中的inventorys和已经看到的inventorys,然后使用后续消息来请求看不见的对象。

Bytes Name Data Type Description
Varies count compactSize uint inventory entries的数量
Varies inventory inventory 一个或多个inventory条目,最多50,000个条目。

以下带注释的十六进制转储显示了一个包含两个inven tory条目的inv消息。 (消息头已被省略。)

02 ................................. Count: 2

01000000 ........................... Type: MSG_TX
de55ffd709ac1f5dc509a0925d0b1fc4
42ca034f224732e429081da1b621f55a ... Hash (TXID)

01000000 ........................... Type: MSG_TX
91d36d997037e08018262978766f24b8
a055aaf1d872e94ae85e9817b2c68dc7 ... Hash (TXID)

MemPool

在协议版本为60002的时候被添加。

mempool消息请求接收节点已验证为有效但尚未出现在块中的交易的TXID。也就是说,在接收节点的内存池中的交易。对mempool消息的响应通常是一个或多个包含inventory格式的TXID的inv消息。

当程序首次连接到网络时,发送mempool消息非常有用。全节点可以使用它来快速收集网络上可用的大部分或全部未确认的交易;这对试图收取交易费用的矿工尤其有用。SPV客户端可以在发送 mempool之前设置过滤器,仅接收与该过滤器匹配的交易;这允许最近开始的客户获得与其钱包有关的大部分或全部未确认的交易。

mempool消息的inv响应充其量只是一个节点的网络视图 - 而不是网络上未经确认的交易的完整列表。以下是列表可能不完整的一些其他原因:

在Bitcoin Core 0.9.0之前,对mempool消息的响应只有一个inv消息。inv消息被限制为50,000个inventory,所以如果某一个节点的内存池的条目大于50000个条目,那么它是不会发送所有内容的。Bitcoin Core的更新版本根据需要发送尽可能多的inv消息以引用其完整的内存池。

mempool消息当前不与filterload消息的BLOOM_UPDATE_ALLBLOOM_UPDATE_P2PUBKEY_ONLY标志完全兼容。Mempool交易不像块内交易那样是经过排序的,比如:一个花费之后输出的交易(tx2)可以出现在包含该输出的交易(tx1)之前,在这种情况下,自动过滤这种机制没办法运行,必须要等到tx2之前的交易(tx1)的到来。在 Bitcoin Core issue #2381
中已经提出,交易在被过滤器处理之前应该被排序。

MerkleBlock

如BIP37所述,在版本协议为70001时被添加。

merkleblock消息是对使用inventory类型MSG_MERKLEBLOCK请求块的getdata消息的回复。这只是答复的一部分:如果找到任何匹配的交易,它们将作为tx消息单独发送。

如果之前已经使用filterload加载消息设置了过滤器,则merkleblock消息将包含所请求块中与过滤器匹配的所有交易的TXID以及将这些交易连接到块头的必要块所需的块merkle树的任何部分 merkle根。 该消息还包含块头的完整副本,以允许客户端对其进行hash并确认其工作证明。

Bytes Name Data Type Description
80 block header block_header block header section 中描述的格式
4 transaction uint32_t 块中的交易数量(包括与过滤器不匹配的交易数量)。
Varies hash count compactSize uint 以下字段中的hash数量
Varies hashes char[32] 交易和merkle节点中的一个或多个hash以内部字节顺序排列。每个hash是32个字节。
Varies flag byte count compactSize uint 以下字段中的标志字节数。
Varies flags bytep[] 一系列位在一个字节中打包8个,其中最低有效位在先。可以填充到最接近的字节边界,但不得包含比那更多的位。用于将hash分配给merkle树中的特定节点,如下所述。

下面带注释的hexdump显示了一个merkleblock消息,它对应于下面的例子。 (消息头已被省略。)

01000000 ........................... Block version: 1
82bb869cf3a793432a66e826e05a6fc3
7469f8efb7421dc88067010000000000 ... Hash of previous block's header
7f16c5962e8bd963659c793ce370d95f
093bc7e367117b3c30c1f8fdd0d97287 ... Merkle root
76381b4d ........................... Time: 1293629558
4c86041b ........................... nBits: 0x04864c * 256**(0x1b-3)
554b8529 ........................... Nonce

07000000 ........................... Transaction count: 7
04 ................................. Hash count: 4

3612262624047ee87660be1a707519a4
43b1c1ce3d248cbfc6c15870f6c5daa2 ... Hash #1
019f5b01d4195ecbc9398fbf3c3b1fa9
bb3183301d7a1fb3bd174fcfa40a2b65 ... Hash #2
41ed70551dd7e841883ab8f0b16bf041
76b7d1480e4f0af9f3d4c3595768d068 ... Hash #3
20d2a7bc994987302e5b1ac80fc425fe
25f8b63169ea78e68fbaaefa59379bbf ... Hash #4

01 ................................. Flag bytes: 1
1d ................................. Flags: 1 0 1 1 1 0 0 0

注意:当完全解码时,上面的merkleblock消息提供了与过滤器匹配的单个交易的TXID。 在网络流量转储中取出此输出,属于该TXID的完整交易在merkleblock消息之后立即作为tx消息发送。

解析MerkleBlock消息

如上面的hexdump所示,merkleblock消息提供了三种特殊数据类型:交易数量,hash列表和一bit的标志列表。

您可以使用交易数量来构造一个空的Merkle树。我们将调用树中的每个条目作为节点;底部是TXID节点 - 这些节点的hash是TXID;其余节点(包括merkle根)是非TXID节点 - 它们实际上可能与TXID具有相同的hash值,但我们将它们区别对待。

image.png

从merkle根节点和第一个标志开始。下表描述了如何基于正在处理的节点是TXID节点还是非TXID节点来评估标志。一旦将标志应用于节点,请勿将另一个标志应用于同一节点或重新使用该标志。

Flag TXID Node Non-TXID Node
0 使用下一个hash作为此节点的TXID,但此交易与筛选器不匹配。 使用下一个hash作为此节点的hash。不要处理任何后代节点。
1 使用下一个hash作为此节点的TXID,并将此交易标记为与过滤器匹配。 需要去计算hash。处理左侧子节点以获取其hash值;处理右侧的子节点以获得其hash;然后将这两个hash链接为64个原始字节并hash它们以获取此节点的哈希。

任何时候你第一次开始处理一个节点,评估下一个flag。切勿在其他时间使用flag。

处理子节点时,可能需要处理其子节点(原始节点的孙辈)或下一级节点,然后再返回到父节点。这是预期的 - 保持处理深度,直到您到达TXID节点或标志为0的非TXID节点。

处理flag为0的TXID节点或非TXID节点后,停止处理flag并开始提升树。随着您的升级,计算您现在有两个子hash或您现在拥有唯一子hash的任何节点的hash。有关hash指令,请参阅merkle树部分。如果您到达仅知道左侧hash的节点,则根据需要下降到其右侧子节点(如果存在)和更多子节点。

但是,如果您发现一个节点的左右两个子节点都具有相同的哈希,则失败。这与CVE-2012-2459.
有关。

继续降序并升序,直到获得足够的信息以获取merkle根节点的hash值。如果在达到该条件之前用完flag或hash值,则失败。然后执行以下检查(顺序无关紧要):

创建MerkleBlock消息

在理解如何解析已经创建的消息之后,理解如何创建merkleblock消息会更容易,所以我们建议您首先阅读上面的解析部分。

在底部行上创建一个带有TXID的完整Merkle树,并计算所有其他hash,直到顶部行的merkle根。对于每个匹配过滤器的交易,跟踪其TXID节点及其所有祖先节点。

create merkle tree

开始使用merkle根节点处理树。下表介绍了如何根据节点是匹配项,匹配祖先项还是既不匹配也不匹配父节点来处理TXID节点和非TXID节点。

TXID Node Non-TXID Node
Neither Match Nor Match Ancestor 将0添加到flag列表中;将此节点的TXID附加到hash列表。 将0添加到flag列表中;将此节点的hash附加到hash列表。不要下降到其子节点。
Match Or Match Ancestor 将1添加到flag列表中;将此节点的TXID附加到hash列表。 将1添加到flag列表中;处理左侧子节点。然后,如果节点有一个正确的孩子,则处理正确的孩子。不要将hash附加到此节点的hash列表。

任何时候您第一次开始处理节点时,都应该将一个flag附加到flag列表中。在其他任何时候都不要在该列表中放置flag,除非处理完成时将flag列表填充到字节边界。

处理子节点时,可能需要处理其子节点(原始节点的孙辈)或下一级节点,然后再返回到父节点。这是预期的 - 保持处理深度,直到你到达一个TXID节点或一个既不是TXID也不是匹配祖先的节点。

处理完一个TXID节点或一个既不是TXID也不是匹配祖先的节点后,停止处理并开始提升树,直到找到一个尚未处理的具有正确子节点的节点。下降到这个正确的孩子并处理它。

在根据上表中的说明完整处理merkle根节点后,处理完成。将flag列表填充到字节边界,并使用本小节开头附近的模板构造merkleblock消息。

CmpctBlock

如BIP152所述,在70014版本中被添加。

Version 1 compact block 是pre-segwit(txids)Version 2 compact block是 post-segwit(wtxids)

cmpctblock消息是对使用 inventory 消息类型MSG_CMPCT_BLOCK请求块的getdata消息的回复。 如果所请求的 block 最近被宣布并且接近接收器的最佳链的尖端,并且在向请求 peer 发送sendcmpct消息之后,节点以包含该块的数据的cmpctblock消息来响应。

如果所请求的块太旧,则该节点以 full non-compact block 进行响应

收到cmpctblock消息后,节点应在发送sendcmpct消息之后计算每个未确认交易的short 交易 ID(即,在它们的mempool中),并将它们与cmpctblock消息中的每个short 交易ID进行比较。 在找到已有的交易之后,没有可用来重建完整块的所有交易的节点应该使用getblocktxn消息请求缺少的。

cmpctblock消息包含一个PrefilledTransaction的向量,其结构在下面定义。

Bytes Name Data Type Description
Varies index compactSize uint 该交易所在块的索引。
Varies tx Transaction 位于索引块中的交易。

cmpctblock消息受到下面定义的序列化的HeaderAndShortIDs结构的影响。HeaderAndShortIDs结构用于广播一个block header,short transaction ID用于匹配已经可用的交易以及 peer 节点可能丢失的少数几个交易。

Bytes Name Data Type Description
80 block header block_header 参见block header中的描述
8 nonce uint64_t 用于给short transaction ID计算的随机数
Varies shortids length compactSize uint 以下字段中的short transaction ID数量。
Varies shortids byte[] 根据事先未填写的交易计算的short transaction ID。规范中的6字节整数向量,填充两个空字节,因此可以读取为8字节整数。在版本2中的compact block,shortid应该使用wtxid代替BIP141定义的txid
Varies prefilled txn length compactSize uint 以下字段中的预填充交易数量。
Varies prefilled txn prefilledTransaction[] 用于提供coinbase交易和一些peer节点可能缺失的少数几个交易。上面定义的PrefilledTransaction结构的向量。

计算short transaction ID

short transaction ID用于表示一个交易但不发送完整的256位hash。他们计算如下,

sendCmpct

如BIP152所述,在协议版本70014中被添加。

sendcmpct消息被定义为一个消息,其中包含一个1字节整数,后跟一个8字节整数。第一个整数被解释为一个布尔值,并且应该具有1或0的值。第二个整数被解释为little-endian版本号。

在收到第一个和第二个整数设置为 1 的sendcmpct消息后,节点应通过发送cmpctblock消息来宣布新块。

在收到第一个整数设置为0的sendcmpct消息后,节点不应该通过发送cmpctblock消息来宣布新块,而是通过发送invheader来宣告新块,如BIP130所定义。

在接收到第二个整数设置为1以外的sendcmpct消息时,节点应该将peer视为未收到消息(因为它表示peer将在cmpctblock消息中提供意外的编码)。 这允许未来版本发送具有不同版本的重复sendcmpct消息,作为未来版本的版本协议的一部分。

在发送sendcmpct消息之前,节点应检查> = 70014的协议版本。 在收到来自该peer的sendcmpct消息之前,节点不应该向peer发送对MSG_CMPCT_BLOCK对象的请求。 节点在将所有sendcmpct消息发送给它打算发送的peer之前,不应请求MSG_CMPCT_BLOCK对象,因为peer无法知道在响应中使用哪种版本协议。

sendcmpct消息的结构定义如下。

Bytes Name Data Type Description
1 announce block_header 表示布尔值的整数必须是1或0(1为true,0为false)。
8 version uint64_t 一个版本号的小端表示。版本2紧凑块应通过将版本设置为2来指定

GetBlockTxn

如BIP152所述,在协议版本70014中添加。

getblocktxn消息被定义为包含BlockTransactionsRequest消息被序列化后的消息。 一旦接收到格式正确的getblocktxn消息,最近向这个消息的发送者提供这个消息中标识的块hash的cmpctblock消息的节点必须用适当的blocktxn消息或完整的块消息来响应。

blocktxn消息响应必须包含所有在getblocktxn消息索引列表中指定的索引处的相应块中的每个交易,并按请求的顺序。

BlockTransactionsRequest的结构定义如下。

Bytes Name Data Type Description
32 blcok hash binary blob 交易被请求的的这个block的blockhash
Varies indexes length compactSize uint 被请求的交易的数量
Varies indexes compactSize uint[] compactSize的矢量,包含块中被请求交易的index。在版本2的 compact block 中,应该使用wtxid来代替BIP141定义的txid

BlockTxn

blocktxn消息被定义为包含BlockTransactions消息被序列化后的消息。 一旦接收到格式正确的blocktxn消息的请求,节点应尝试通过从原始cmpctblock消息中取出prefilledtxn交易并将它们放置在标记的位置中,然后对于来自原始cmpctblock消息的每个short transaction ID,重新构建完整块订单,请从blocktxn消息或其他来源找到相应的交易,并将其置于该块中的第一个可用位置,然后一旦该块被重新构建,就应该按照正常方式处理,请记住预计会有short transaction ID 偶尔会发生碰撞,而且不管这些碰撞出现在哪里,这些节点都不应受到处罚。

BlockTransactions的结构定义如下。

Bytes Name Data Type Description
32 transactions length binary blob 正在提供交易的块的block hash
Varies transactions length compactSize uint 被提供交易的数量
Varies transactions transactions[] 交易矢量,对于原始交易格式的示例hexdump,请参阅原始交易部分。

NotFound

在协议版本为70001时被添加。

notfound消息是对getdata消息的回复,该消息请求接收节点没有可用于发送的对象。 (预计节点不会传递不再存在于内存池或发送列表中的历史交易,节点也可能从较旧的块中删除已用完的交易,使它们无法发送这些块。)

notfound消息的格式和最大大小限制与inv消息相同;只有消息 header 不同。

Tx

tx消息以原始交易格式传输单个交易。它可以在各种情况下发送;

上一篇 下一篇

猜你喜欢

热点阅读