看完这篇区块链的文章,就有东西和别人扯皮了,而且扯的还很好

网上有很多关于区块链的文章,要么过于深奥,对于初学者很难弄懂,要么过于浅显,也很难想通。这篇文章从底层讲起,由浅入深,从密码学算法讲起,不断深入,算是一遍比较完备的区块链——比特币的文章。文章以比特币为例,来讲解区块链。因为区块链最初就是因为比特币被人们熟知的。坚持把这篇文章看完,比特币的知识就基本了解了。在辅以区块链的一些其他应用,就会真正在区块链这个方面上一个台阶。

1. 比特币中的密码学知识

 1.1 哈希函数

  又称散列函数,散列算法。基本原理就是把任意长度的输入,通过Hash算法变成固定长度的输出这个映射的规则就是对应的Hash算法,而原始数据映射后的二进制串就是哈希值。最常使用的MD5和SHA都是历史悠久的Hash算法。(文末有sha256算法代码)
  简单来说,什么是哈希算法呢?例如比特币中用到的哈希算法SHA256。对于任意长度的消息,执行SHA256算法都会产生一个256bit长的哈希值,称作消息摘要。例如:SHA256(“任何东西”)=01011101010…,一共256个0和1。比如说一张图片,又或者是一段视频,只要执行SHA256算法,就会输出一个256bit的字符串,这个字符串就叫做摘要

下面是比特币中哈希函数的重要特性

  • 【1】collision reistance

  碰撞避免(就是说一般不可能发生碰撞)
  哈希碰撞指的是:x \neq y,H(x)=H(y), H指的是哈希函数,H(X)指的是将X进行一次哈希运算。理论上哈希碰撞是不可避免的但是,一般来说,对于好的哈希算法,人为制造哈希碰撞的可能性几乎为零!这被称为:collision reistance。
  哈希碰撞不可避免证明:例如,SHA256,能将任意的输入转换为256bit的串。输入空间有无穷大,输出却只有2256。根据鸽笼原理,一定会有两个不同的输入,对应相同的输出。
  collision reistance有什么用呢?它比较重要的一个应用是,验证一个message是否被篡改。例如:如果对message进行哈希运算,即H(m)。一旦message被篡改,那么可以说他的哈希值一定与之前的哈希值不同。

  • 【2】hiding

  哈希运算单向,不可逆
  正向运算很简单,也很快,但是逆向运算几乎不可能。也就是说,给你一个值,要你算它的哈希值,这个很简单。但是,给你一个哈希值,你不可能知道它的输入。(输入空间足够大,分布足够均匀)。

  • 【3】puzzle friendly

  哈希值的运算,事先是不可预测的
  也就是说,如果要求你的哈希运算结果是a,对于想知道输入是什么,没有什么好的办法,只能一个一个去试,不可能猜到应该输入什么,也没有什么好的方法去解开输入,只能暴力破解(如果输入空间很大,那么就称不可能破解)。

 1.2 数字签名

  这里就要说一下,比特币系统中的账户管理。如何开一个比特币账户呢?开户过程很简单,就是创立一个公钥、私钥对。公钥就好像你的银行账户,只要别人知道你的公钥,就可以转钱给你。私钥就好像你的账户密码。
  公钥、私钥又是什么呢?这就要提起非对称加密(RSA)——在计算机网络中很常见的一种加密算法。
  公钥和私钥唯一对应,用某个公钥签名过得内容只能用对应的私钥才能验证签名;同样用某个私钥加密的内容只能用对应的公钥才能解密。公钥私钥,常涉及两种情况:

情景假设:A有一个属于自己公钥、私钥对。B有一个属于自己的公钥、私钥对。当A想给B发一条消息时(公钥是公开的,私钥只有自己才能知道):

  • 签名:
    (1)A将要发送信息做一次哈hash,得到一个hash值,再将hash值用A的私钥加密作为签名,后缀在信息的末尾。
    (2)B接到传输的资料后,使用A的公钥解密这段加密过的hash,得到hash值,然后对信息原文进行hash,对比两次hash是否一致(验签)。

  • 信息加密:A用B的公钥对信息内容和签名进行加密,这样,只有拥有B私钥的人(也就是B)才能将信息和签名解密。解密后,B再用A的公钥对签名进行验签

在使用RSA进行通讯的时候,一般是两者结合,即:签名->加密->解密->验签


2. 区块链的数据结构

 2.1 区块

  区块链由一个个区块(block)组成。区块很像数据库的记录,每次写入数据,就是创建一个区块。每个区块包含两个部分:

  • 区块头:记录当前区块的特征值

在这里插入图片描述

  • 区块体记录的就是交易的详细信息
    在这里插入图片描述
      区块链中的一个个区块是怎样链接起来的呢?区块链中用哈希指针代替了普通的指针。也就是,区块头中存了前一个区块的哈希值(父区块的哈希值),通过这样将区块链接在了一起。(哈希指针并不是真的存在,只是这么个意思)

  区块的哈希值是通过区块头中的信息进行计算的,对区块头进行二次哈希计算得到的数字被称为区块的哈希值。这个哈希值是区块的标识符,可以通过这个哈希值找到对应的区块(当然也可以通过区块高度来查询,但区块分叉时,区块高度有可能会对应多个区块)。
  我们知道当前区块中包含来前一个区块的哈希值,但自己本区块的哈希值是不存储当前区块结构中的。而是当该区块被接收时由每个节点计算出来。然后存储在一个独立的索引数据库中的,这样可以方便检索。

 2.2 Merkle 树 (区块中交易信息的组织形式)

  区块链中的节点分为两类,一类是全节点,一类是轻节点(SPV)。全节点是保存区块的整个内容。轻节点是保存区块头(不包括具体交易信息),像手机上的区块链钱包通常就是轻节点。

  由于在一个区块里面包含很多交易信息(以哈希值来表示),这些交易信息就是通过Merkle树进行表示的。那么要怎么得到这颗树的呢?
  Merkle树是自底向上构建的。假设区块中有A、B、C、D 4个交易信息,那么将这个4个交易信息分别哈希之后,构成Merkle树的叶子节点。

  • A节点的哈希与B结点的哈希又组成了它们父节点的哈希值H(AB)
  • C节点的哈希与D结点的哈希又组成了它们父节点的哈希值H(CD)
  • 最后HAB节点与HCD节点又组成了根结点的哈希H(ABCD)
  • 而H(ABCD)就是Merkle根,它归纳总结了所有的交易信息。

在这里插入图片描述
  有人可能要问了,这是二叉树,如果区块中的交易个数为奇数,那么如何计算呢?那就将最后一个交易复制一份然后就可以组成满二叉树了。

  那么,这个Merkle树到底有什么作用呢?
例如下图
  如果轻节点想验证某个交易(下图中的tx)是否在某个区块中,那么,它要向全节点求助。但是,全节点不会把区块的所有信息发送给轻节点来验证,这样速度会大打折扣。

  • 如果全节点发现,轻节点问的交易信息tx确实在区块中。那么,全节点就会把下图的红色节点的值发给轻节点,
  • 绿色节点的值,轻节点自己就能计算。
  • 轻节点根据这些哈希值,就能算出根节点的哈希值。再和自己存储的区块头中的Merkle根哈希值对比,如果一样,就说明信息tx确实在区块中。

PS: 这里要声明一下,有很多人会很疑惑,当全节点知道交易信息tx在区块中,直接告诉轻节点结果不就行了?为什么还要把一条通往根节点的路径发给轻节点?
  这是因为,区块链中会有恶意节点。如果全节点告诉你的消息是假的怎么办?这时候hash就派上用场了。因为轻节点已经保存了一个正确的Merkle树根哈希值,这样,任何一个全节点,是无法创造出一条错误路径,最后算出的Merkle树根哈希值与轻节点的值等同(除非这条路径是确实存在的),这是由hash性质决定的。
在这里插入图片描述
  Merkle树的另一个重要作用是,只要记住Merkle根,就能检测出对树中任何一个部分的修改。也就是说,Merkle树中任何一个交易信息被篡改,那么最后算出的Merkle树根的哈希值一定与之前的不同。

3. BTC协议

 3.1 交易

  每个交易都由一个输入、输出组成。当前这个交易的输入脚本和提供币来源的那个交易的输出脚本拼在一起,如果能执行,就是那这个交易是合法的。关于输入、输出脚本,后面会具体解释。

举个例子:一笔交易,需要的信息(输入、输出):

  • 输入:A的公钥(签名)、A的币的来源(A的币是来源于哪个交易) 、转账的数量等
  • 输出:收款人的地址、收款人的公钥(或者公钥的哈希等)

铸币交易:所谓的铸币交易,就是挖矿所得的奖励。例如,A挖矿成功,获得比特币奖励,这个交易就叫做铸币交易。这个交易只有输出,没有输入。

  • 每个区块都有一个铸币交易。
  • 铸币的这次交易,有一个域,叫coinbase域,这个域里面可以存储任何东西而不影响其他因素,存储权在挖矿成功(也就是获得打包权力)的人手里。

 3.2 双重支付问题

  数字货币最常见的一个问题就是双重支付问题。
  比如说A有10个BTC,他却同时发了两条消息,一条消息是给B10个,另一条消息给了C10个,这该如何鉴别(纸质货币就不会出现这样的问题)?
  这里要首先说一下余额检查。区块链是把好多交易信息一个块一个块进行打包,然后链接起来的。每一个矿工(参加记账的人),都会把把从创始区块开始的所有区块下载下来(主链)。这样,他就可以知道别人的余额。例如:A广播了一条消息,说A给了B10个BTC,那么收到消息的人,就会在区块链上查询、计算,看看A的比特币最初是从哪来(挖矿获得、或者是别人给他)、A自己用了多少、还剩多少。若A剩余BTC大于等于10个,则这个消息被网络认可,可以作为一条记录被打包放到区块链上,否则网络不接受这个消息。这一过程称之为追溯。
  这里要重申一点,很重要!只有交易记录被打包放到区块链上,才叫真正的交易成功,否则,交易失败
  利用余额检查,就可以解决双重支付的问题。接着上边所说的问题,若D先收到消息1(D查询知道,此时A余额已经为0),那么他就会拒绝接收消息2。与此同时,若E先收到消息2(E查询知道,此时A余额已经为0),那么他就会拒绝接收消息1。至于说,D和E谁接受到的消息是对的,就看D和E谁先算出那个数学题,谁先把自己的消息记录打包到主链上。
在这里插入图片描述
  所以说,当我们要接收别人的付款时或者付款给别人时,我们不能就认为钱已经到账了,我们要等着,只有那条付款记录被打包放到主链上才叫真正到账成功。一般来说,经过6个确认之后(也就是6个区块之后),就能保证交易成功。

 3.2 分布式共识(记账)

  区块链系统中要记账,但是记账以谁为准?(你银行账户里有多少钱,不是你说多少就是多少,而是中央银行里记录你有多少钱)。但是区块链是去中心化的,不存在一个中央机制去记账。区块链中的账本是分布保存在所有的区块链节点中的,也就是说区块链系统中每个节点都维护一个相同的账本。

  简单的来说,区块链有一条主链,这条主链是公开的,是大家都承认的,这个链就是由一个一个区块组成,每个区块都包含交易信息。每个人都有自己的个人区块和主链,个人区块里面记录他刚刚接收到的交易信息。主链就相当于一个账本。那么,谁能往账本上添加信息呢(谁能将一段时间内的交易信息打包成区块,然后接到主链上)?

  每个人通过工作量证明机制(POW)来争夺记账权,他们将会计算一个很复杂的数学题,第一个计算出正确结果的人获得打包的权力。这个数学题很难很难,难到没有任何一个人能直接用脑袋算出来。必须一个一个去尝试,类似于暴力破解(就是用计算机去暴力搜索),破解的过程就叫挖矿。一旦这个数学题被成功破解,那么破解的人会获得系统给它的奖励,这就叫挖矿成功。这个奖励是比特币的真正来源所在。

比特币系统中,发行比特币的唯一方式就是挖矿所得的比特币(被称为铸币交易——coinbase transaction)。
  中本聪设计的奖励方案是,每十分钟生成一个区块,每打包一个区块会奖励一定数量的比特币。最开始每打包一个区块是50个BTC,过4年会奖励25个BTC,再过4年再减少一半,以此类推。这样比特币的产生会越来越少。越来越趋近于一个最值,计算公式:50×6×24×365×4×(1+1/2+1/4+1/8+…)≈2100万,其中最初奖励50个比特币,每小时有6个区块,每天24小时,每年365天,前四年是如此,之后每四年减半。也就是说,流通的比特币最多只有2100万个,通过打包奖励将其扩散出去,比特币不是无限的。

  • 每个人都会有属于自己的一个字符串,包括:父区块的哈希值、自己收到记录(也就是账单)、时间戳、随机数nonce、和难度系数n等。(除了交易信息,其他的都在区块头中)
  • 每个人都将自己的这个字符串作两次哈希运算,得到自己哈希值。Hash=SHA256(SHA256(字符串))= 一个256bit的字符串
  • 系统要求,Hash值的前n位为0的人(这就是前面说到的数学题),即为获胜者,将获得打包的权力,将自己的Hash值放到区块头部里面,再链接到主链上,然后拿到奖励,挖矿成功!
  • 第一个成功计算出数学题的人,将获得打包权力。再将区块广播给所有人,别的节点在检查完区块内信息的有效性之后,就会把这个区块加到自己本地保存的主链上。
  • 那如何才能使前n位为0呢?通过不断的改变随机数nonce的值,来一个一个尝试,直到找到符合要求的随机数。谁先找到,谁就挖矿成功。这里要注意一下,每个人的计算难度是不同的,因为每个人所记录的信息、时间戳等都不同,所以每个人的正确答案也不一样。有的人可能运气好,算一次,就可以找到正确答案,有的人算几万次都算不出来,这也是有可能的。但平均来看,谁的CPU在单位时间内计算的次数越多,谁就能更快的找到答案。这也是大家为什么拼命的去买高性能矿机的原因。
  • 上边的这个n,是如何确定的呢?很显然可以看出,n越大,题目就越难。平均每十分钟产生一个区块,总体上来看,挖矿成功的概率为1/2n。现假设世界上有1W台矿机,每台矿机的算力是14T次/s = 1.4×1013 次/s,即一秒可以计算这么多次。10分钟是600s,所以10分钟可以做1.4×1013× 600×104=8×1019次哈希运算,从概率角度看,想要挖矿成功需要做2n次运算,列出等式2n = 8×10^19,可以解出n约为66。这时,n就被设置成66,第一个算出前66位为0的人将获得打包的权力。n的值是动态的,随着矿机的增加、计算能力的增加,难度也会随之增加,保持出块时间稳定。所以,挖矿没有捷径,只能买更多、更高性能的矿机来增加单位时间内计算的次数。
  • 比特币系统中规定,每2016个区块(14天)改变一次难度系数。公式是:target=target*(actual time/expected time)。actual time是过去两个星期产生2016个区块的实际时间。expected time是预期产生2016个区块的时间(也就是两个星期)。这里有一个规定,为了防止意外情况,target不能超过4倍(不管是增加还是减少)。

  PS:这里多说一句,比特币系统中实际上是没有这个n值的。n只是通俗来讲,准确的说,数学题应该是:H(H(block header))<=target,target才是挖矿的难度系数。target越大,挖矿难度越小。这个数学题通俗来说就是前n位为0

 3.3 最长合法链(防篡改)

  考虑一种情况,因为网络中传输延迟的存在,有可能存在两个人同时破解了这个数学题(虽然这种可能性很小,但是还是存在的)。那么,主链就会出现分岔
  解决这个问题的方法就是最长合法链。比特币里面的最长链原则,假设某一个区块后面有两个矿工同时挖到了矿,或者由于网络延迟等原因产生了分歧,这时,各个节点先根据自己认为对的区块链分支,接着挖矿直到下一个区块产生,这时会有两条链,但是有一条是长的,比特币规定,以最长的链为准。如果某个节点仍然的固执的以较短的链为准,他就是在和大多数算力作对,这样做的结果是,他挖的块不被大家认可,会浪费时间和算力。

还有一种情况,有恶意节点想篡改账本(就是想篡改主链上的信息):
  假设A转给B10个比特币,但是他想把这个信息从区块链上删除,这样大家就都不知道这个事情存在,就可以赖账。
在这里插入图片描述
  如果有一个人想要篡改信息(假设他想篡改的信息在区块三),那么他就必须这样做,如下,他要在原有的区块链上,计算数学题,重新打包一个不含A给B10个BTC这个消息的包,链接到区块二上,形成B链。但是B链太短,别人不承认怎么办?他必须以一己之力,对抗全世界所有矿机,他要把所有数学题都最先算出来,他才有一直打包权力,直到他把B链长度变得比A链的长,那么他就篡改成功了。
  但是,理论上虽然可以,但现实操作却几乎不可能,全世界的矿机都在链接A链,而你却要链接B链,每十分钟才能出来一个区块,而且必须你一直都能是第一个算出数学题的人,这可能么?基本没可能。区块链的防篡改就是这么来的。在这里插入图片描述

4. 比特币的实现(Transaction-交易)

  因为比特币是去中心化的,所以系统中并没有哪个地方能显式的知道A这个账户上有多少钱。这个需要通过交易记录来推算,系统中有哪些交易是给A转钱,哪些是从A中花钱…
  比特币系统中的全节点,要维护一个叫UTXO的数据结构。

 4.1 UTXO(Unspent Transaction Out)

  比特币中没有余额的概念。只有一个被全节点维护的UTXO数据结构,这里面记录了比特币系统中所有的“未花费的交易输出”。

  比特币系统中的交易,分为输入和输出。例如一笔交易n:A->B转了一笔钱,

  • 输入就是: B的地址,转账的钱数,A的签名等…
  • 输出就是:B获得了A给他的钱。若以后B想要花掉这笔钱,那么这笔钱的来源就是这次交易n(Transaction)。
    而,UTXO记录的就是,那些没有被花费掉的输出。

  Transaction之间的网状关系:一切交易可追溯
  PS:比特币中规定一次交易必须把账户的所有钱一次性花完,除了需要支付的比特币,剩下的作为找零,正常的钱包软件会帮你生成一个找零地址(为了保护隐私),你的剩余钱就在那个找零地址里面了。

考虑如下场景:用户A和用户B之间发生了一个交易T3,A向B转100元。

  • 那这100元,哪来的呢? 来自交易T1:C向A转的80元 +
    交易T2:D向A转的30元(共110元,但A只转了100元给B,10元找零返回给A的账号)。
  • 同理,C向A转的这80元,来自用户E、F的某次交易。
  • D向A转的这30元,来自用户E的某次交易。

举这个例子,是想说明一个问题
  交易与交易之间组成了网状关系,1个交易的输出,成为了下1个交易的输入;下1个交易的输出,又成了下下1个交易的输入。所有的钱,在这个网络中流动,每1笔钱的去向、来源,都是可追溯的,而这也是区块链网络的一个重要特点。

  一个交易,可以有多个输入和多个输出。

 4.2 钱包

深刻理解了UTXO的概念,钱包就很容易理解了:

某个人的钱包的余额 = 属于他的UTXO的总和

在这里,你会发现一个不同于现实世界的“银行”里的一个概念:

在银行里,会存储每个账号剩余多少钱。但这里,我们存储的并不是每个账号的余额,而存的是1笔笔的交易,也就是1笔笔的UTXO,每个账户的余额是通过UTXO计算出来的,而不是直接存储余额!!

5. BTC网络

  区块链是去中心化的网络,利用P2P技术 (peer-to-peer),所有节点具有平等地位,没有所谓的中心节点。在这个P2P网络上的所有节点都直接或者间接地联通起来,某一个节点上发出的信息,最终可以扩散到全球所有节点。任何节点自由的加入和退出。P2P架构,健壮性强,部分节点遭到破坏对整个系统影响很小。P2P网络中的联通不成问题,但是会存在信任问题,这条消息可能历经了多个节点,消息是否被篡改?消息是否真实(确实是发送方发送的)?这些都需要密码学的知识来解决这些问题。

区块链节点之间的通信类型主要分为2种:

① 为了维持节点与区块链网络之间的连接而 进行的通信,通常包括索取其他节点的地址信息和 广播自己的地址信息(地址信息是指 TCP/IP中的 IP地址和端口号).节点新加入区块链网络时,首先 读取硬编码在客户端程序中的种子地址并向这些种 子节点索取其邻居节点地址,然后通过这些地址继 续搜索更多的地址信息并建立连接,直到节点的邻 居节点的数量达到稳定值.此后,节点会定期通过
ping等方式验证邻居节点的可达性,并使用新的节 点替代不可达节点.此外,为了保证新节点的信息被更多节点接收,节点将定期向自己的邻居节点广播 自己的地址信息。

② 为了完成上层业务而进行的通信,通常包括 转发交易信息和同步区块信息(交易和区块是区块 链中的数据结构,将在交易层介绍).节点转发交易 信息时采用中继转发的模式.始发节点首先将交易 转发给邻居节点,邻居节点收到交易后再转发给自 己的邻居节点,以此类推,逐渐传遍整个网络.同步 区块信息采用请求响应的模式.节点首先向邻居节 点发送自己的区块高度(类似于ID),如果小于邻居 节点的高度则索取自己欠缺的区块,如果大于邻居 节点的高度则邻居节点将反向索取区块信息.所有 节点都不断地和邻居节点交换区块信息,从而保证 整个网络中所有节点的区块信息保持同步。

6. 比特币脚本

  比特币脚本是一种基于栈的脚本语言,也被称为堆栈语言。不是图灵完备的,在比特币没有账户的概念,谁拥有这笔交易的输出谁就可以花费这笔交易中的比特币,为了证明拥有这笔交易的输出就需要提供密钥接受验证,验证通过就可以花费这笔交易的输出。

一个交易实例如下:
在这里插入图片描述

  • Input Scripts——输入脚本,分别对应上边的最左边两个
  • Output Scripts——输出脚本,分别对应上边的最右边两个输出

交易结构:
在这里插入图片描述

  • txid:交易的id号
  • hash:交易的哈希值
  • version:使用的比特币协议版本号
  • size:这个交易的大小
  • locktime:用来设定交易的生效时间,一般为0
  • vin:输入脚本(后面会详细讲)
  • vout:输出脚本
  • blockhash:所在区块的哈希值,可以看到是以一长串的0开头,这就与前面提到的挖矿的难度要求相对应。
  • confirmations:这个交易已经有多少个确认。一般6个确认后,交易就可以说是不可改变的了。
  • time:交易产生的时间(表示从很早的一个时间点到现在过了多久)
  • blocktime:区块产生的时间

交易结构是一个数组:每个交易的输入,都要说明输入花的这个币的来源,是来自之前的哪个交易的输出。

在这里插入图片描述

  • txid:输入要花的这个币的,是来自于id=txid的那个交易的输出
  • vout:表示是那个交易的第几个输出
  • scriptsig:输入脚本

如果有多个输入的话,就要说明每个输入的来源,并且要签名。也就是说,比特币中的一个交易可能要多个签名。

在这里插入图片描述

  • value:转账的金额,单位是比特币(BTC)
  • n:表示序号,是这个交易的第几个输出
  • scriptpubkey:输出脚本

如下图
  前面一个区块中,交易是:A把比特币转给了B。后面的那个交易指:B把比特币有转给了C。B->C比特币这个交易中,币的来源是前面A->B这个交易。要验证这个交易的合法性,就要B->C这个交易的输入脚本,和A->B这个交易的输出脚本,拼接在一起执行。早期是拼接在一起执行,后来为了安全起见,就先运行B->C这个交易的输入脚本,运行成功后,再运行A->B这个交易的输出脚本。两个脚本都运行成功(栈顶的结果为非零值),表示这个交易是合法的。
  如果有多个输入的话,每个输入脚本都要和前一个交易的输出脚本相运行,来检查这个交易是否合法。全都验证通过,这个交易才是合法的。
在这里插入图片描述
输入/输出脚本的几种形式:

 6.1 P2PK(Pay to Public Key)

——这种形势是最简单的

input script:
    OP_PUSHDATA(Sig)
output script:
    OP_PUSHDATA(PubKey)
    OP_CHECKSIG

输出脚本中直接给出收款人的公钥。输入脚本中直接给出签名就行。CHECKSIG是检查签名的操作。

脚本的执行:
PUSHDATA(Sig)——把输入脚本的签名压入栈
PUSHDATA(PubKey)——把输出提供的公钥压入栈
CHECKSIG——讲栈内两个元素弹出,用公钥检验签名是否正确。若正确返回True

  6.2 P2PKH(Pay to Public Key Hash)

——这种形式是最常见的

input script:
    OP_PUSHDATA(Sig)  //压入签名
    OP_PUSHDATA(PublicKey)  //压入公钥
output script:
    OP_DUP  //复制栈顶元素,再压入栈
    OP_HASH160  //弹出栈顶元素,取哈希在压入栈
    OP_PUSHDATA(PubKeyHash)  //压入输出脚本提供的公钥哈希
    OP_EQUALVERIFY   //弹出栈顶元素,比较是否相等
    OP_CHECKSIG   //公钥检查签名是否正确

输出脚本里没有直接给出收款人的公钥,而是给出了收款人公钥的哈希值,公钥是在输入脚本里面给出的。

脚本的执行:
PUSHDATA(Sig)——将签名压入栈
PUSHDATA(PubKey)——将收款人公钥压入栈
DUP——把栈顶元素复制一遍
HASH160——将栈顶元素弹出,再将弹出的元素取哈希,再把得到的哈希值再压入栈。所以栈顶变成了公钥的哈希值。
PUSHDATA(PubKeyHash)——把输出脚本里提供的公钥的哈希值压入栈
EQUALVERIFY——若栈顶两个公钥哈希值相同,则弹出
CHECKSIG——用公钥检验签名,若正确返回True

 6.3 P2SH(Pay to Script Hash)

——这种是最复杂的一种

  这种形式的输出脚本是收款人提供脚本(redeemScript)的哈希,到时候收款人要花费这笔交易的时候需要输入脚本的内容和签名,验证的时候分两步;

  • 验证输入脚本的哈希是否与输出脚本中的哈希值匹配
  • 反序列化并执行redeemScript,验证input script给出的签名是否正确
input script:
    ...
    OP_PUSHDATA(Sig)          
    ...
    OP_PUSHDATA(serialized redeemScript)  
output script:
    OP_HASH160                   
    OP_PUSHDATA(redeemScriptHash)  
    OP_EQUAL

Pay to Script Hash在比特币最初版本是没有的,后期软分叉加入,最重要的一点是对多重签名的支持。

多重签名中,只要提超过供指定数量即可,容忍了一定程度的私钥丢失。
原来的多重签名需要外部用户提供一一对应的公钥,一共有多少公钥,几个公钥通过验证就可以完成交易,对用户比较繁琐,现在使用Pay to Script Hash将整个过程打包到脚本中,对外部用户来说降低了多重签名的复杂性,将复杂性转移到了系统中。

 6.4 Proof of Burn

output script
    RETURN
    .
    .
    .
    .[zero or more ops or text]

这种形式的output被称为Provably Unspendable/Prunable Outputs

脚本说明:加入有一个交易的input指向这个output,不论input里的input script如何设计,执行到RETURN命令之后都会直接返回false,不会执行RETURN后面的其他指令。所以这个output永远无法被花出去。其对应的UTXO也就可以被剪枝了,无需保存。

  这种类型的脚本,他的输出脚本中第一个就是RETURN——无条件返回错误。也就是说,包含这个操作的脚本永远不可能被验证通过,RETURN后面跟的内容根本没法执行。这样的输出,意思就是这个交易的钱永远花不出去。
设计这个脚本的目的:

  • 比特币的销毁(带有一定目的的)
  • 往区块链里面写入一些内容,区块链是一个不可被篡改的账本,有人利用这个特性,在区块链中保存一个需要永久保存的内容。就是把你要保存的内容放在RETURN后面。有的交易只有少量输入,输出比特币是0。说明输入的比特币都用来给矿工奖励费了。

PS:前面讲到的,铸币交易里面会有一个coinbase域,coinbase域里面也可存储任何东西,而不会对系统造成任何影响。但是只有获得打包权力的矿工有权利来使用相应区块里面的coinbase域。而Proof of Burn是所有节点都有权。

7. 比特币的分叉(fork)

  • state fork,forking attck(分叉攻击),人为造成的,deliberate fork
  • protocol fork,比特币协议发生了改变,需要对软件升级,在一个去中心化的环境中,没有办法保证所有的节点都能升级软件。根据修改协议内容的不同,又可以分为硬分叉、软分叉。

 7.1 硬分叉(hard fork)

  如果对比特币协议增加一些新特性,例如对区块大小限制(block size limit)进行修改,之前规定block size limit是1MB,大概每个区块有4000个交易左右,平均十分钟出一个区块,这样算下来,每秒钟大概7比交易,延迟非常高。像我们平常使用的信用卡,每秒钟处理的交易,和比特币每秒处理的交易,完全不在一个数量级上。所以,有些人觉得,区块太小,限制了比特币的交易,并且增加了交易延迟。假设发布软件更新,将1MB更新为4MB。假设大多数节点已经更新,只有少部分没更新。就会发生如下的情况:
在这里插入图片描述
上边的链,是已经更新过协议的新节点挖出的区块,区块大小限制≤4MB。下边是还未更新的旧节点挖出的区块,区块大小限制≤1MB。
  旧节点认上边的链是非法链,因为超过了block size限制,所以旧节点只挖下面的那条链。
  因为大部份节点都已更新,新节点挖出的区块一定比就旧节点挖出的多。所以上边的链比下边的链要长,新节点根据最长链原则,只在上边那条链上挖区块。
  所以最后的结果就是,只要旧节点不更新协议,这个分叉就会一直存在,是永久性的。这就是所谓的硬分叉(hard fork)。

  比特币社区中有人比较保守,对于有些软件更新,不支持。并且block size limit也不是越大越好,比特币采用的P2P网络的广播,区块越大,对网络带宽消耗是非常大的,带宽是瓶颈。

 7.2 软分叉(soft fork)

  对比特币协议加一些限制,原来一些合法的交易,在新区块中可能变的不合法了。
  假设发布软件更新,将1MB更新为0.5MB。假设大多数节点已经更新,只有少部分没更新。就会发生如下的情况:
在这里插入图片描述
上边的链,是已经更新过协议的新节点挖出的区块,区块大小限制≤0.5MB。下边是还未更新的旧节点挖出的区块,区块大小限制≤1MB。
  旧节点认上边的链是合法链,因为≤1MB。旧节点认为下面的链也是合法链,因为≤1MB。但根据最长链原则,旧节点认为上边的链式合法链,下面的是非法链。会转而去挖上边的链。
  新节点认为下面的链是非法链,因为存在>=0.5MB的区块。所以会挖上边的链。
  所以最后的结果就是,新旧节点都认为上边的链是合法链,但是,当旧节点将1MB的区块链接到上边的链上后,新节点又不认同,又会出现分叉…
  这个分叉是临时性的(这也是为什么被称为软分叉的原因)。旧节点如果不更新协议,就会发生挖矿失效的情况,就是说,旧节点辛苦挖到的比特币,不被认同,并且自己也不认同自己挖到的矿(比较委屈)。

  实际当中,可能出现软分叉的情况:给某些协议中没有规定的域,增加一些新的含,赋予她们一些新的规则。例如:铸币交易中的coinbase域,挖矿的时候,需要修改block header中的nonce来找到合适的结果,但是nonce只有四个字节,很有可能出现所有情况都试完还没有发现正确结果。这时候就要调整coinbase域,例如将coinbase域中前8个字节也用来作为nonce。有人提出将coinbase后面的部分存储UTXO的根哈希值。这样,新节点发布的区块,旧节点是认的(因为coinbase域里面本来是可以存储任何东西)。旧节点发布的区块,新节点有可能不认同(coinbase域里面放的不是一个根哈希值)——这就是软分叉。比特币中比较著名的软分叉是:P2SH:Pay to Script Hash。


8.区块链总结

  关于区块链——比特币的知识就说到这。下面我会就区块链的特性,以及其他的一些应用来扩展讲解区块链。区块链近几年很火,但是还是要有一颗清醒的心,要真正理解一项技术,再去谈是否要把它作为方向,不能随波逐流。

发布了36 篇原创文章 · 获赞 65 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43275558/article/details/105163000