比特币之七交易的验证

南楼画角 提交于 2020-02-11 01:32:16

正在学习区块链,如果我哪里有错误希望大家指出,如果有任何想法也欢迎留言。这些笔记本身是在typora上写的,如果有显示不正确的敬请谅解。笔记本身也是给我自己写的,所以如果有侵权的请通知我,我立即删除。

7.交易的验证

7.1 前戏

下面讲的这些交易的验证其实都是在block body中,就是一次次的交易,之前的那些都是在block header中的数据。

avatar

图片最上面左侧的BTC-Output是指UTXO中的前一个比特币的输出,右侧unspent就是剩下的钱,spent就是花掉的钱,这个交易已经收到了23个交易,这都是之前讲过的。

“下面是这个交易的具体内容”,肖老师是这么说的, 我的理解是这就是merkle tree中的一个节点。一个确定的交易内容。VIN和VOUT是数组,太长了,放在下面单独说。我觉得下面这种格式不太正确,因为对于一次交易来说,vin和vout不是下面这种存放方法的。

avatar

**txid:**交易的id,transaction id

**hash:**交易的哈希值,不是nonce的那个,那是什么呢?不知道

**version:**使用的比特币协议的版本号

**size:**这个交易的大小

**locktime:**交易的生效时间,一般来说都是0,立即生效。有的特殊的交易会用到这个值,比如有的交易会在10个区块后生效

**block hash:**这个区块所在的哈希值,这个肯定是需要nonce的那个了。

**confirmations:**交易的确认信息,比如这里是23个区块的确认。

**time:**交易产生的时间。time和block time都和程序中的时间是一样的,都是一个绝对时间过了多少秒。

**block time:**区块产生的时间。

![avatar][pic7.1-3]

**txid:**每个交易都要说明币的来源,有的币可能有几个来源,因为一个交易的钱不足嘛,这里只有一个,数组中只有一个元素。

**vout:**表示是上面txid那个交易的第几个输出

**scriptSig:**验证脚本,(没懂)这个值后面用input script代替,别不认识了。解锁脚本。

![avatar][pic7.1-4]

这个例子的输出有两个

**value:**转出的金额,上面的单位是(/个比特币),有地方的单位是聪(Satoshi),这个单位已经很小了,0.22684000个比特币就是22684000Satoshi。应该没有比1聪再小的了吧。

**n:**第几个输出

**scriptPubkey:**输出脚本,后面用output script代替。锁定脚本

交易的验证是通过成功执行脚本实现的。这个脚本并不是说要用pthon还是shell,而是一种DSL语言(domain-specific language,领域特定语言)。这种脚本很简单,下面会有说。

脚本分为了两种,输出脚本(锁定脚本)和输入脚本(解锁脚本)。这两个名字都比较形象,输出脚本是指有人给你转钱时提供的vout带的,用于以后花这部分钱时验证用。因此也可以成为这部分钱被”锁定了。在使用这部分钱的时候按照锁定脚本的上锁方式,使用自己的私钥生成解锁脚本(就是新一次交易的输入脚本),如果组合脚本通过了,说明提供这个解锁脚本的用户拥有这个UTXO,即有权使用这部分比特币。至于矿工会不会修改这部分交易的收款人,我就不知道了,我觉得是可能的,因为解锁脚本中并不包含收款人的任何信息,而且矿工还可以成功打包。

脚本实际执行的时候不会将两个脚本叠加在一起,因为执行失败可能会导致锁定脚本被污染,所以10年更新的时候就把两个分开执行,两者一样结果就可以。执行的时候是反着的,先执行输入脚本(解锁脚本),再执行输出脚本(上锁脚本)。不过下面的讲解为了方便,就把两个叠在一起了。如果输入脚本有1条语句,输出脚本有两条,相当于将3条连在一起执行,具体的可以看P2PK脚本的讲解。A->B转账,B->C转账,输出脚本指的是A->B的那个输出,输入脚本指的是B->C的输入脚本,下面的讲解以这个为例。下面几种脚本的区别就是输出脚本和输入脚本需要提供的信息不同

7.2 脚本指令

下面这几个用的时候查就行

  • PUSHDATA(a):将数据a压入栈中
  • CHECKSIG:将栈顶元素弹出栈a和b,用b来解锁a
  • DUP:将栈顶元素复制一遍再压入栈
  • HASH160:将栈顶元素弹出,计算好哈希再压入栈中
  • EQUALVERIFY:将栈顶元素弹出,比较两者是否相等,如果相等就继续计算,如果不等就结束
  • CHECKMULTISIG:多重签名
  • RETURN:无条件返回错误

7.3 P2PK脚本(Pay to Public Key)

input script
    PUSHDATA(Sig)
output script
    PUSHDATA(PubKey)
    CHECKSIG

B总共需要的就是用私钥加密后的自己的签名。

先将签名Sig压入栈,再将公钥PubKey(B的公钥,因为比需要证明自己拥有那部分比特币)压入栈,执行CHECKSIG,对PubKey进行哈希计算检查是否等于Sig。

之前说过,私钥加密的东西只有用公钥解开。所以b只需要用自己的私钥加密得到Sig,解密的时候再用自己的PubKey就OK了,现在两个密钥都有了,那原文是什么呢?不知道嘞。

不过这里有问题,如果输入脚本和输出脚本分别执行,CHECKSIG在检查的时候栈顶只有一个元素,怎么检查呢?不过这个问题不是很重要,就算了。

7.4 P2PKH(Pay to Public Key Hash)

input script:
    PUSHDATA(Sig)
    PUSHDATA(PubKey)
output script:
    DUP
    HASH160
    PUSHDATA(PubKeyHash)
    EQUALVERIFY
    CHECKSIG

B总共需要提供的是用自己的私钥加密的签名和自己的公钥。输出脚本提供的一定是哈希,因为哈希是不能推出原文的,输入脚本提供的是原文,却能求出哈希。

1.将B的签名压入栈,栈中:Sig

2.将B的公钥压入栈,栈中:Sig,PubKey

3.将栈顶元素复制,栈中:Sig,,PubKey,,PubKey

4.对栈顶元素计算hash,栈中:Sig,PubKey,PubKeyHash

5.压入B的公钥hash,栈中:Sig,PubKey,PubKeyHash,PubKeyHash

6.判断栈顶元素的相等与否,栈中:Sig,PubKey

7.公钥解密签名验证

7.5 P2SH(Pay to Script Hash)与多重签名

7.5.1 P2SH

此时前一次交易的输出脚本需要提供的是redeem script(赎回脚本)的哈希可以,而不是公钥的哈希。总共分为两部分,第一步需要验证输入脚本给出的赎回脚本是否正确(如果不知道赎回脚本是什么,自然不会知道赎回脚本的哈希是什么了),第二步要执行一下赎回脚本,验证成功才可。

输入脚本给出的serialized redeemScript,什么是序列化和反序列化,不懂。

赎回脚本可以使用之前的方法,P2PK、P2PKH和多重签名形式,因为P2SH的第二阶段和前面的是没有区别的。

input script
    PUSHDATA(Sig)
    PUSHDATA(serialized redeemScript)
output script
    HASH160
    PUSHDATA(redeemScriptHash)
    EQUAL

第一阶段:

1.将签名压入栈中,栈中:Sig

2.将脚本压入栈中,栈中:Sig,seriRs

3.计算栈顶的哈希,栈中:Sig,RSH(redeemscript hash)

4.压入脚本哈希,栈中:Sig,RSH,RSH

5.进行比较,栈中:Sig

第二阶段:

例如赎回脚本使用的是P2PK

PUSHDATA(PubKey)
CHECHSIG

6.将公钥压入栈,栈中:Sig,PubKey

7.比较

7.5.2 被废弃的多重签名

先说什么是多重签名,例如一共公司有5个人,需要4个人提供私钥才能把钱花出去,或者7个人需要6个人才能把钱花出去。也为私钥的丢失提高了容错,一个人把私钥丢了也没事。

input script
    xxx
    PUSHDATA(Sig_1)
    PUSHDATA(Sig_2)
    ...
    PUSHDATA(Sig_M)
output script
    M
    PUSHDATA(pubkey_1)
    PUSHDATA(pubkey_2)
    ...
    PUSHDATA(pub_key_N)
    N
    CHECKMULTISIG

最上面xxx是因为CHECKMULTISIG有个BUG,弹数据的会弹两个,所以就给个xxx的废值。执行的时候先输入M个签名,再压入M,N个公钥,N,最后执行CHECKMULTISIG进行多重签名。N>=M。

这种方式有个缺点,例如向公司转账,输出脚本都是用户提供的,因为用户是交钱的,如果公司的结构改变,或者向不同的公司转账,这个输出脚本都要发生变化,新的多重签名这个复杂的过程是由公司即接收方提供的。

7.5.3 新的多重签名

如果用P2SH,用户在生成交易的时候只需要输入店家的赎回脚本哈希就可以了,如果公司结构修改,用户也只需要修改这个哈希。当店家要花这部分钱时,由他们提供赎回脚本。

avatar

过程就不讲了,和上面是一样的。

7.8 Proof of Burn

在生成输出脚本的时候带上RETURN。这个脚本的意思是不管怎么执行都会返回FALSE,也就是说这个币被送到了一个永远不可能花的地方,不管输入脚本怎么写都不可能解锁。那这样有什么用呢?有两个。

  • altcoin用比特币做背书。这个我不是特别理解,应该是金融相关的内容。是说烧掉多少比特币就可以用这些价值的比特币做背书发放等价值的新的数字货币货币。可以理解为数字货币中非比特币的数字货币都叫altcoin。

  • 数据存储。我可以销毁一部分比特币,让它存储一堆信息的哈希,因为比特币时间戳的存在,可以证明在什么时间我就已经获得了那些信息。例如我已经有了某种专利,我把专利信息求哈希写入已经burn掉的比特币交易中。我可能只用了几聪的比特币却记录了我的专利。

    之前讲过每个区块的coin base域不也是可以随便写的嘛,而且还不用burn比特币。不过那个有个致命的缺点,这个事情必须由最早完成工作量证明的矿工完成,大多数人都不是矿工,而且写上这些消息矿工还不一定能挖到矿,因为他们的可尝试的nonce变少了。

有个问题要考虑一下,如果写的收款人地址是错的,比如可能这个地址根本就是不存在的,是不这个钱就永远都取不出来了?当然是,所以就有些人把需要记录的东西求哈希写到了收款人地址那里,因为有的求完哈希后长得和收款人地址很像,所以节点也看不出来哪里不对。这么做和用return Proof of Burn效果肯定是一样的,都保存了东西,但是这样好吗?答案是不好。因为全节点根本就找不知道这是个死节点,会加大全节点的压力,所以这样没有return好。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!