有人说,区块链是最慢的数据库,还有人说是新型商业模式,但是并不是这样的,区块链是一个去中心化的账本,比特币采用的是基于交易的账本模式。
Ps:比特币不等于区块链,比特币只是区块链的一种加密货币。
比特币的价格走向图:
描述:2017年是加密货币价格大爆发的一年,从2017年1月开始暴涨,不要觉得现在去了解区块链算晚了,现在还是区块链发展的早期阶段。
各种加密货币的市场占比:
描述:一开始一直是比特币一家独大,从2015年某一天出现以太坊后,各种加密货币都能在市场上占据一定定位,本文也主要说明比特币和以太坊两种加密货币。
课程大纲:
比特币主要使用了密码学的两个东西:哈希、签名。
抗碰撞性:
将不同的输入进行哈希后,可能得到相同的结果,这就叫哈希碰撞。
X ≠ Y,H(X) = H(Y)
产生的原因是,输入空间大于输出空间,输入空间是无限的,输出空间为 2256。
在哈希碰撞的情况下,我们发现人为去找两个哈希值相同但是信息不同的数据非常难,所以引出一个概念:抗碰撞性,也就是说,我们可以根据信息的hash值来判断信息是否被篡改过。
PS:常用Hash算法MD5已经被破解了,可以人为制造哈希碰撞。
Hiding:
我们将X进行hash后得到H(X),这个时候想以H(X)反推原数据X目前是无法做到的,这就叫hiding。
X --> H(X),H(X) -//->X
Puzzle friendly:
如果想要得到一个固定类型的Hash值,并且从H(X)推算出X大概落在哪个范围,是不现实的,比如想要得到一个前K位为0,后256-K位为字符串的H(X),去估算X大概的范围,是无法做到的,这就保证了我想得到一个比特币,一定是要付出很大工作量去找满足要求的nonce。
比特币用的Hash函数是:SHA-256(Secure Hash Algorithm)
签名要提到一个概念:非对称加密,用户创建账户后,会获得属于他自己的公钥和私钥,并且公钥和私钥是不一样的,公钥是对外公开的,私钥只有用户自己知道,用户在发起交易后,首先会对交易进行hash,hash后使用用户的私钥进行签名,其他用户可以使用发起交易用户的公钥对这笔交易进行验证签名,这样就可以保证交易不会被伪造,。每个用户的公钥私钥都不相同,这个是通过使用非常好的随机源保证的,如果出现不同用户拥有相同的公钥私钥,那么就全完了。
区块链使用的是Hash指针,Hash指针不仅保存了数据的地址,还保存了数据的Hash值,保证数据不被篡改。
区块链结构图:
每一个区块含有:自己的数据、上一个区块的Hash指针,Hash指针的Hash值会根据区块的整个内容来进行Hash。这么设计的好处在于可以保护数据不被篡改。当途中红色区块的数据被篡改后,指向它的Hash指针的Hash值就会改变,蝴蝶效应,会导致再下一个区块的Hash值改变…最后导致后面所有的区块的Hash值都会变。
有两种Hash指针:
merkle tree的结构:
描述:最下面的是一个个交易,将交易两两组合进行Hash,如果交易数为奇数,会复制一个交易凑成偶数,通过一层层Hash,最后得到一个根Hash值,通过判断根Hash值,就可以保证整个merkle tree的交易不被篡改。
区块包括区块头和区块体:
block header
包括:
block body
包括:
merkle tree 将区块头和区块体连接起来,交易放在区块体,根Hash值放在区块头。
比特币的节点分为两类:
block header
;block header
,block body
。如果轻节点要想要知道特定交易是否已经进入区块链应该怎么做呢?
Ps:使用Hash指针的时候,必须是无环的,如果有环,那么环内每个区块的Hash指针会出现循环依赖的情况,每个区块的Hash指针的Hash值都无法求出来。
共识协议:
由于每个用户都有机会获得记账的权利,往区块链中加入新的区块,那么出现了以下几个问题,解决这些问题的方法也就是共识协议:
为什么用户会有记账的欲望?
因为用户将满足标准的新区块加入区块链后,会获得奖励,在比特币数量区间为[0,21W]时,一个新区块奖励50比特币,比特币数量区间为[21W,42W]时,一个新区块奖励25比特币,如此往复;用户还会从区块中的交易中抽取一部分当做手续费,这样可以保证用户不会仅仅只将自己的交易打包进区块。
其他用户怎么分辨,新加入的区块的交易有没有被篡改?
交易流程图:
交易大致流程:A获得了10个比特币,在一次交易中,A需要给B和C各转账5个比特币,首先,A通过解析B和C的公钥拿到B和C的收款地址,A在交易里写明对B和C个转5个比特币,接着A使用A的私钥对这笔交易进行了签名且将A的10个币的来源信息进行Hash存进交易里。对于B和C来说,通过使用A的公钥进行解密,可以知道这笔钱是A转过来的;对于BC和其他所有用户,通过验证A的10比特币来源信息的Hash值,从区块链中A获得币的区块开始一步步往下验证交易信息,就可以知道A是不是真正有10个比特币,再通过使用A的公钥进行解密去验证交易的签名,就可以知道这笔交易确实是A发起的。
交易时时刻刻都在发生,只有当交易被打包成区块,收入区块链后,交易才算生效。
新的交易已经可以保证真实可靠了,那么如果有人想篡改区块链中原有的交易会出现什么问题呢?见2.2.1↑。
在短时间内,如果同时出现两个用户都添加了新的区块,我们应该选哪一个作为正确的区块?
在用户不断的记账时,可能发生以下情况:
图中一个方块代表一个区块,区块上面的A->B表示的是区块的交易中有一则A转账给B的交易,以此来表示区块的不同。我们可以看到,区块链发生了分叉,在一条链中,新的区块为A转账给B,在另一条链中,新的区块为A转账给A‘,我们应该将哪一个区块作为新的区块呢?为了保证区块链的唯一性,采用最长区块链原则,哪一条区块链最长,就使用哪一条作为区块链,旧的分叉的链就直接抛弃,旧链的奖励也会回滚,所以A转账给B的区块所在的链最长,我们将它记账入区块链。
记账成功的概率和什么因素有关?
非常明显,受算力影响,因为挖矿的时候,要不断的计算出符合标准的nonce
随机字符串,要满足整个区块头的Hash值小于target,所以只要你算的快、运气好,就能比别人先一步添加新的区块。
UTXO:Unspent Transaction Output,是所有没有被花掉的交易的输出组成的集合。
描述:在一个交易中,A转账给B 5个比特币、C 3个比特币,如果B和C都没有将获得的比特币花掉,那么都会进入UTXO集合,但是B花掉了,C没花掉,所以B的记录会从集合中删除,C的记录会保存在集合中。集合中会记录这个交易的Hash值,并且记下C留有3个比特币。
记录这个UTXO集合的目的是,防止二次转账,比如B将获得的5个比特币转给了D,那么B持有5个比特币的记录就会从UTXO集合删掉,这个时候B再想转给E比特币的时候,会因为UTXO集合里面没有B持有比特币的记录而导致交易失败,就防止了二次转账。
由于现在挖矿的人很多,难度被调得非常高,所以可能会导致nonce就算遍历完了也没办法找到匹配的,这个时候我们还可以通过改变merkle tree里面的铸币交易中的一个域CoinBase
去进行调整,Coinbase
可以写入任何的内容,这个域里面的内容是没有啥影响的,但是可以增加挖矿的空间范围。
比特币系统设定,每隔10分钟出一个新的区块,每次去计算nonce时,都可以看做是一次伯努利实验(Bernoulli trial),意思就是概率和之前的实验是无关的,就相当于抛硬币,每次正面的概率都是50%。这样设定的目的是为了限制算力高的用户挖到矿的概率,不会太高,使挖到矿的概率绝大部分受算力的影响,极少部分受运气的影响。如果不这样设定,让实验次数影响概率的话,算力高的用户挖到矿的概率会随着时间的进行越来越大,就违背了比特币系统的约束。
selfish mining 的意思是指,用户挖到矿以后,不发布,而是接着继续挖矿,最后挖出来的链比现在区块链要长的时候,一次性发布出来,替代区块链中现有的链。这种攻击方式显然是不显示的,因为需要非常高的算力,当一个用户的算力比比特币系统总算力的50%要多的时候,用户可以随意篡改区块链,那么这么牛逼的用户可以利用这个机制干什么呢?在目前现有的链里面M向A付款5个比特币,并且由于写入了区块链,M在现实生活中拿到了A付出的3W美金,但是M呢又自己挖了一个区块,自己挖的区块里面是没有向A付款的,接着M利用自己的算力不断挖矿,挖到比现有的区块链长的时候,M发布所有区块,这个时候区块链回滚,M付给A的5个比特币又回到了M手上,但是M玩起了消失,不将5W美金还给A。
这么大的算力是不现实的,如果出现了这么大的算力,区块链就木得了,对于M不还钱的线下行为,线上是没办法管理的,但是线上能做一些交易确认的处理。对于一些别的情况,比如在区块链分叉的刚开始,就分了两个区块,不知道选哪一个作为真正的区块的时候,区块链有确认机制保证交易安全–confirmation,当新的区块进入区块链后,会记录confirmation的值,一个新区块confirmation值就加一,直到在这个区块后增加了6个新的区块,confirmation = 6,才能保证这个区块是安全的、不可篡改的。
比特币系统的结构:
比特币的设计原则是:简单、鲁棒、非高效。
交易在比特币网络如何发布?
比特币网络中每个节点都要维护一个等待上链的交易的集合,第一次听到一个合法交易的时候,将这个交易加入等待集合,再将交易随机转发给其他邻近节点,以后再收到同一个交易的时候,就不会再转发了。有可能会出现恶意节点发布双重消费的交易,就是A转账给B,A又将同一笔比特币转账给C,每个节点由于网络的位置不同,可能会收到不同的交易记录,比如D收到了A转账给B的交易,E收到了A转账给C的交易。由于交易还没有被打包上链,那么对于D来说,先收到A转账给B,是合法的,再收到A转账给C就知道是不合法的,就不将A->C的交易记录入D的交易等待集合了;对于E来说,先收到A->C的交易,再收到A->B的交易就认为是不合法的了。如果这个时候新发布了一个区块,区块中记录了A->B的交易,那么D就会将A->B的交易删除,因为已经入链不需要再保存,E会将A->C的交易删除,因为不合法。
区块在比特币网络如何发布?
和交易的类似,每个节点都要确定区块内的交易是不是合法,并且要判断区块是不是在最长合法链上,越是大的区块在网络上就传输得越慢,所以比特币网络对区块大小做了限制:1M。
比特币网络中的节点分类:
挖矿就是不断调整block header里面的nonce值,使整个block header的hash值小于等于目标阈值,比特币使用的hash算法是 SHA-256
,产生的hash值是256位,所以整个输出空间是2256。
H(block header) ≤ target
调整挖矿难度就是调整目标空间在整个输出空间所占比例。
为什么我们要调整挖矿难度?
比特币协议中规定,每个2016个区块,就会调整一下目标阈值,大概是每两个星期调一次,难度调节公式: t a r g e t = t a r g e t × a c t u a l t i m e e x p e c t e d t i m e target\ =\ target\ \times\ \frac{actual\ time}{expected\ time} target = target × expected timeactual time ,注意分母的expectedtime
是2016*10分钟,actualtime
是产生2016个区块的时间,target越大,出块越容易,target被设定一次最多增加或减少4倍。
注意:目标阈值不是挖矿难度,目标阈值只是反映了挖矿难度,目标阈值和挖矿难度成反比,挖矿难度的计算公式: n e x t _ d i f f i c u l t y = p r e v i o u s _ d i f f i c u l t y × 2 w e e k s t i m e t o m i n e l a s t 2016 b l o c k s next\_difficulty\ =\ previous\_difficulty\ \times\ \frac{2\ weeks}{time\ to\ mine\ last\ 2016\ blocks} next_difficulty = previous_difficulty × time to mine last 2016 blocks2 weeks,实际比特币代码里算的是目标阈值。
挖矿设备的演化趋势是越来越专业化,从每个人的电脑都使用的CPU挖矿,到后来更适合的GPU挖矿,再到出来一款只能挖矿、专业挖矿的芯片ASIC。为啥要从CPU换成GPU呢,因为使用CPU挖矿,我们电脑上有很多其他资源比如硬盘、一些其他的部件是闲置的,所以划不来,GPU适合通用并行计算,里面有很多负责运算的部件,所以效率比CPU挖矿高很多;从GPU到ASIC是因为,GPU挖矿时,还是有很多部件用不上,比如负责浮点数计算的部件,很适合深度学习计算,但是挖矿用不上;ASIC的产生就是专门为了挖比特币矿的。
随着挖矿的竞争越来越激烈,ASIC芯片也会不断的更新,会出现更好的矿机打败它,所以购买ASIC矿机的时间也很重要,算力是不断变化的,而且一些提供新型强力矿机的厂商做出来后不会马上交货给矿工,会自己先用着挖矿,等挖了2个月了,再给矿工,导致矿工挖矿的风险很大。
由于算力很容易掌握在一少部分人手中,所以单干的矿工获得奖励的几率就更低,风险就更大,大型矿池就成为了趋势。
矿池就是将一些分散的算力集合起来,为矿主打工,在挖出框后,根据每个矿工的工作量去分配获得的比特币,确定矿工工作量是根据每个矿工提供的接近target的nonce数量确定的,矿主会个每一位矿工分配一个范围的nonce区间,并且在merkle tree的coinBase域的收款参数会填上矿主的收款地址,这使得矿工不可以挖到矿后自己找别的全节点发布,因为收款地址对不上就取不出比特币来。
矿池的优点:减少了矿工的经济风险,以前没有矿池的时候,矿工可能2年才能挖出一个区块,也可能2年都挖不到,有了矿池后,矿工可能每天都能得到分红。
矿池的缺点:增加了51%攻击的风险,因为每个矿工都是接受了矿主分配的任务,并且一个机构可能拥有很多个矿池,这使得有一些矿工可能在不知情的情况下去帮矿主干坏事。
比特币有自己的脚本语言,主要是为了验证交易的真实性,但是也有一些用户用脚本语言去记录一些知识产权等等,想把这些记录加入区块链中。交易的脚本分为两类:input scripts和output scripts,每个交易都有这两个脚本,交易的输入脚本要和上一笔交易的输出脚本匹配,说明交易真实有效,如果不匹配,交易非法无效。
比特币的脚本语言是基于栈的,也就意味着,脚本中最上面的程序,最晚被执行。
脚本肯定在交易里,那么我们从交易的结构入手,慢慢往脚本进行推导。
交易结构:
交易结构中vin的结构:
交易结构中vout的结构:
所以交易的输入和输出脚本就在交易结构里的vin和vout中。
使用下面的例子,来对P2PK的脚本进行讲解。
当前交易的输入脚本:
上一笔交易的输出脚本:
脚本执行:
实例:
使用下面的例子,来对P2PKH的脚本进行讲解,这个脚本与上面P2PK的脚本相比,在输出脚本里,没有直接给出上一笔交易的收款人的公钥,而是给的公钥的Hash值。
当前交易的输入脚本:
上一笔交易的输出脚本:
脚本执行:
实例:
P2SH主要解决了,当验证交易需要多重签名的时候,付款人在写入output script时由于收款人提供的公钥过多,导致付款人写的很累的问题。
以前没有用到P2SH的多重签名例子:
说明:我们发现在input script的第一行有一个false,是历史遗留问题,因为在运行最下面的CHECKMULTISIG的时候,会多出一个对象,所以才多加一个false去抵消。
脚本执行:
我们确实能发现,上一个交易的output script里面东西很多,上一笔交易的付款人要将收款人如此多的公钥装进output script里,不同的收款人他们要求的公钥数量也不一样,这就导致每次付款人付款的时候,要根据不同的收款人提供的公钥去写output script,非常麻烦。为了减少付款人的压力,P2SH将这些收款人提供的公钥封装在另外一个脚本(redeemScript)里,并且付款人只要将这个脚本的Hash值放入output script里面就可以了。
P2SH多重签名的例子:
所以在验证的时候,P2SH需要验证两步:
步骤一脚本执行:
上面的脚本执行完,在堆栈中还剩下:
所以我们还需要执行步骤二的脚本:
P2SH的实例:
这是一种特殊的脚本形式,有人在交易的output script里面开头写上一句return,这会导致下一笔交易在验证的时候,拼接input script和out script执行后,碰到output script里面的return就直接返回了,使得验证不通过,这会导致output script里面有return的那些交易中输出的比特币永远无法被花出去,其对应的UTXO里面的数据也可以被删除了。
使用场景:
那么为啥不用coinbase去做一样的处理呢?是因为coinbase域所在的交易只有获得记账权的全节点才有资格去写,而使用Proof of Burn就可以让随便一个用户完成操作。
分叉就是指区块链变成了多条链,分叉有下面几种类型:
state fork,自然挖矿产生的分叉;
deliberate fork,人为恶意分叉;
protocol fork,比特币协议发生修改时,由于有一些旧节点更新慢或者不愿意接受更新而造成的分叉,根据协议的不同分为硬分叉(hard fork)和软分叉(soft fork);
硬分叉软分叉都是由于比特币协议发生了变化导致的。比特币协议改变后,旧节点不认可协议更新后产生的区块,会因为旧节点不更新就永远存在分叉,那么就是硬分叉;如果旧节点认可协议更新后的新区快,新节点只认符合新协议的区块(因为旧节点挖出来的区块不符合新协议),因为大部分算力掌握在新节点手中,旧节点挖出来的区块不会被认可所以会经常被回滚,导致分叉临时存在,那就是软分叉。
以太坊的内容暂时没看,等需要用到的时候再了解~
北京大学肖臻老师《区块链技术与应用》公开课