BTC
bitcoin 的论文标题为 一种点对点的电子现金系统,可见它最初的设计目标是为了摆脱第三方中介,建立一个新型的信任模型。是区块链技术第一次实际运用。
密码学原理
哈希
collision resistance (这里指哈希碰撞) :
例如 x≠y H(x)=H(y) 两个不同的输入,输出却是相等的,这就称哈希碰撞。它是不可避免的,因为输入空间总大于输出空间。
给出x,很难找到y,除非蛮力求解(brute-force)。
该性质的作用:对一个message求digest
比如message取m m的哈希值是H(m)=digest 如果有人想篡改m值而H(m)不变,则无法做到。
collision resistance无法验证,是根据实践经验得来的。
hiding :
哈希函数的计算过程是单向的,不可逆的。无法从哈希值推出输入内容。
前提是输入空间足够大,分布均匀。否则可能被蛮力破解。可以在输入后面拼接一个随机数来取hash,保证输入足够大。
该性质的作用:和collision resistance 结合在一起,用来实现digital commitment(又称为digital equivalent of a sealed envelope)。把预测结果作为输入x,算出一个哈希值,将哈希值公布,hiding让人们知道哈希值而不知道预测值,最后再将x公布,因为有collision resistance的性质,预测结果是不可篡改的。
puzzle friendly :
哈希值事先不可预测,仅仅根据输入很难预测输出。在比特币中采用SHA-256哈希函数,需要一个哈希值,存在于某一个范围内,只能通过不停运算查找出来。该性质保证了比特币系统中,只能通过“挖矿”获得比特币。也就是说,该性质保证了工作量证明(POW)机制可以运行下去【“挖矿难,但验证易”】。
签名
比特币中账户管理 在第三方中心化系统中,账户开通依赖于第三方。但去中心化的比特币系统中,很明显不能 进行“申请账户”。在比特币系统中,申请账户是用户自己来处理的,即自己创建一个公钥-私钥对。(关于公私 钥请自行了解非对称加密体系和对称加密体系) 公钥和私钥的应用保证了“签名”的应用。当在比特币网络中进 行转账时,通过“签名”可以明确是由哪个账户转出的,从而防止不良分子对其他账户比特币的盗取。 在发布交 易时,通过自己私钥签名,其他人可以根据公钥进行验证,从而保证该交易由自己发起。也就是说,只有拥有 私钥,才能将该账户中的比特币转走。 【注意:比特币系统中,很难通过生成大量公私钥对来获取他人私钥】
数据结构
hash pointer (哈希指针)
从上图可以看出每个区块包含一个时间戳、一个随机数、一个对上一个区块的引用(即哈希)和上一区块生成以来发生的所有交易列表。 随着时间推移就创建出了一个持续增长的区块链,它不断地更新,从而能够代表比特币账本的最新状态。
区块链上的区块(除了创世区块)都有一个hash pointer指向前一个区块,通过前一个区块整体来获取一个hash。如果一个区块被恶意修改,则后面的区块hash都会无法匹配。
区块链系统中有的节点只需保存部分区块,等到需要其他区块时,再向其他节点获取,可以通过hash来验证正确性。
Merkle tree
比特币一个重要的可扩展特性是:它的区块存储在多层次数据结构中。 一个区块的哈希实际上只是区块头的哈希,区块头是一段约 200 字节的数据,包含时间戳、随机数、上个区块的哈希和默克尔树根的哈希,而默克尔树是一个存储了该区块所有交易的数据结构。
默克尔树是一种二叉树,由一组叶节点、一组中间节点和一个根节点构成。最下面是大量包含基础数据的叶节点,每个中间节点是其两个子节点的哈希,顶部的根节点也是其两个子节点的哈希。 默克尔树的目的是允许区块数据可以零散地传送:节点可以从一个源下载区块头,从其它源下载相关树的一小部分,而依然能够确认所有的数据都是正确的。
简化支付确认协议(SPV)允许另一种节点存在,这样的节点被称为“轻节点”,它下载区块头,使用区块头确认工作量证明,然后只下载与其交易相关的默克尔树分支。 这使得轻节点只要下载整个区块链的一小部分,就可以安全地确定任何一笔比特币交易的状态和帐户的当前余额。
协议
double spending attack
数字货币本身为带有签名的数据文件,可以进行复制。即:对用户来说,可以将同一货币花费两次。
对货币添加唯一编号(不可篡改),每次支付向货币发行单位查询真伪。 该方法每次交易都需要依赖于第三方机构来判断货币真伪且防止双花攻击。是一个典型的第三方中心化方案。
而比特币系统中是通过挖矿来决定货币的发行权和发行量,通过系统维护的区块链结构来解决双花问题
分布式共识
为了保证区块链内容在不同节点上的一致性,需要取得分布式共识。
FLP不可能结论: 在一个异步系统中,网络时延无上限,即使只有一个成员是有问题的,也不可能达成共识。
CAP(Consistency一致性、Availability可靠性、Partition tolerance容错性) Theorem: 任何一个分布式系统中,最多只能满足其中两个性质。分布式共识中协议Paxos 可以保证Consistency(若达成共识必然一致),但在某些情况下,可能会一直无法达成共识。
假设系统中存在部分节点有恶意,但存在比例较小。大多数节点为“好”的节点,在这种情况下进行共识协议设置。
想法1:直接投票某个节点打包交易到区块,将其发给其他节点,其他节点检查该候选区块,检查若正确投赞成票,若票数过半数,加入区块链。
- 存在的问题1——恶意节点不断打包不合法区块,导致一直无法达成共识,时间全花费在投票上。
- 存在的问题2——无强迫投票手段,某些节点不投票(行政不作为)。
- 存在的问题3——网络延迟事先未知,投票需要等多久?效率上会产生问题。
- 更大的一个问题——membership。如果是联盟链,对加入成员有要求,可以基于投票。但比特币系统,任何人都可以加入,且创建账户及其简单,只需要本地产生公私钥对即可。只有转账(交易)时候,比特币系统才能知道该账户的存在。这样,黑客可以使用计算机专门生成大量公私钥对,当其产生大量公私钥对超过系统中一半数目,就可以获得支配地位(女巫攻击)。
所以,这种简单的投票方案也是不可行的。
比特币系统中采用了很巧妙的方案解决这个问题。虽然仍然是投票,但并非简单的根据账户数目,而是依据计算力进行投票。 在比特币系统中,每个节点都可以自行组装一个候选区块,而后,尝试各种nonce值,这就是挖矿。[H(block header)<=target] 当某个节点找到符合要求的nonce,便获得了记账权,从而可以将区块发布到系统中。其他节点受到区块后,验证区块合法性,如果系统中绝大多数节点验证通过,则接收该区块为最新的区块并加入到区块链中。
可能出现分叉情况。当两个节点同时获得记账权,这时就会出现分叉,但区块链只承认最长合法链,随着时间推移,必然存在某一条链变成最长合法链。这样,也就会导致合法区块被拒绝
分叉攻击。A用户对上面的A转账给B的记录回滚,从而非法获取利益。在两条链上,发现交易都合法。这是一个典型的双花攻击。A给B转账后,用分叉攻击将钱又转回来,覆盖掉原来的记录。 在比特币系统中,这种情况实际上很难发生。因为大多数矿工认可的是最长的合法链,会沿着上面的链继续挖下去。而A这个攻击者要想回退记录,就必须使得下面的链变得比上面的链还长。理论上来说,攻击者需要达到整个系统中51%的计算力,才能使得这种攻击成功
激励机制
节点竞争记账权需要消耗算力和电力成本,为了让激励节点参与,系统会给出块节点 出块奖励,一个获得合法区块的节点,可以在区块中加入一个特殊交易(铸币交易)。事实上,这种方式也是唯一一个产生新比特币的途径。
比特币系统设计规定,起初每个区块可以获得50个比特币,但之后每隔21万个区块,奖励减半。因为平均出块时间为10分钟,可以算出大约四年减半。
区块中保存交易记录,如果仅仅设置出块奖励,那么,会不会存在节点只想发布区块获得出块奖励而不想打包交易?
BTC系统设计了Tranction fee(交易费),每个交易可以有多个输入,也可以有多个输出,但输入之和要等于输出之和(total inputs = total outputs)。 存在一些交易的total inputs 略大于 total outputs,这部分差额便作为交易费,给了获得记账权的节点。对于获得记账权节点来说,除了出块奖励之外,还可以得到打包交易的交易费。但目前来说,交易费远远小于出块奖励。等到未来出块奖励变少,可能区块链的维护便主要依赖于交易费了。
实现
UTXO
比特币采用了 基于交易的账本模式 。然而,系统中并无显示记录账户包含比特币数,实际上其需要通过交易记录进行推算。在比特币系统中,全节点需要维护一个名为 UTXO(Unspent Transaction Output尚未被花掉的交易输出) 的数据结构。
A转给B五个BTC,转给C三个BTC,B将五个BTC花掉,则该交易记录不保存在UTXO中,C没有花掉,则该交易记录保存在UTXO中
UTXO集合中每个元素要给出产生这个输出的交易的哈希值,以及其在交易中是第几个输出。通过这两个信息,便可以定位到UTXO中的输出。
判断一个交易是否合法,要查一下想要花掉的BTC是否在该集合中,只有在集合中才是合法的。如果想要花掉的BTC不在UTXO中,那么说明这个BTC要么根本不存在,要么已经被花过。所以,全节点需要在内存中维护一个UTXO,从而便于快速检测double spending(双花攻击)。
网络
bitcoin 网络层使用的是p2p网络,区块链运行在应用层。
节点之间的通信采用了TCP协议,便于穿透防火墙。
当一个节点要加入网络时,要先找到一个seed node (种子节点),通过这个种子节点来获得其他节点。退出时自行退出,过段时间其他节点就会把它从网络中删除。
网络设计原则: simple,robust,but not efficient
节点维护一个收到的待上链的交易集合,当第一次收到一个交易时,会转发给邻居节点并加入集合,下次收到时就不会转发。当交易上链就把它删掉。
假如网络中存在两个冲突交易,如交易1:A->B,交易2:A->C(假设花费的同一笔钱)。具体接收哪个取决于节点先接收到哪个交易,之后收到另一个交易会将其放弃。
新发布区块在网络中传播方式与新发布交易传播方式类似,每个节点除检查该区块内容是否合法,还要检查是否位于最长合法链上。区块越大,则网络上传输越慢。BTC协议对于区块大小限制为不大于1M大小.
此外,比特币网络传播属于 Best effort(尽力而为) ,不能保证一定传输成功。以一个交易发布到网络上,未必所有节点都能收到,也未必所有节点收到交易顺序都一致。
挖矿
从 blockchain.com 网站 截取一个最近的block:
可以看到,区块哈希与前一区块哈希都是以一长串0开头的,挖矿本身就是尝试各种nonce,使得产生的区块哈希值小于等于目标阈值。
看一下 block header 的代码描述: bitcoin
nonce是一个32位的无符号整型数据,在挖矿时候是通过不断调整nonce进行的,但可以看到,nonce的取值最多为$2^{32}$种。但并非将这些nonce全部遍历一遍,就一定能找到符合要求的nonce。由于近年来,挖矿人员越来越多,挖矿难度已经调整的比较大了,而这一搜索空间太小,所以仅调整nonce很大可能找不到正确的结果。
可以看到除了nonce其他的元素只有ntime和hashMerkleRoot可以进行调整。我们主要是调整MerkleRoot。
每个发布区块者可以得到出块奖励,也就是可以在区块中发布一个 铸币交易(coinbase交易),这也是BTC系统中产生新比特币的唯一方式。中可以写入任何内容,在这里写什么都没有影响。所以可以在这里添加一些任意信息,便可以实现无法篡改(也无法删除)。所以,只要我们改变了写入内容,便可以改变Merkle Tree 的根哈希值。
所以,在实际的挖矿中,包含两层循环。外层循环调整coinbase域(可以规定只将其中前x个字节作为另一个nonce),算出block header中根哈希值后,内层循环再调整nonce。
概率分析
挖矿本质上是不断尝试各种nonce,来求解这样一个puzzle。每次尝试nonce,可以视为一次伯努利试验。最典型的伯努利试验就是投掷硬币,正面和反面朝上概率为p和1-p。在挖矿过程中,一次伯努利试验,成功的概率极小,失败的概率极大。挖矿便是多次进行伯努利试验,且每次随机。这些伯努利试验便构成了a sequence of independent Bernoulli trials(一系列独立的伯努利试验)。根据概率论相关知识知道,伯努利试验本身具有无记忆性。也就是说,无论之前做多少大量试验,对后续继续试验没有任何影响。 对于挖矿来说,便是多次伯努利试验尝试nonce,最终找到一个符合要求的nonce。在这种情况下,可以采用泊松分布进行近似,由此通过概率论可以推断出,系统出块时间服从指数分布。(需要注意的是,出块时间指的是整个系统出块时间,并非挖矿的个人)
系统平均出块时间为10min,该时间为系统本身设计,通过难度调整维护其平均出块时间。 指数分布本身也具有无记忆性。也就是说,对整个系统而言,已经过去10min,仍然没有人挖到区块,那么平均仍然还需要等10min(很不符合人的直觉)。也就是说,将来要挖多久和已经挖多久无关。
工具演化
CPU->GPU->ASIC专用矿机
矿池
矿池通常是一个全节点驱动多台矿机。矿工只需要不停计算哈希值,而全节点其他职责由矿主来承担。ASIC芯片只能计算哈希值,不能实现全节点其他功能。此外,矿池出现解决了单个矿工收益不稳定的问题。当获得收益后,所有矿工对收益进行分配,从而保证了收益的稳定性。
矿池一般具有两种组织形式。
1.类似大型数据中心(同一机构),集中成千上万矿机进行哈希计算。
2.分布式。矿工与矿主不认识(不同机构),矿工与矿主联系,自愿加入其矿池,矿主分配任务,矿工进行计算,获得收益后整个矿池中所有矿工进行利益分配。
难度调整
在比特币系统中,区块链的出块时间保持在平均10min左右。毫无疑问的是,伴随着参与挖矿的人增多,系统总算力不断增强,出块时间越来越短,虽然提高了系统效率但是增加了不稳定性,不利于达成共识,所以挖矿的难度绝对不能一成不变。
这里的难度系数就是上面block信息中的difficulty。
H(block header)<=target.(target便是目标阈值,target越小,目标难度就越大)对于挖矿难度的调整,可以视为调整目标空间在整个输出空间中所占比例大小。
之前有提及,比特币系统采用的哈希算法为SHA-256,所以整个输出空间大小为$2^{256}$,调整目标空间所占比例,简单的说需要目标值前需要多少个0。 当然,挖矿难度和目标阈值成反比,如下图所示,其中difficulty-1-target为是挖矿难度为1时候的target,即最小挖矿难度:
$$difficulty = \frac{difficulty-1-target}{target} $$在BTC协议中规定,每隔2016个区块需要调整一次难度,根据10min产生一个新区块可以得到,大概需要14天的时间。具体调整公式如下:
$$\text { target }=\text { target } * \frac{\text { actual time }}{\text { expected time }}$$可见,如果实际时间比较长,target会比较大,相应的挖矿难度会降低;如果实际时间比较短,target会比较小,相应的挖矿难度会增大。
如何让所有矿工都愿意调整这个挖矿难度呢? 这一调整算法在代码中已经写入,如果有恶意节点故意不调,其所产生的区块不会被大多数诚实的节点承认。在block header中有一个nbits的域,它是对target的编码存储(target为256位,nbits为32位,也就是说block header并未直接存储target),其他节点在进行合法性验证时候会验证nbits域是否合法,不合法则对该区块不予以承认。
挖矿难度变低是好事吗? 对于矿工来说,挖矿难度变低,挖矿变得更容易,这也说明大多数人对该币种不再看好,这个币种的价值也会大跳水,这对矿工来说可是一个坏消息。
安全性分析
因为矿池的出现,引发了对51%算力攻击的担忧,矿工只负责计算,并不知道矿池的行为(乌合之众)。,51%攻击只是一个概率问题,并非达到51%算力就能发动攻击,不能达到就无法发动攻击。此外,矿池本身算力也是在不断变化的。
- 分叉攻击: 比特币协议中,缺省需要等6个确认区块,此时才认为该记录是不可篡改的。平均出块时间10min,六个确认区块便需要1小时,可见等待时间还是相对较长的。
- 封锁交易(Boycott) 假如攻击者不喜欢某个账户A,不想让A的交易上区块链,在监听到有其他人将A的交易发布到区块链上时,立刻发动分叉攻击,使A所在链无法成为”最长合法链“。这样,便实现了对A账户的封锁。
- 盗币(将他人账户BTC转走) 这个是不可能的,因为其并没有他人账户私钥。如果依仗算力强,强行将没有签 名的转账发布到区块链,正常节点不会认为其合法,这样,即使这条链再长,其他人也不会认为其是最长合法链。
分叉
分叉指的是,原来的系统中为一条链,但分成了两条链。分叉形成的原因可能有多种,例如:挖矿时两个节点差不多同时挖出矿,都会发布区块(对比特币系统当前状态产生分歧导致的分叉——state fork);分叉攻击,同样也会导致分叉(forking attack,人为故意造成);比特币协议改变,在分布式系统中不能保证所有节点同时升级软件,假设存在少数节点未升级,导致出现分叉(protocal fork);
硬分叉
对比特币协议增加新协议,扩展新功能,未升级软件的旧节点会不认可这些修改,会认为这些特性是非法的。这也就是对比特币协议内容产生分歧,从而导致分叉。硬分叉的一个典型例子,就是对比特币区块大小的修改
如果我们将区块最大限制增大为4MB,对于新节点来说,上面的为最长合法链,新节点便都会沿着上面的链继续挖;对于旧节点来说,上面的链无论多么长,都是一条非法链,不会认可该链,所以旧节点就会沿着下面的链继续挖矿。此时,就出现了新节点永远沿着上面的链挖矿,旧节点永远沿着下面的链挖矿,由于新节点算力足够强,所以形成两条永远都在延伸且平行的链。出现hard fork后,便变成了两条平行的链,也就造成了社区分裂。
软分叉
如果对BTC协议添加限制,使得原本合法交易在新交易中不合法,便会形成软分叉。
假设系统中大多数节点更新了软件,少数节点仍然遵从1MB限制的协议(注意,这里大多数和少数是按照算力来区分的,和账户数量无关)。即:新节点认为区块大小最大0.5MB,旧节点认为区块大小最大1MB,且新节点占据大多数
新节点只认可新节点挖出的块(而且占大多数),旧节点挖出的区块一直被抛弃,无法得到出块奖励(不在最长合法链上)。这就倒逼旧节点升级软件,最终会实现区块链上的所有矿工共同认可新协议,实现软件协议的升级。
soft fork 特点:只要系统中拥有半数以上算力节点更新软件,系统就不会产生永久性分叉 hard fork 特点:必须系统中所有节点更新软件,系统才不会产生永久性分叉
匿名性
比特币中的匿名并非真正的匿名,而是假的匿名。纸币的匿名性更好,因为其并没有对个人信息的标记。也正是因为其匿名性,很多非法交易采用现金。
比特币中的数据是完全公开的,而网上的交易是要与实体世界进行交易的,所以大大破坏了其匿名性。假如银行允许用假名(以前的存折时代),由于银行数据并非公开,所以银行系统的匿名性是要比比特币更好的。
从应用层看,可以将各个不同用户的BTC混合在一起,使得追查变得混乱(Coin mixing);从网络层看,可以采用多路径转发的方法,数据不直接发送出去,而是经过很多跳(洋葱路由的基本思想)。
实际上,暴露用户隐私正是由于区块链的公开性和不可篡改性。不可篡改性对于隐私保护,实际上是灾难性的。
零知识证明
零知识证明:一方(证明者)向另一方(验证者)证明某一个陈述是正确的,但不需要透露除该陈述是正确的之外的 任何信息。 例如:A想要向B证明某一账户属于A,这说明A知道该账户的私钥。但不可能通过A公布私钥的方法来证 明,该账户确实属于A。因此,A可以产生一个账户签名,B通过公钥对签名进行验证。(实际上该证明是否属于零知 识证明存在争议,因为泄露了用私钥产生的签名)
零知识证明的数学基础便是同态隐藏。
一些其他相关的: 盲签,零币零钞
哈希指针
在block header中只有hash值,没有指针。那么如何查找到前一个区块的内容? 全节点一般将区块存储 于一个key-value数据库中,key为哈希,value为区块内容。常用的key-value数据库为levelDB,只要掌握到最后一 个区块的哈希值即可依据哈希值一直往前找到区块链所有内容。 有些节点只保存区块链部分信息,如果需要用到前 面的区块,可以问其他节点要。哈希指针性质保证了整个区块链内容是不可篡改的
量子计算
会不会BTC这种建立在密码学上的加密货币,在量子计算出来后会不会变得不安全。 一. 量子计算距离使用仍然有很 长距离(人工智能也是,目前仍然处于弱人工智能阶段。其实很多技术都是如此,炒的情况很严重,但距离实用很 远。但是不炒便不会有资本流入进行研究,这也是一个非常相悖的地方)。 二. 量子计算若真正使用到破坏现有加密 算法,对传统金融业的破坏仍然是最大的。 三. 实际中使用的并非公钥,而是采用公钥哈希。而哈希函数一般都是不 可逆的,所以即使量子计算也无法反推私钥。 BTC中用的SHA-256,无论输入多大,最终结果都为256位,必然会导 致信息丢失,无法反推原本数据。 总结:加密可逆、哈希不可逆;加密不损失信息、哈希破坏信息(加密和哈希的区别)