比特币交易实现流程

比特币作为最早诞生的加密货币之一,其独特的交易账本模式为其在数字货币世界的地位树立了标杆。交易账本模式意味着比特币区块链上的每一笔交易都被记录在一个区块中,而这些区块再被链接起来形成一个不断增长的链条,即区块链。每个区块里存储了一定数量的交易信息,包括转账交易、铸币交易等,但并没有明确记录每个账户当前持有多少比特币,而是通过追溯该账户的交易历史来计算账户余额。

欧易
欧易(OKX)

全球三大交易所之一,注册领50U数币盲盒,币圈常用的交易平台!

币安
币安(Binance)

币安交易所是世界领先的数字货币交易平台,注册领100U。



比特币交易的具体实现

区块链是一个去中心化的账本。

比特币采用的是基于交易的账本模式(transaction-based ledger),每个区块里记录的交易信息(转账交易、铸币交易等),但是系统中并没有记录每个账户当前有多少个币,这个需要通过区块链中涉及该账户的交易来推算。

比特币交易实现流程

除了比特币的这种基于交易的账本模式,与之对应的还有基于账户的账本模式(account-based ledger),例如以太坊就是用的这种模式。在这种模式中,系统需要显式的记录每个账户中剩余多少个币。

基于交易的账本模式中,因为没有记录账户,所以对隐私保护比较好。但是因为没有记录账户中剩余的币的数量,所以每次交易时都需要说明币的来源。而以太坊就不再需要显式的说明币的来源。

比特币UTXO

比特币的全节点要在内存维护一个UTXO(Unspent Transaction Output)的数据结构。有些交易的输出已经被花掉了,有些还没被花掉。所有还没被花掉的输出的集合就是UTXO。

一个交易可能有多个输出,比如一个交易中,A给B转了5 BTC,同时A也给C转了3 BTC。如果B收到的5BTC已经花掉了,A给B转账的输出就不再存在UTXO中,C的3 BTC还没花掉,该输出就会存储在UTXO中。所以,同一个交易中的多个输出,可能有的输出存储在UTXO中,有的不在UTXO中。

UTXO的每个元素要给出:产生这个输出的交易的哈希值,以及它在这个交易中是第几个输出。

通过UTXO可以验证交易的合法性,想要花掉的币必须要在UTXO中存储的才是合法的。如果不在UTXO中,表示准备花掉的这个币可能是不存在的或者已经被花掉过的。

所以全节点要在内存中维护UTXO数据结构,以便快速检测double spending。

每个交易会消耗掉一些输出,同时也会产生一些新的输出。

每个交易可以有多个输入、多个输出,多个输入的金额加起来要等于输出的总金额:total inputs=total outputs。

一个交易的多个输入可以来自不同的账户地址,所以一个交易可能会有多个签名,每个输入的账户地址都要提供对应的签名。

有些交易中,可能存在total inputs略微大于total outputs的情况,例如输入端是1 BTC,输出端只有0.99 BTC,多出来的这0.01 BTC作为交易费给了打包这个交易的获得记账权的节点。

区块实例

比特币的一个区块示例:来源blockchain

比特币交易实现流程

其中:

●Minted:出块奖励

●Fees:该区块中所有交易的总交易费

●Reward:出块奖励+交易费

●Average Fee:平均交易费

●Median Fee:交易费中位数

●Height:区块的序号

●Difficulty:挖矿的难度(每隔2016个区块会根据前2016个区块的计算时间和算力调整一次难度,使得每个区块计算时间维持在10分钟左右,产生2016个区块大约需要2周)

●nonce:挖矿时尝试的随机数

●hash:区块头的hash值(可以看出hash值前面有一长串的0,这不是偶然的,计算出来的哈希值需要小于等于给定的目标阈值,表示出来就是哈希值前面有一长串的0)

●Merkle Root:该区块中包含的交易Merkle Tree的根哈希值

可以看出,矿工的主要挖矿动力还是出块奖励。

Block Header脚本实例

Block Header结构:参考github的bitcoin项目

class CBlockHeader
{
    public:
        // header
        int32_t nVersion;  // 当前使用的比特币协议的版本号
        uint256 hashPrevBlock;  // 前一个区块的区块头哈希
        uint256 hashMerkleRoot;  // 区块的merkle根哈希值
        uint32_t nTime;  // 区块产生的时间
        uint32_t nBits;  // 挖矿的目标阈值编码后的nBits,根据协议中的要求进行定期调整,不能随便改
        uint32_t nNonce;  // 随机数nonce
        
        // .........
};

其中,nonce域的类型为uint32_t,即最多只有$2^{32}$个取值,“挖矿”时需要调整该域。

但是因为现在的“矿工”越来越多,挖矿的难度被调整的越来越高,即使把nonce的$2^{32}$的取值全部遍历一遍,也很可能找不到可以匹配的值。

因为调整nonce很可能找不到匹配的值,所以还需要区块头中的其他域。除了调整nonce外,能调整的只剩下区块产生的时间nTime、merkle根哈希值。

比特币中对区块产生的时间要求并不精确,只要不是很离谱就行,但是该域的类型也是uint32_t,能调整的范围也很有限。

Merkle Tree根哈希值的调整方式:在Merkle Tree中,会存在一个铸币交易,该交易是凭空产生的,不需要输入,它的CoinBase域可以随意填写内容不受限制,可以用它来写入digital commitment,甚至人生感想等各种内容。修改了该域的值,会导致该交易的哈希值发生变化,进而Merkle Tree上层的哈希指针节点发生变化,最终传导到Merkle Tree的根节点,导致根哈希值发生变化。

所以,可以把Merkle Tree的CoinBase域的前8 byte(即前64位)当做extra nonce。当nonce不够用时,可以调整这个值。这样整个可调整的空间就从$2^{32}$变成了$2^{96}$。在目前的挖矿难度下,这个长度已经足够了。

真正挖矿时,是有两层循环:外层循环调整CoinBase的extra nonce,内层循环调整nonce。

交易实例

区块的Merkle Tree中记录的交易实例:来源blockchain

比特币交易实现流程

其中:

●From:交易的输入,一个交易可以有多个输入。该笔交易有2个输入。后面的按钮可以点击查看对应的Input Script

●To:交易的输出,一个交易可以有多个输出,该笔交易有2个输出。黑色箭头表示该币已经被花掉了,点击箭头可以跳转到花掉该币的交易。还未花掉的输出会被记录到UTXO中。点击后面的脚本按钮可以查看对应的Output Script

●From、To中的每条记录的地址为账户地址

●Input Value:输入的总金额

●Output Value:输出的总金额

●Fee:交易费

InputScript示例:上面的交易的From中的第一条输入:

比特币交易实现流程

OutputScript示例:上面的交易的To中的第一条输出:

比特币交易实现流程

比特币系统中,验证交易的合法性,就是把本次交易的InputScript脚本前面提供币的来源的交易的OutputScript脚本配对后执行来完成的。如果InputScript和OutputScript拼接在一起能够顺利执行不出错,那么这个交易就是合法的。

比特币挖矿的过程

比特币挖矿就是不断的尝试各种nonce,来求解puzzle,每次尝试nonce可以看做一次Bernoulli trial(伯努利试验,在同样的条件下重复地、相互独立地进行的一种随机试验,试验只有两种结果:发生或者不发生。例如投掷硬币就是伯努利试验)。

如果我们做很多的Bernoulli trial,每个试验结果都是随机的,这些Bernoulli trial就构成了一个Bernoulli process(伯努利过程,伯努利实验的一个序列)。

Bernoulli process的一个性质是无记忆性(memoryless):做大量的试验,前面的试验结果对后面的试验是没有影响的。

我们关心的是出块时间(系统中产生下一个区块的时间),可以推导出来这个整个系统的出块时间服从指数分布(exponential distribution)。通过定期调整puzzle的难度,控制整个系统的出块的平均时间为10分钟。

出块时间概率密度分布:

比特币交易实现流程

整个系统的平均出块时间为10分钟,具体到某个矿工,这个矿工的平均出块时间取决于这个矿工的算力占整个系统算力的百分比。

出块的时间也是无记忆性的:假如当前已经过去了10分钟,还没有人出块,接下来等待出块的时间依然符合该概率密度分布,平均还是要等10分钟。将来还需要挖多长时间,和已经挖了多长时间是没有关系的。这个性质也称为progress free(过去的progress是没有用的)。

如果哪个加密货币的puzzle不是progress free的,那么算力强的矿工将会获得不成比例的优势。

比如一个矿工是另一个的算力的10倍,理想状态下,这个矿工获得记账权的概率也是另一个的10倍。但是算力强的矿工因为算力强,曾经做过很多不成功的尝试,如果不是progress free的,那么算力强的矿工因为前期不成功的次数多,导致后面成功的机会变大,获得记账权的概率就会超过10倍。所以,只有是progress free的,前面的计算结果不会影响后面,才能保证算力强的矿工不会获得不成比例的优势,progress free是挖矿公平性的保证。

比特币的总量

出块奖励是比特币系统中产生新的比特币的唯一途径,而这个出块奖励每隔21万个区块(约4年)就减半,这样产生的比特币数量就构成了几何序列:

比特币交易实现流程

比特币的安全性

比特币挖矿的意义

比特币求解的puzzle除了比拼算力外,没有任何其他的实际意义。比特币越来越难被挖到,是因为出块奖励被人为逐渐减半了。

虽然挖矿本身是没有任何实际意义的,但是挖矿的过程对维护比特币系统的安全性至关重要(Bitcoin is secured by mining)。对于比特币这种没有membership控制的系统来说,挖矿提供了一种依靠算力投票的有效手段。只要大部分的算力掌握在诚实的节点手里,系统的安全性就能得到保证。

即使大部分算力掌握在诚实节点手里,但是系统只是保证下一个区块有很大可能是由一个诚实的矿工发布的,但是不能保证记账权不会落到有恶意的节点手里。

盗窃他人账户

对偷盗他人比特币的防范:

比特币交易实现流程

M如果想要偷盗A账户里面的钱,需要伪造一笔A到M的转账交易。但是M没有A的私钥,无法伪造A的签名,这笔交易是无效的。如果M硬把该笔没有A签名的交易打包写到了一个区块里,诚实的节点在验证到含有无效的交易时不会认可该区块,诚实的节点会继续沿着之前正确的区块向后扩展。

分叉攻击

分叉攻击:

比特币交易实现流程

M给A转了一笔钱,此交易被写到了区块4中,接到区块3后面。

此时如果M获得了记账权,又重新打包了一个区块5,包含一笔M给自己另一个账户M2的转账,也接到了区块3后面。区块4和区块5就构成了等长分支,哪个分支有效取决于其他节点接着哪个分支扩展。

如果恶意节点M又获得了记账权,再发布一个区块6,接到区块5上面,将区块5所在分支做成最长合法链,那么区块4中M给A的转账交易就等于被“回滚”了。

如果,M转账到A的交易所在的区块不是最后一个区块,后面还跟着几个区块,那么恶意节点再想攻击成功的难度就会大大提升。因为恶意节点的区块5不是最长合法链,而诚实节点只会沿着最长合法链继续向后扩展。恶意节点的区块5只有恶意节点自己在向后扩展,就等于一个节点的算力在和整个系统中诚实节点的算力在赛跑。而且恶意节点只获取到一次记账权是不够的,需要获取多次的记账权,才能将自己的区块5所在的分支构造成最长合法链。

所以,一种简单的防范分叉攻击的方式就是多等几个区块,也可以叫做多等几个确认(Confirmation)。

从区块3后面开始:区块4称为One confirmation、区块41称为Two confirmation、区块42称为Three confirmation…………

比特币系统中,缺省的需要等6个confirmation,这时才认为前面的交易是不可篡改的。按平均一个出块时间10分钟算,等6个confirmation需要一个小时。

比特币交易实现流程

比特币被称为不可篡改的账本(irrevocable ledger)。实际上,这种不可篡改性只是一种概率上的保证。刚写入区块的交易很可能被篡改,经过一段时间,后面扩展了几个confirmation后,被篡改的可能性被大大减小,是指数级别的下降。

实际中,还存在着Zero confirmation:在交易发布出来,还未写到区块链的时候的确认。

对应到上图中,就是区块3后面,发生了M给A的转账交易,但是区块4未产生,这笔交易还未写入区块链中。虽然这种方式听上去风险很大,但在实际当中运用的比较普遍。原因有:

●比特币协议缺省设置,当两个交易有冲突时,节点接受最先收到的交易。例如上图中M给A做了转账交易,然后M再给M2转账时很大概率上不会被诚实节点接受。

●很多购物网站,在交易支付之后,到发货之前,天然有一定的处理时间。当电商发货时,如果发现该交易没有被写入最长合法链上,只需要取消发货即可。

故意不包含某些交易

如果一个恶意节点获取到记账权后,故意不把某些交易打包进区块中,这是可能的。比特币协议中,并没有规定必须把哪些交易写入区块中。

但是这样做的结果影响不大,因为写入后面的其他区块中也可以,系统中总会有诚实的节点愿意发布这些交易。

即使是区块链在正常的情况下,也会出现合法的交易没有被包含进区块链的情况。例如同一时间内发布的交易过多,而比特币协议中对区块大小有限制,最多不能超过1Mb,写不下的交易就只能等到写入下一个区块中。

Selfish mining

依然是为了达到分叉攻击的目的。恶意节点M先准备好M到M2的转账交易,打包该交易为区块5,但是不进行发布,然后继续接着区块5挖矿挖出来连续的6个区块,但是也都藏起来不进行发布。等区块4中M给A的转账交易经过了6 confirmation后,立即将自己藏起来的这7个区块进行发布,构造成一条最长合法链,回滚掉M给A的转账交易。

在正常情况下,矿工挖到区块会马上进行发布,因为要抢在他人之前发布,来获取出块奖励。但Selfish mining并不着急发布,而是先藏起来。

如果系统中诚实的节点占据很大的算力,那么这个恶意的节点这样成功的概率并不大。因为正常包含M到A转账交易的区块4是已经发布了的,系统中其他矿工会接着这个区块向后挖矿,而自己藏起来的区块5这条链只有自己和自己的同伙知道,依然是要靠自身的算力和整个比特币系统诚实节点的算力赛跑,所以成功概率也是很低的。

除了用来达到分叉攻击外,Selfish mining还可以有其他的目的,例如减少竞争者。一个节点挖出了区块3后面的区块A,看到系统中除了自己还没人挖出来区块3后面的区块,而自己节点的算力很强大,就藏起来不发布,继续在自己的区块A的基础上挖出来区块B、区块C。系统中其他节点并不知道区块A的存在,依然在接着区块3往后挖,挖出来区块4、区块5时,恶意节点把自己的区块A、B、C三个区块突然发布出来,使自己的链称为最长合法链。

这样做的好处是,在接着区块A往后挖区块B时,因为没有发布区块A,所以没有竞争者,让别的节点做一些无用功。

比特币交易实现流程

但是这样做的风险很大,因为要和系统中所有其他节点的算力赛跑。万一自己在挖出区块A、区块B时,系统中其他节点挖出来了区块4、区块5、区块6,那么自己的区块A、B就作废了。

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...