[比特币]比特币的实现
比特币系统是一个基于交易的账本,这意味着存储在账本中的是一笔笔交易。那么在用户进行转账交易时,首先就需要确保用户所持有的数量要大于或等于将要交易的。这要做的目的是为了避免双花问题。
这种基于交易的账本好处在于隐私保护性比较好,缺点在于每次交易都需要说明币的来源。
在比特币的全节点中,会在内存中维护一个名为UTXO(Unspent Transaction Output)的数据结构 -- 未消费的交易输出,用以记录哪些交易的输出已经被消费了,哪些交易的输出还没有被消费。已经消费的交易输出会从UTXO中移除。
随着比特币系统中交易的不断增加,UTXO的体积也在不断的增加。虽然被消费的交易输出会被移除,但UTXO中也会有一些陈旧的交易输出存在。其中有些是所有者不想使用的(像中本聪账户下的),也有一些是丢了私钥花不了的。
所以在执行交易过程中,首先会从UTXO中检索交易输入的比特币的数量是否大于或等于交易输出的数量,即 \(total \quad inputs >= total \quad outputs\),差额就是付给记账节点的小费。所以比特币的奖励不仅仅是出块奖励,还有交易过程支付的小费,不过在目前的环境下,各个节点争夺记账权主要还是为了出块奖励。
求解Puzzle
比特币中的block header结构如下:
class CBlockHeader
{
public:
// header
int32_t nVersion; // 当前使用的比特币版本,不可更改
uint256 hashPrevBlock; // 前一个区块的Hash,不可更改
uint256 hashMerkleRoot; // MerkleTree的根Hash
uint32_t nTime; // 时间戳,因为比特币网络不要求绝对的时间同步,所以这个值可以小范围的调整
uint32_t nBits; // 目标阈值,根据协议定期调整
uint32_t nNonce; // 随机数
......
}
nonce是一个无符号的整数,这意味着它的取值范围在\(0 \sim 2^{32}\)。按照现在的挖矿难度来说,很可能把这\(2^{32}\)个值都算了一遍也找不到合适的值,这个时候该怎么办呢?
从上面比特币的block header的定义中,可以发现除了nonce可以修改外,还可以修改hashMerkleRoot。
上一节说过,每个区块中都包含一个铸币交易,在该交易中包含一个coinbase域。在比特币交易中,这个域是不做检测的,这意味着可以在这个域中随意写入。在《数据结构》章节中介绍MerkleTree时讲过,MerkleTree任意叶子节点的修改都会影响到MerkleTree的根Hash值。因此,当block header中的4个字节的nonce不够用的时候,可以使用其它字节,比如将coinbase域的前8个字节当做extra-nonce来使用,这样搜素空间就扩展到了\(2^{96}\)。
所以真正挖矿的时候会有两层循环,外层循环调整coinbase域的extra-nonce,算出block header里的根Hash值之后,内层循环再调整block header里的nonce。
在比特币中,求解puzzle的过程又被称为“挖矿”,因为都是在一个很大的空间中寻找一件极有价值的物品。
挖矿过程中的每一次尝试都可以看做是一个Bernoulli trial,也就是说每次计算的结果都不会影响到下一次计算的结果。
虽然挖矿过程本身并没有什么实际意义,但挖矿的过程对于维护比特币系统的安全性至关重要。挖矿提供了一种以算力投票的有效手段,只要大部分算力掌握在诚实节点手中,系统的安全性就能够得到保障。
比特币系统安全性分析
假设大部分算力是掌握在诚实的矿工手里,那我们能得到什么样的安全保证?能不能保证写入区块链的交易都是合法的?
挖矿给出的只是概率上的保证,只能说有比较大的概率下一个区块是由诚实的矿工发布的,但不能保证记账权不会落在有恶意的节点手里。
偷币
恶意节点能不能偷币?答案是不能,因为他没有别热的私钥,无法伪造别人的签名。
如果恶意节点硬将偷币的交易写到区块链上,诚实节点是不会接受这个这个区块的,因为它包含有非法交易。所以诚实节点会继续沿着前一个区块挖,生成新的区块来代替非法区块,其他诚实节点会沿着这个合法区块继续往下挖。比特币的要求是扩展正常合法链,这样做的结果不仅没有得到区块奖励,没有偷到钱,还白白浪费了算力。
双花
恶意节点能不能实现双花攻击?答案也是不能的。假如M转账给A的交易已经写到了一个区块里面,现在他获得了记账权,又发起一笔交易把转给A的钱再转给自己,发起双花攻击。诚实节点是不会接受这个区块的。
如果他还想发布这个区块,那就只能将这个区块连接到记录上次转账交易区块的前一个区块。而区块要插在哪个位置,这是在挖矿开始时就决定的,因为block header中要填写上一个block header的Hash。这样操作的话,会产生两条链,且这两条链都是合法的。之后就要看其他节点会按哪一条链往下扩展了,最终只会有一条链胜出,另一条链会作废。
双花攻击的目的是为了在向A转账得到某些东西后,再将花出去的钱转给自己,从而达到不当获利的目的。但按照上面的操作,只会产生两种结果:向A支付比特币购买物品,或者将钱转给自己,什么也没得到。显然这两种结果都没达成双花攻击的目的。
那么针对使用比特币支付购买商品时,怎样才能避免上面的问题呢?
在比特币协议中,默认在包含该交易的区块后有6个新增的区块,这时才能认为交易成功。以平均10分钟的出块时间记,需要在交易发起一小时后才能确认交易。
声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。