下面以交易的字节占用为例,解析 btcd 是如何处理 golang 的字节占用问题的。
交易结构
一个交易的大小分成了隔离见证占用和非隔离见证占用,非隔离见证部分包括:交易版本号、交易输入的数量、交易输入本身、交易输出数量、交易输出本身、锁定时间。
交易的结构:
一个交易的字节占用可被拆解为:
隔离见证数据在交易结构中位于交易输入结构中,但btcd单独计算了其数据占用情况,而没有和TxIn一起计算。
交易的字节占用
一个交易包括4字节版本号、4字节锁定时间、txin长度的变长整数占用、txout长度的变长整数占用,再加上txin和txout占用。
VarIntSerializeSize() 函数对不同大小的整数规范了不同的字节占用,也是为了优化存储,以及网络传输成本。具体内容可以参考: Variable length integer(https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer)
代码理解:
传递一个 uint64 类型的整数参数
如果比 0xfd 小,那么就返回 1,说明数据在一个字节内存储而不会溢出
如果比 0xfd 大,在分别和 math.MaxUint16、math.MaxUint32 比较,也就是 2 字节和 4 字节能容纳的最大正整数,如果在相应的范围,则返回对应的字节数量。需要注意的是 2 字节、4 字节判断前需要分别有 0xfd、0xfe 标记,故返回的数据需要加 1
如果前面的分支都没有进入,由于变长整数的最大值被规范在 uin64 范围内,因此该数据占用为 8 个字节,加上开始的 1 字节 0xff 标记,故返回 9
txin 的字节占用了:包括 32 字节的 outpoint hash、4 字节的 outpoint 索引、4 字节的序列号,总共 40 字节。再加上 sigScript 长度的变长整数占用,以及 sigScript 占用字节数。
txout 的字节占用:8 字节转账金额、pkScript 长度变长整数、pkScript 占用字节数。
btcd 在计算时加入了很多变长整数的字节占用,但是在其交易结构里面并没有对 txin、txout 的长度统计的字段,在使用该长度信息时在进行统计。比如在交易的序列化的过程中,对相应数据的长度进行了统计和写入。
下面通过一张图来说明整个流程:
总结
btcd 的这种统计内存占用的方式,其实和 golang 的内存分配和布局已经没有什么关联了,只是对 bitcoin protocol 的原始实现。虽然并不能体现其真实的底层内存占用情况,但是在一定程度反映了内存占用的变化情况。另外,在统计 []byte 是并不是使用 unsafe.SizeOf() 函数简单实现,而是通过统计 []byte 的长度,更贴近于真实情况,虽然 golang 真实占用比这个值要大。
本文由 Copernicus 团队 戚帅 编写,转载无需授权。
领取专属 10元无门槛券
私享最新 技术干货