区块链学习笔记

您所在的位置:网站首页 command的各种形式 区块链学习笔记

区块链学习笔记

2023-12-05 14:16| 来源: 网络整理| 查看: 265

写在前面

这篇博客主要总结一下之前做过的区块链作业中的一些有趣的东西。

实验环境搭建

按照老师的要求以及助教给的一些问题的解决方案。把python从3.8装回3.7再装回3.6,好像都没什么用。在pycharm运行代码总是说缺少依赖,然而明明已经装过了,点击它给的解决方案安装依赖,然后安装失败。于是我灵机一动,把之前装过的都卸掉了,再点那个安装,还是失败,于是我打开里面的虚拟终端通过命令行来安装。终于不提示缺少依赖。然而运行的时候,提示我的python-bitcoinlib库有问题,点进去查看源码依然毫无头绪。然后打开了虚拟机里的Ubuntu,安装依赖后可以跑代码。于是我的环境终于有了。

领取测试币

进到比特币测试网络领取比特币,在找完公交车或者自行车以及斑马线之后总是卡住不同,于是挂vpn再访问终于施舍给了我点币子。

比特币脚本(暂时了解的)

比特币脚本语言是一种基于栈的脚本语言(每个命令只执行一次),不是图灵完备的。比特币脚本包含两个部分,解锁脚本+锁定脚本,连在一起才是完整的,运行正确之后在栈中返回一个Ture。

公钥支付 P2PK (Pay to Publish Key)

锁定脚本

#公钥 OP_CKECKSIG #验证签名的脚本语言

解锁脚本

#签名

OP_CKECKSIG的部分函数定义,大致看起来就是利用pubkey生成一个数字签名,再与输入的签名比较,最后如果是ok就会在栈里留下一个Ture

def _CheckSig(sig, pubkey, script, txTo, inIdx, err_raiser): key = bitcoin.core.key.CECKey() key.set_pubkey(pubkey) if len(sig) == 0: return False hashtype = _bord(sig[-1]) sig = sig[:-1] (h, err) = RawSignatureHash(script, txTo, inIdx, hashtype) return key.verify(h, sig) ------------------------------------------------------- elif sop == OP_CHECKSIG or sop == OP_CHECKSIGVERIFY: check_args(2) vchPubKey = stack[-1] vchSig = stack[-2] tmpScript = CScript(scriptIn[pbegincodehash:]) tmpScript = FindAndDelete(tmpScript, CScript([vchSig])) ok = _CheckSig(vchSig, vchPubKey, tmpScript, txTo, inIdx, err_raiser) if not ok and sop == OP_CHECKSIGVERIFY: err_raiser(VerifyOpFailedError, sop) else: stack.pop() stack.pop() if ok: if sop != OP_CHECKSIGVERIFY: stack.append(b"\x01") else: # FIXME: this is incorrect, but not caught by existing # test cases stack.append(b"\x00")

完整的脚本

---------- OP_CKECKSIG

执行过程

NULL|初始状态,栈为空 |压栈 |压栈 Ture|执行OP_CHECKSIG,成功会把前两个东西pop掉,压入一个Ture P2PKH(Pay To Public Key Hash)

刚开始不知道公钥哈希值是什么,原来就是我们的地址。私钥通过SHA256得到公钥,公钥经过RIPEMD160得到公钥哈希,公钥哈希经过Base58编码就是地址,某种意义上,地址就是公钥哈希

锁定脚本

OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG

解锁脚本

完整脚本

---------- OP_DUP OP_HASH160 OP_EQUALVERIFY #检验两个值是否相等,不会返回值,只会删去栈顶两个元素 OP_CHECKSIG

执行过程

NULL | 初始状态栈为空 | sig压栈 | pubKey压栈 | OP_DUP复制栈顶元素并将其压栈 | OP_HASH160计算公钥哈希值并用其替换栈顶 | 已知的address压栈,即不知道 | 比较address和pubKeyHash,相等后抛弃栈顶开始的两位,不相等就终止 true | 用CHECKSIG验证签名有效,有效则true,无效则false 多重签名P2MS(Multiple Signatures)

对于一个M-N的交易(M是需要认证的人数,N是总人数,即只要N中M个人的签名),锁定脚本为:

OP_M ... OP_N OP_CHECKMULTISIG

解锁脚本

OP_0 # OP_CHECKMULTISIG的一个bug,下面会解释 ... # N个人中随意M个人的签名

完整脚本

OP_0 ... ---------- OP_M ... OP_N OP_CHECKMULTISIG

执行过程

NULL |初始状态,栈为空 0 |OP_0将0压栈 0···|M个签名入栈 0···M|OP_M把M压栈 0···M··· |N个PublicKey入栈 True |OP_CHECKMULTISIG进行验证,成功则返回一个ture

OP_0是OP_CHECKMULTISIG的一个bug,不过由于历史遗留问题,debug成本过高,所以就遗留下来了,没什么具体含义,可以当成一个小彩蛋。OP_CHECKMULTISIG所做的具体操作

弹出一个N,得到公钥数量 弹出N个公钥的值 弹出一个M,得到签名数量 弹出M个签名 得到了所有数据,计算脚本是否有效

在弹出M个签名之后,会再pop一次,如果没有OP_0,存在,这时候栈已经空了,pop会出错,脚本没法运行,OP_0就是为了这个bug而生的,弹出的OP_0不会对脚本运行结果产生影响,只是让它能够运行,所以压入什么数应该都可以

脚本哈希支付P2SH(Pay to Script Hash)

一开始以为这个和Multiple Signatures是一样的东西,后来看了很多博客才知道是可以通过这种方法实现P2MS,P2SH是2012年提出的,个人理解这个脚本是为了防止Script过于长而导致交易失败,通过HASH160得到脚本的哈希值,这样就能解决长度问题

P2SH中的脚本包括三部分,赎回脚本(redeem Script)、锁定脚本(locking Script)、解锁脚本(unlocking Script)

赎回脚本(redeem Script)类似于之前脚本里的锁定脚本(locking Script)内容是一致的 锁定脚本(locking Script)形式是固定的:

OP_HASH160 #redeem Script的哈希 OP_EQUAL

解锁脚本(unlocking Script)与赎回脚本相关

··· ··· #需要的签名以及其他内容 #序列化的赎回脚本,作为数据而不作为脚本语言压栈

脚本的执行过程

首先就是解锁的脚本压栈 此时栈顶就是 然后锁定脚本依次压栈 先得到赎回栈顶的哈希值 再与锁定脚本中的哈希值对比两者不匹配验证就失败 成功则序列化脚本会被反序列化再与栈内的剩余内容(也就是解锁脚本中剩下的所有或者其他内容)构成完整的脚本

下面以实现P2PKH与P2MS两种脚本为例写两个P2SH脚本

P2PKH

赎回脚本(redeem Script)

OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG

锁定脚本(locking Script)

OP_HASH160 #redeem Script的哈希 OP_EQUAL

解锁脚本(unlocking Script)

完整脚本

---------- OP_HASH160 OP_EQUAL P2MS

赎回脚本(redeem Script)

OP_M ... OP_N OP_CHECKMULTISIG

锁定脚本(locking Script)

OP_HASH160 #redeem Script的哈希 OP_EQUAL

解锁脚本(unlocking Script)

OP_0 ... # N个人中随意M个人的签名

完整脚本

OP_0 ... ---------- OP_HASH160 OP_EQUAL 作业中的unknown脚本(P2PK+P2MS) 作业要求 生成一个涉及四方的多签名交易,这样交易可以由第一方(银行)与另外三方(客户)中的任何一方(客户)共同赎回,而不仅仅只是客户或银行。对于这个问题,你可以假设是银行的角色,这样银行的私钥就是你的私钥,而银行的公钥就是你的公钥。使用keygen.py生成客户密钥并将它们粘贴到ex2a.py中。赎回事务并确保scriptPubKey尽可能小。可以使用任何合法的签名组合来赎回交易,但要确保所有组合都有效 解题思路

该交易中一共有四位参与者,银行与三位客户,需要银行与其他任何一个用户的签名才能赎回交易,如果只用多重签名,那么不需要银行的签名也能赎回,所以我们要把实验分两步,一步是验证银行签名,另外一步是验证客户签名。所以可以用P2PK+P2MS构成的脚本

脚本实现

其实上述两个步骤没有先后顺序,银行和客户身份先验证后验证都无所谓,反正逃不过验证,只不过用的脚本会不一样

先银行后客户

锁定脚本

#银行公钥 OP_CHECKSIGVERIFY #使用OP_CHECKSIGVERIFY而不用OP_CHECKSIG是因为它不会有在栈里压数剧,后面脚本执行不会出错 OP_1 #三个客户的公钥 OP_3 OP_CHECKMULTISIG

解锁脚本

OP_0 #注意顺序,因为先验证银行,所以银行的pubkey要在栈顶

完整脚本

OP_0 ---------- OP_CHECKSIGVERIFY OP_1 OP_3 OP_CHECKMULTISIG

执行过程

NULL | 起始状态 0 | OP_0压栈 0 | 压栈 0 | 压栈 0 | pubkey bank压栈 0 | CHECKSIGVERIFY验证和是否匹配并将它们pop掉,匹配则继续,不匹配直接就验证不成功 0 1 | OP_1压栈,确定需要检查的参数 0 1 | 3个pubkey压栈 0 1 3 | OP_3确定3个参数作为检查的范围 true | OP_CHECKMULTISIG检查多重签名,成功返回一个ture,否则验证失败 先客户后银行

原理相同只给出完整脚本(注意脚本指令变了)

OP_0 ---------- OP_1 OP_3 OP_CHECKMULTISIGVERIFY OP_CHECKSIG


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3