跳转到正文
buybitcoinsmart

术语表 /开发者参考

CompactSize

又称 Compact size unsigned integer、VarInt、变长整数。

CompactSize 是什么?
CompactSize 是比特币在序列化数据中为计数和长度添加前缀所用的变长整数,小于253的值占1个字节,其余最多占9个字节。

比特币的二进制格式不带字段名,因此解析器必须被告知后面有多少个输入,以及每个脚本有多长。CompactSize 最少只用1个字节来表示:0至252只需这1个字节,更大的值则由一个标记字节加上2个、4个或8个字节组成。您发送的每笔付款至少包含四个这样的字段。

运作方式

CompactSize 通过读取自身的第一个字节,来判断自己还剩多少字节。

第一个字节小于0xfd 时,该字节本身就是数值,因此从0到252的计数只占1个字节,不再有其他内容。第一个字节为0xfd 表示数值位于后面的2个字节,0xfe 表示后面的4个字节,0xff 表示后面的8个字节。这些后续数字全部采用小端序。最长的编码为9个字节,可表示超过18,000,000万亿的值,而实际的数据结构远远用不到这么大的数。

编码必须是最短形式。把数字5写成0xfd 0x05 0x00 与写成0x05 在数值上完全相同,但 Bitcoin Core 会拒绝前者,并在反序列化时报告非规范编码错误。没有这条规则,同一笔交易可以有多种写法,每种写法的标识符各不相同,这属于交易延展性问题,而不只是格式整洁与否的问题。

实际的上限远低于算术上的上限。对于任何声称大于0x02000000 字节(约33.5 MB)的反序列化对象,Bitcoin Core 都拒绝为其分配内存,而点对点协议另外将单条消息限制在4,000,000字节以内。因此,恶意节点无法通过宣称一笔含有百亿亿个输入的交易,让您的节点预留数以 GB 计的内存。

有一个命名上的陷阱值得提醒。网络传输格式的文档称之为 var_int,钱包库通常称之为 VarInt,而 Bitcoin Core 自己的源代码用 VarInt 这个名称指代一种完全不同、互不相关的编码,该编码只出现在其数据库和撤销文件中。阅读代码时,请核实您看到的是两者中的哪一个。

在哪里会遇到它

CompactSize 是比特币中最常遇到、却完全没有面向用户的名称的结构。

在原始交易中,它紧跟在4字节的版本号之后(若存在隔离见证的标记字节和标志字节,则紧跟在它们之后),用于存放输入数量。在每个脚本之前,它再次出现,用于给出脚本长度;在输出数量之前,它又出现一次。在序列化的区块中,它用于存放交易数量。在点对点协议中,它位于 inv、getdata、addr 和 headers 消息的载荷开头。

headers 消息最能体现这种编码的怪异之处。每个80字节的区块头后面都跟着一个值为零的 CompactSize 交易数量,因此每个条目占81字节而不是80字节,一整批2,000个区块头在网络上共占162,000字节。该计数始终为零,因为 headers 消息从不携带交易。设置这个字段,是为了让同一段解析代码无论区块头后面有没有区块体,都能读取区块头。

如果您手动解码原始交易,读到一半字段就不再合理,原因几乎总是 CompactSize 读错了。少读一个字节,之后的所有内容都会错位。

常见问题

比特币为什么要使用变长整数?

因为比特币中几乎所有的计数都很小。一笔交易通常只有一两个输入,如果历史上每笔交易的每个字段都要花4个或8个字节来表示这一点,就会白白增加大量永久性的数据负担。

CompactSize 与 Bitcoin Core 的 VarInt 是同一种东西吗?

不是,尽管两者的别名相同。CompactSize 是本文所述的网络传输和磁盘存储格式,而 Bitcoin Core 中名为 VarInt 的类是另一种紧凑编码,仅用于其自身的数据库和撤销文件。

相关术语

开发者参考中的更多术语

阅读本页英文版