BTC-挖矿难度
H(block header) leq target ,target越小,挖矿难度越大,挖矿难度跟目标阈值成反比。调整挖矿难度,就是调整整个目标空间在整个输出空间所占的比例。哈希函数 SHA-256 ,整个输出空间大小为 2^{256} 。
difficulty_1_target 表示挖矿难度为1时所对应的目标阈值。挖矿难度最小为1,它对应的目标阈值是一个很大的值。
如果不调整挖矿难度的话,随着算力增强,出块时间越来越短。这样的话,很容易出现分叉。
比如出块时间是1s,那么很容易出现一个十分叉,然后假如有一个恶意节点想回滚之前的交易 A rightarrow B ,按照之前的想法,恶意节点至少要拥有过半的算力,但是现在区块链中有一个十分叉,它将系统中的算力给分散了,而恶意节点可以集中算力扩展 A rightarrow A' 的链,很容易让其成为最长合法链,因为诚实节点的算力被分散了。这种情况下不再需要51%的算力,可能10几%就够了。所以出块时间不是越短越好。
以太坊出块时间15s,设计出新的协议 ghost,分叉产生的 orphan block 不能够简单的丢弃,给一些简单的奖励 uncle reward。
怎么调整挖矿难度?比特币每2016个区块调整一次难度,需要两个星期。
公式。 time小,target变小,难度上升。目标阈值增大最多四倍。也就是如果actual time超过8周,只按8周计算。最小也是四分之一,不到半个星期也按半个星期算。
挖矿难度调整写在区块链系统代码中,每过2016个区块调整一次难度,恶意节点不调怎么办,mBits区域不可以通过。诚实的节点不认,合法性通不过。
比特币中算力增长趋势。
挖矿难度的增长曲线。
最近半年难度调整曲线。难度增长,反映出大家对比特币热情越来越高。
出块时间。10分钟左右。
最近半年的出块时间。10分钟左右。
挖矿难度的公式。和阈值调整相反。
BTC-挖矿
全节点的职责。缺省情况,沿着最长合法链挖下去,当出现等长的分叉,选择最先听到的那个分叉。
轻节点的职责。
正在挖矿,突然系统中有人挖到了节点,重新组装设备,挖下一个。并不可惜,因为 memoryless or progress free。
比特币是怎么保证安全性的?两方面,一方面是密码学上的保证,别人没有你的私钥,不能伪装你的签名,所以不能转走你账上的比特币。另一方面是共识机制,系统中大部分的矿工是诚实的。
挖矿设备。
第一代,CPU。资源闲置,性价比不高。
第二代,GPU。通用并行运算。但仍然有浪费,如浮点数计算。
Mining puzzle ASIC芯片,application specific integrated circuit,专门为了挖矿设计的芯片。一种芯片只能为一种加密货币挖矿。除了merge mining的情况。
Alternative mining puzzle ,抗ASIC芯片化(ASIC resistance),普通人也能挖矿。
矿池(pool)。
矿主(Pool manager)下面有很多矿工(miner)。 矿工计算哈希值。矿主监听网上的交易打包成候选区块,看看有没有其他节点抢先发布区块。一起分红。
如何分红?之前挖矿难度太难了,矿工要挖一两年才能挖到一个区块,收入不稳定,于是降低挖矿难度。矿工拿到share(almost valid block),提交给矿主。share是工作量证明。按提交的share数目分配利益。矿工任务由矿主分布。矿工不能自己发发布区块。CoinBase里面的收款地址是矿主的。有一些搞破坏的矿工,挖出来不提交,扔掉,别的矿池派来的间谍故意去搞别人的池子。
中国矿池算力占全球81%。
曾经有一个矿池(Ghash)算力达到51%,引起大家恐慌,然后主动减少算力,保护大家对比特币的信心。现在不再运营了。
矿主按比例抽取管理费。矿主降低管理费,吸引越来越多的矿工。就可以发动攻击。矿池加大了51%攻击的风险
分叉攻击。
先等六个区块让B放心,再让交易滚回去。看着追赶很漫长,但是因为矿主掌握了51%的算力,下面的链的平均增长速度比上面快。A rightarrow B 交易会被回滚。
注意:不到51%也是可以发动攻击,这只是个概率问题。并且矿池的算力也只是估计而已。
Boycott攻击。
攻击者不喜欢A账户,想封锁A账户,所有和A有关的交易都不让上链。 没必要等六个区块。马上分叉。
没有人家的签名,所以盗币是不可能的。
矿池实际上是On demand mining。自己不需要维护很大的挖矿集群,但是需要的时候,可以召唤。
BTC-脚本
比特币一个交易实例。包含一个输入两个输出。收到23个确认,回滚概率很低。下面是输入脚本和输出脚本。
输入脚本包含两个操作,分别把两个很长的数压入栈里,比特币所使用的脚本语言非常简单,唯一可以使用的内存空间就是一个堆栈,基于栈的语言(stack-based language)。
输出脚本有两行,分别对应上面的两个输出。每个输出有自己的单独的一个脚本。
首先看交易的一些宏观信息。
locktime:用来设定交易的生效时间。0表示立即生效,如果非0值,过一段时间才能生效。
vin,vout:输入输出部分,后面详细讲解。
blockhash:交易所在的区块的哈希值。
time:交易产生的时间。
blocktime:区块产生的时间。(表示从某个很早的时间点过了多少秒)。
txid:之前交易的哈希值。
vout:这个交易的第几个输出。
这两个给出了比特币的来源。这项交易所用的比特币来自 "c0cb...c57b" 交易的第0个输出。
输入脚本(scriptSig),最简单的形式给出一个签名(signature),证明我有权力花这个钱。后面的scriptSig用 input script代替。
如果一个交易有多个输入的话,每一个输入都要给一个来源和一个签名。比特币中的交易可能有多个签名。
value:输出的金额,单位是比特币。
n:序号,表示是这个交易的第几个输出。
输出脚本(scriptPubKey),最简单的形式给出一个公钥(public key)。
asm:显示输出脚本的内容,里面包含一系列的操作,后面详细解释。
reqSigs:需要多少个签名。
type:输出的类型。
addresses:输出的地址。
意思是想给谁钱,地址是那个人公钥的哈希。
首先执行输入脚本,再执行输出脚本,如果执行没有出错,最后栈顶的结果为TRUE,验证通过,交易合法。如果交易有多个输入,那么每个输入,都要和其对应的输出进行验证,验证通过,这个交易才是合法的。
第一种形式。P2PK
输出脚本里直接给出收款人的公钥。输入脚本里的签名是用私钥对这个输入脚本所在的整个交易的签名。
注意:现实中,为了安全考虑。是分开执行的。这里为了方便,放在了一起进行考虑。
脚本执行。首先分别将签名和公钥压入栈。
读到CHECKSIG,将栈顶两个元素弹出来,用公钥检测签名是否正确。注意这里交易是 A rightarrow B 的输出脚本和 B rightarrow A 的输入脚本。
实例。
第二种形式。P2PKH
这种形式最常用,这里输出脚本没有直接给出收款人的公钥,给出的是公钥的哈希值,公钥在输入脚本里给出。
脚本执行。
DUP:把栈顶元素复制一遍。
HASH260:把栈顶元素弹出取哈希值,再压入栈。
注意:这里栈顶有两个哈希值。上面这个哈希值是输出脚本里提供的收款人的哈希值。下面的哈希值是输入脚本里的公钥运行HASH160取得的。
加下来EQUALVERIFY弹出栈顶的两个元素,比较是否相等。作用是防止有人冒名顶替。假设相等,就从栈顶消失。接下来检测签名。
实例。
第三种形式。P2SH
最复杂。这里输出脚本给出的不是收款人公钥的哈希,而是收款人提供的一个脚本的哈希。这个脚本叫赎回脚本(redeemscript)。将来要花这笔钱时,输入脚本要给出赎回脚本的具体内容,同时也要给出让赎回脚本正确运行的签名。
进一步说明。
用P2SH实现P2PK。
第一阶段的验证。先验证用户给出的赎回脚本是正确的。
两个RSH相等,从栈顶消失。
第二个阶段的验证。首先将输入脚本中序列化的赎回脚本进行反序列化。然后执行赎回脚本。
然后检测签名。
这么复杂?为了多重签名。
比特币系统中一个输出可能要求多个签名才能把钱取出来。
多重签名。红色的x是因为系统实现的bug,在CHECKMULTISIG中弹出栈时会多弹出一个元素,用红色的x填补一下。
M表示需要M的签名,N表示N个人。N个人中M个人签名即可取钱。
注意:给出的M个签名的相对顺序要和他们在N个公钥中的相对顺序一致才可以。
脚本执行。
FALSE多余的操作。
存在的问题,用户需要知道N和M等等,写在输出脚本里,复杂性交给了用户,并不好。
用P2SH实现的多重签名。
本质把输出脚本里的复杂度转移到赎回脚本中。用户只需要知道赎回脚本的哈希值。
执行脚本。
第一阶段的验证。
第二阶段的验证。
实例。
一个特殊的输出脚本。证明销毁比特币的方法。
1.小币种(Alternative coin),除了比特币以外的其它小的加密币种,都可以叫做Alternative coin。销毁一个比特币,获得小币种,证明你付出了代价。
2.往区块链写入一些内容。比如一个知识产权,取哈希值放在RETURN的后面,永远保存,不会被篡改。任何用户都可以用这种方法销毁很少一点比特币,换取往区块链中写入一些内容的机会。注意,coinbase域写入内容只有拥有记账权的人才可以做到。
发布交易不需要有记账权,发布区块才需要有记账权。
实例。
coinbase交易。
普通交易。
缺少OP,比如 CHECKSIG 实际上是OP_CHECKSIG。
BTC-分叉
临时分叉(State fork)。由于对比特币区块链当前的状态有意见分歧而导致的分叉。分叉攻击(Forking attack),属于临时分叉。也叫做Deliberate fork。
Protocal fork。对比特币协议产生了分叉,用不同版本的比特币协议。包括硬分叉(hard fork)和软分叉(soft fork)。
硬分叉(hard fork)。
如果对比特币协议增加一些新的特性,扩展一些新的功能,这时,那些没有升级的旧节点不认可这些新的属性,认为这些特性是非法的,产生了硬分叉。
举例:Block size limit,1MB=1000000B,每个交易250B,大概是1000000/250=4000个交易。4000/(60*10)=7 tx/sec,每秒钟7笔交易。
有人发布一个软件更新,要求更新协议:1MB rightarrow 4MB。大部分算力更新了软件,少部分没有更新。
分叉是永久性的。只要旧节点不更新软件,分叉就不会消失。
出现硬分叉后,之前旧链中的出块奖励还有没有用?出现硬分叉后,就变成了两条平行运行的链,这两条链彼此之间有各自的加密货币,变成了社区分裂,最后导致分家了。分叉之前的币按道理变成上下两条链都认可,一个币变成了两个币。
ETH已经分叉了,分出来个ETC。https://blog.csdn.net/woshilingdaoren/article/details/81808433
分叉之前有一笔帐 A rightarrow B ,然后B拿到钱后,在分叉后的两条链上都可以花。所以,分叉之后,不采取某些行动,彼此之间会有影响。
举例:两条链之前由一条链分叉而来,之前的私钥账户不变,就是协议不一样,但账户的余额也是不一样的。但是上面有一笔 B rightarrow C ,在下面的链回放。这样C收到了两笔币。
还有,比如,在上面的链,B在C那里购物,后来取消了订单,C将钱转回了B,B在下面的链回放。但是这样有风险,可能C重放b转给C的交易,但是B可能在下面的链没有钱。。。。。。。
解决办法:现在这两条链各带一个chain ID。
软分叉(soft fork)。
如果对比特币协议加一些限制,原来合法的区块在新的协议下不再合法,造成了软分叉。
举例:Block size limit:1MB → 0.5MB。大部分算力更新了软件,少部分没有更新。
是临时性的分叉。旧节点如果不更新软件,经常白挖。所以旧节点放弃了自己的链,跟到了长链。因为新节点不认旧节点,但是旧节点认新节点。系统不会有永久性的软分叉。
实际中会出现软分叉的情况:
给某些目前协议中没有规定的域增加新的含义,例如coinbase域,有一个用途,做为extra nonce提高挖矿难度。
但是coinbase域不止8个字节,有人提议后面的字节用来把UTXO集合的内容组织成merkle tree,算出根哈希值写入coinbase域。
coinbase域的值本身会被算法 block header 中的根哈希值中,这样就可以用 merkle proof 证明出全节点给出的账户余额是否正确。这是软分叉。
软分叉经典例子:P2SH(pay to script hash)。赎回账本(Redeem script),旧节点只做第一阶段验证,新节点两个阶段都验证。
总结。
Soft fork:只要系统中拥有半数以上算力的节点更新了节点,系统就不会出现永久性的分叉。会有临时性的分叉。
Hard fork:必须所有节点都要更新软件,系统才不会出现永久性分叉,小部分节点不愿意更新,系统就会分出两条链。
BTC-问答
- 转账时接收人不在线怎么办?不需要接收者在线,知道他的地址就行,离线没关系。
- 假设某个全节点收到了某个转账交易,有没有可能接收者的收款地址是这个节点从来没有听说过的?账户在创建时不需要通知其他人,只需要在本地创建一个公私钥对就可以了,当收款地址第一次收到钱时,节点才知道这个账户的存在。
- 账户私钥丢失了怎么办?莫办法,变成了死钱。没有人可以重置密码。比特币交易所处于缺乏监管的情况,并不安全。Mt Gox 交易所机构曾被盗并破产了。
- 私钥泄露怎么办?尽快把钱转到其他的安全账户上。公私钥对一旦生成,没有办法更改,所以更改不了私钥。只能再生成一个公私钥对,转账。
- 转账写错地址怎么办?没有办法取消已经发布的交易。转到了一个不存在的地址,地址比如是知识产权的哈希值,这样比特币也成了死钱,这个做法不提倡,会永久保存在UTXO里,对全节点不友好。
- 注意一点:当使用Proof of Burn时,将这个交易写入区块链进行验证时,并不会验证这个输出脚本,只会验证这个交易的输入脚本和来源的输出脚本,所以会被写入区块链。
- 挖矿就是尝试nonce,会不会有的矿工偷答案,偷别人的nonce,怎么知道是哪个矿工最先找到的nonce?发布的区块里有一个coinbase tx是收款人地址,要改成自己的地址,这样coinbase tx发生改变,导致了markle tree 的根哈希改变。nonce和根哈希都在block header里。block header 发生了改变,所以不可能偷答案。
- 怎么知道交易费该给哪个矿工?事先怎么知道哪个矿工挖到矿?事先不需知道哪个矿工会得到这笔交易费。只要total inputs>total outputs,差额就是交易费。哪个矿工挖到了矿,就把这些差额收集起来,作为自己的交易费。