一 区块链
区块链是由一个个的区块连接起来组成的,如下所示:
二 区块
一个完整的区块包含以下数据:
字段 |
字节 |
说明 |
神奇数 |
|
0xD9B4BEF9,作为区块之间的分隔符 |
Block Size |
4 |
用字节表示的该字段之后的区块大小 |
Block Header |
80 |
组成区块头的几个字段 |
Transactions Counter |
1-9 |
该区块包含的交易数量,包含coinbase交易 |
Transactions |
m*n(n>=250) |
所有交易的列表 |
三 区块头
区块头的长度是80字节,区块头中记录了版本号、上一个区块的Hash地址、merkle根、区块创建时间戳、区块的工作量难度系数、随机数。
1 结构
字段 |
大小 |
描述 |
version |
4字节 |
版本号 |
prevBlockHash |
32字节 |
上一个区块的Hash地址 |
merkleRoot |
32字节 |
该区块中交易的merkle root的哈希值 |
time |
4字节 |
该区块的创建时间戳, 从1970年1月1日开始所经过的秒数,不考虑闰秒。必须严格大于前11个区块时间的中值,同时也会拒绝那些超出自己2个小时时间戳的区块。 |
difficulty |
4字节 |
难度系数 |
nonce |
4字节 |
随机数,工作量证明就是枚举此值,直到找到一个符合要求的值。 |
2 Merkle root
1) Merkle Tree
Merkle Tree(梅克尔树)是存储hash值的二叉树,叶子节点存储的是数据块的hash值,非叶子节点存储的是叶子节点的hash值。数据的任何修改,都会影响其对应的Hash值,并逐步传导到上层,直到Root节点,这意味着树根节点是底层所有数据的数字摘要。
每个区块都有一个Merkle Tree,区块头中的Merkle Root(也称为Merkle树的根哈希值)是由区块体中所有交易的哈希值生成的。如果一个区块拥有四个交易,其Merkle Tree结构如下:
2) 应用场景
a) 快速比较大量数据
对需要比较的每组数据构建默克尔树,当两个默克尔树根不同,则说明这两组数据必然存在差异,如果两个默克尔树根相同,意味着两组数据相同(hash碰撞概率其实比较小,如果有顾虑,可以在逐个对比)。
b) 快速定位修改
因为默克尔树的任何数据修改,都会传导到根节点,所以当根节点数据变化以后,通过二叉树的快速查找特性,定位到修改的节点。例如当D1修改以后,通过Root->N4->N1即可找到D1发生修改,时间复杂度为O( lgN )。
c) 零知识证明
如果想证明某组数据(D1、D2、D3、…、DN)中拥有某数据Dx,可以构建出默克尔树,然后判断Dx的拥有者的默克尔树根和此组数据的默克尔树根是否相同即可。
3) 简单支付验证(SPV)
在比特币系统中,很多时候用户只关心与自己相关的交易。例如:当用户收到他人发来的比特币是,只希望知道交易是否合法,区块是否是共识区块,而不需要自己做完整校验,典型场景是比特币钱包客户端。
中本聪曾在比特币白皮书里提到,“不运行全节点也可以验证支付,用户只需要保存所有的区块头(Block Header)就可以了。用户虽然不能自己验证交易,但如果能够从区块链的某处找到相符的交易,他就可以知道网络已经认可了这笔交易,而且得到了网络的多个确认。”这就是SPV,即简单支付验证。因为每个区块头仅80字节,每10分钟左右产生一个区块,所以一年下来也就4M左右的数据,即便是将目前所有区块头信息保存下来,也就几十M的数据量。
下面以比特币钱包的客户端为例来讨论,如何通过SPV验证某一笔交易的合法性。当收到一个转账交易时,假设要验证此交易对应的输入是合法的,那么就需要验证此交易输入对应的区块(假设名为Block A)是存在的,并且是得到网络共识的区块。SPV的验证过程如下:
- 钱包客户端节点从区块链网络上获取并存储最长链的所有区块头至本地。
- 根据交易输入中的hash值定位到区块(Block A),并判断此区块的header是否在本地保存的区块头中,如果不存在则说明非法。
- 从区块(Block A)中获取构建Merkle Tree值,计算出Merkle Tree Root的hash值,将此计算出的hash值与区块(Block A)头中的Merkle root值进行比较,相同则认为交易是真实的。
- 根据区块(Block A)头在区块链上的位置,确认此区块是否是共识的区块。
四 区块体
区块体中主要存储两种类型的交易:conbase交易和普通交易。
1 Coinbase交易
一个区块第一个交易规定为coinbase交易,是系统奖励给矿工的比特币,其结构如下:
字段 |
字节 |
描述 |
版本 |
4 |
版本号 |
输入计数器 |
1-9 |
包含的交易输入数量,不引用任何交易,所以为0 |
交易哈希 |
32 |
不引用任何一个交易,值全部为0 |
交易输出索引 |
4 |
固定值,0xFFFFFFFF |
数据长度 |
1-9 |
coinbase数据长度 |
数据 |
1-9 |
采用大端格式编码 |
顺序号 |
4 |
固定值,0xFFFFFFFF |
输出计数器 |
1-9 |
包含的交易输出数量 |
总量 |
8 |
用聪表示的比特币值 |
锁定脚本大小 |
1-9 |
用字节表示的后面的锁定脚本长度 |
锁定脚本 |
不定 |
一个定义了支付输出所需条件的脚本 |
锁定时间 |
4 |
锁定时间为0,表示立即执行。 |
2 普通交易
普通交易记录的是区块链网络中提交上来的交易单,典型的示例为:A给B转账1个BTC。
1) 交易结构
字段 |
字节 |
描述 |
版本 |
4 |
版本号 |
输入计数器 |
1-9 |
包含的交易输入数量 |
输入 |
不定 |
一个或多个交易输入 |
输出计数器 |
1-9 |
包含的交易输出数量 |
输出 |
不定 |
一个或多个交易输出 |
锁定时间 |
4 |
一个区块号或UNIX时间戳 |
2) 交易输入结构
字段 |
字节 |
描述 |
交易哈希值 |
32 |
指向被花费的UTXO所在的交易的哈希指针 |
输出索引 |
4 |
被花费的UTXO的索引号,第一个是0 |
解锁脚本大小 |
1-9 |
解锁脚本长度 |
解锁脚本 |
不定 |
满足UTXO解锁脚本条件的脚本 |
序列号 |
4 |
目前未被使用的交易替换功能,设为0xFFFFFFFF |
3) 交易输出结构
字段 |
字节 |
描述 |
总量 |
8 |
用聪表示的比特币值 |
锁定脚本大小 |
1-9 |
锁定脚本长度 |
锁定脚本 |
不定 |
定义支付输出所需条件的脚本 |