[写给程序员的]比特币科普文

区块链是目前技术热点之一,有人将它与互联网媲美,也有人声称区块链没有未来。确定的是在大多数人对区块链的理解仍停留在比特币等加密数字货币概念的时候,已经有公司和个人尝试将其应用于其它领域。若要对一个事物作出合理评价,较深入的了解必不可少,而了解的较快方式是从某个具体应用入手。现在最知名和“成功”的区块链应用仍然是与它一同诞生的比特币,网上不乏有比特币的科普文,但是本人看来,这些文章要么写得太抽象,要么写得太通俗,看完之后能得到几个概念,但若要深想下去还是一头雾水。本篇是之前为公司培训做的ppt整理而来,主要阐述了比特币的核心概念,ppt面向公司全员,删除程序员必备知识后整理成该博文。

注:本文旨在帮助大家理解比特币[&区块链],所以会有意简化一些概念,即不保证描述和实际场景完全一致。关键点粗体显示。

区块链 = 链成一串的区块集合

[写给程序员的]比特币科普文

这肯定没有异议吧,那么区块里面包含什么呢?暂时可以认为包含如下东东:

[写给程序员的]比特币科普文

重点关注散列值,它的身影贯穿区块链始终。目前散列函数有很多种,以SHA256为例,输出有2256种可能(培训时费了老大劲让大家对这个数的大小有个感性的认识)。散列函数的特点是不可逆,即给定一个散列值,推算出对应的输入基本上是不可能的。这种特性让它有了防篡改的功能,比如区块被广播并被其它节点确定后,若再去更改区块里的其它字段(比如交易的比特币金额,存于数据字段中),会造成新数据计算得到的散列值≠原散列值,那这个区块一定是有问题的。我们用这种方式保障了区块的安全/合法性。

目前区块链典型应用之一知识产权保护的关键就是防篡改和相关度计算。

那么数据本身的安全呢?众所周知,[公共]区块链是匿名的,那么如何知道数据属于谁,谁能存取该数据,这里就引出了密钥的概念。

密钥:用于加密和解密的一种规则

[写给程序员的]比特币科普文

根据加密密钥和解密密钥是否相同,又可分为对称加密和非对称加密。各位对它们一定非常熟悉,它们在纯粹加解密上的优缺点这里就不细说了,在效率上,前者一般都要高于后者。因此我们使用非对称加密并非冲它的加密效果去,而是为了使用它“非对称”这个特性带来的好处,现实中有很多场景用到,比如调用第三方openapi,或者更复杂的oauth认证过程,调用方都会以私钥签名调用信息,服务方使用调用方的公钥校验调用者的合法性。这个特性使得匿名和去中心化成为可能,一次比特币的交易可如下图所示:

[写给程序员的]比特币科普文

XX将1000B打入Bob的账户(即用Bob公钥加密),Bob私钥解密后取出

公钥不需要含有任何Bob的个人信息,所以虽然其他人可以使用公钥将数据绑定到这个“账户”,但也仅此而已,他们无法也无需知道这个公钥到底是属于谁的(除非公钥所有人自己公开个人信息);只要有人能将之解出,那就表示那人有对应的私钥,亦即他的身份是合法的(如果他将私钥给了别人,那也无话可说)。于是,一个匿名的交易就完成了。

当然,上述图示是非常不严谨的。首先,比特币网络是个闭环,Bob无法将比特币取出(你见过比特币长啥样?),当然也无法主动存入,这类似于我们只能在ATM机上进行账号间的转账操作,而不能取现;那Bob私钥解密后的比特币去哪了呢,或者说,本来这笔钱在“公钥里”呆的好好的,取又取不出,你把它解出来干啥?答案是当Bob要将钱打入其他人的公钥的时候,他就要用私钥转账了;而之前XX转给Bob亦是如此。所以更准确的图示如下:

[写给程序员的]比特币科普文

比特币的交易链

现在让我们看下比特币中的加解密过程具体是怎样的。

首先我们把交易给它结构化,可以简单理解为:交易 = 我的钱转给你 = 我的钱 + 转给你。

由上图可知:我的钱 = 我用私钥解出的别人转到我公钥的钱 = 别人转给公钥的钱 + 我用私钥解出;转给你 = 转多少到你的公钥。

翻译成数据结构,示例如下:

    {
        from: {
            上一笔交易to数据的ID: "7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",
            key: 私钥解密(用于证明我拥有上一笔交易转出的钱)
        },
        to: {
            本笔输出的ID: "8ae4538afc617cd284d36d135fe09f1a0d2f42a22d890d548f6f65cda45e6f1d2",
            转出多少钱: 0.01500000,
            lock: 公钥加密(只有正确解密才能获得转出的钱)
        }
    }

我们将上述代码的key和lock称为脚本。lock和key可看作问题与答案,注意from中的key解密的是上一笔转给我的交易的lock。比如XX转了一笔钱给Bob,同时设置了lock:1+1=?——?是占位符,之后用key代替——等到Bob要花这笔钱时,他一定要提供正确的key值(此处是2),否则无法通过节点的交易校验,同时Bob也要设置本笔交易的lock,以此类推。key与lock可以互换,只要合在一起等式成立即可。

将公私钥代入脚本,可构建如下交易:

XX转给Bob——

    {
        from: ……,
        to: {
            本笔输出的ID: "7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",
            转出多少钱: 10,
            lock: 转入方的公钥
        }
    }

Bob转给Alice——

    {
        from: {
            上一笔交易to数据的ID: "7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",
            key: ? 解密 (私钥加密(本次交易的散列值)后的密文)=本次交易的散列值
        },
        to: ……   
 }

节点校验Bob转给Alice这笔交易,会将上一笔交易lock与当前交易key合并,此处即为:转入方的公钥解密 (私钥加密(本次交易的散列值)后的密文)=本次交易的散列值。易得,若公私钥是一对,那等式成立。

所谓节点,我们可以理解为日常听说的比特币矿工。他们无时无刻不在搜集交易,并将它们打包进区块,这个过程就是“挖矿”。挖矿 = 新建有效区块,具体过程如下:

  1. 交易校验无误后,节点将其放入本地的“未确认交易池”;
  2. 节点选取部分或全部未确认交易(一般选取佣金相对高的交易,佣金=from的金额-to的金额,可以自己找零调节,比如XX转Bob10块,Bob以此转Alice8块,转自己即找零1.5块,那么佣金为0.5块),放入本地临时区块中;
  3. 计算该临时区块散列值。

如图:

[写给程序员的]比特币科普文

图中上一个区块的ID取的是区块链中当前最末的有效区块的ID。而计算得到散列值是刹那间的事,如果交易数据足够多,那每秒都能产生万亿个区块,如此,则吞吐量杠杠的。但是亿万个区块承载的数据未必相同(根据节点自己的选择而来),更不用说有同笔交易被多个节点打包的情况,为了账本的一致性,我们需要避免链分支(分叉)的出现,能添加到链末尾的只能是其中一个区块,即只能承认其中一个区块是有效的。那么如何选出这个区块,并让其他节点心服口服呢,这就是共识算法需要解决的问题。

比特币使用的共识算法是POW,proof of work,工作量证明。具体到实现,就是提高计算散列值这个环节的难度。怎么提高呢?

中本聪说,你算出的散列值一定要符合某种的规则,比如一定要是“9ce4538afc617cd284d361d53fe09f1a0d2f42a26d890d548f6f65cda45e6f1d2”这个数,如此,你的区块就是可以被承认的。

此言一出,众矿工纷纷吐槽:哇,中大神你懂不懂的,数据(交易)我都打包好了,出来的散列值还能变吗?你说重新打包?选佣金低的试?求求你不要秀了。

中本聪说,行吧,给你们加个字段,该字段可随意取数,汝等将其一并纳入散列值计算中,该字段的值的改变势必会影响到散列结果,如此,只要找到正确的随机数,那么就能得到规定的结果,那么,区块就可以被承认。

now,挖矿变成了:新建有效区块 = 计算出符合规则的散列值 = 获得一个使计算出的散列值符合规则的随机数
[写给程序员的]比特币科普文

规则为:

[写给程序员的]比特币科普文

我们知道,2256是很大的一个数字, 要找到一个随机数(为了覆盖整个输出值域,该随机数也得是同量级位数),使得结果恰好等于其中之一,以目前计算机能力,到时光尽头也未必能找到。所以说,这个规则定义的难度太大。

那么,我们制定一个难度不大的规则,如下:

[写给程序员的]比特币科普文

只要散列结果落入特定区间即可,这个难度的量化不言自明:若红色区间占满整个横条,那么难度是最低的,即得到的任意散列值都满足条件,也就是刚开始没有规则的状态;如若占到一半,那么概率上讲每两次都有一次会成功,难度也不大;若调节到亿分之一,那么概率上需要试错一亿次。也就是说,我们可以通过调节区间大小调节计算难度。比特币目前是保持平均10分钟出块的速度,且会根据前几次的出块时间自动调节计算难度,而不至于由于硬件发展等因素导致速度的变化。另外,随机数字节数也不用很大,比特币中是4个字节。

在挖矿时,nonce随机数是未知的,要从0试到232,但是这个数字其实不大,只有4294967296,以现在的矿机动辄14T每秒的算力,全部算完到上限也不需要一秒,肯定无法覆盖难度对应的散列值域。所以我们需要使用创币交易中的附带信息,额外的字符串成为extra nonce,参看 比特币挖矿究竟在计算一个什么问题?手动验证区块链给出答案。至于为什么是10分钟,这个就见仁见智了,有人说考虑到网络延时、带宽,10分钟是一个相对合适的时间,也有人觉得太长,比如LTC就是2.5分钟。其实,这只是当初中本聪的一个设定,总要设置个时间嘛,只要不太短就行你说是不。

挖到矿后,节点需要将区块入链。节点通过p2p向外广播,其它节点收到后校验区块合法性,无误后将该区块存入自己的本地链,同时继续向外广播,并开启下一轮挖矿竞赛。通过p2p,所有节点的本地链都可以保持一致,这样,去中心化就实现了。

值得讨论的是,在新区块发现到被网络确认的时间段内,如果有多个节点同时找到有效的随机数,或者节点收到其他多个节点的挖矿成功的消息,以哪个为准呢?答案是随意,因为一般来说这种临时分叉的情况会随着后续区块的挖掘自动消失,因为比特币优先选择链最长的那条分支,除非能保证各分支出块的速率完全一样,否则总会出现一定的时间差,从而导致其中一条分支变得更长成为主链,其余分支被网络抛弃(也可能有恶意节点维持短分支,加大算力以图在将来超过主链,这就涉及到51%攻击了)。

康奈尔大学的研究人员Ittay Eyal和 Emin Gun Sirer发表的一篇论文里,描述了只要掌握25%算力即可成功攻击比特币网络的场景,可参看 Selfish Mining: A 25% Attack Against the Bitcoin Network

 

转载请注明本文出处:https://www.cnblogs.com/newton/p/9496872.html

上一篇:《从问题到程序:用Python学编程和计算》——3.3 程序终止性


下一篇:某电商平台开发记要——全文检索