zoukankan      html  css  js  c++  java
  • 比特币中P2PKH(pay-to-public-key-hash)的锁定脚本和解锁脚本

    脚本格式

    P2PKH的锁定脚本为:

    OP_DUP OP_HASH160 PUSHDATA(<Cafe Public Key Hash>) OP_EQUALVERIFY OP_CHECKSIG
    

    P2PKH的解锁脚本为:

    PUSHDATA(<Cafe Signature>) <Cafe Public Key>
    

    脚本参数解释

    脚本中的常量值

    OP_DUP=0x76
    OP_HASH160=0xA9
    OP_EQUALVERIFY=0x88
    OP_CHECKSIG=0xAC
    

    PUSHDATA

    PUSHDATA封装格式为:

    如果0 < data.length < 76(0x4C),则结果为:1个字节data.length + data数据
    如果76(0x4C) <= data.length < 2^8,则结果为:0x4C + 1个字节data.length + data数据
    如果2^8 <= data.length < 2^16,则结果为:0x4D + 2个字节data.length + data数据
    如果2^16 <= data.length < 2^32,则结果为:0x4E + 4个字节data.length + data数据
    

    Signature的格式

    Signature的格式为DER(r,s) + SIGHASH

    DER的封装格式

    DER是ASN1数据格式中的一种,DER的封装规则还没有搞太明白,但是数据结构可解,开头是固定值0x30,后面0x45为后续数据长度,0x0220或者0x022100后面就是签名值r和s。
    DER的java代码实现:

    private static byte[] toDER(BigInteger r, BigInteger s) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream(72);
        DERSequenceGenerator seq = null;
        byte[] res = new byte[0];
        try {
            seq = new DERSequenceGenerator(bos);
            seq.addObject(new ASN1Integer(r));
            seq.addObject(new ASN1Integer(s));
            seq.close();
            res = bos.toByteArray();
            return res;
        } catch (IOException e) {
    
        }
        return null;
    }
    

    SIGHASH

    SIGHASH是签名哈希类型。

    比特币签名具有指示交易数据的哪一部分包含在使用 SIGHASH 标志的私钥签名的哈希中的方式。SIGHASH 标志是附加到签名的单个字节。 每个签名都有一个SIGHASH标志,该标志在不同输入之间也可以不同。 具有三个签名输入的交易可以具有不同SIGHASH标志的三个签名,每个签名签署(承诺)交易的不同部分。

    记住,每个输入可能在其解锁脚本中包含一个签名。 因此,包含多个输入的交易可以拥有具有不同SIGHASH标志的签名,这些标志在每个输入中承诺交易的不同部分。 还要注意,比特币交易可能包含来自不同“所有者”的输入,他们在部分构建(和无效)的交易中可能仅签署一个输入,继而与他人协作收集所有必要的签名后再使交易生效。 许多SIGHSASH标志类型,只有在你考虑到由许多参与者在比特币网络之外共同协作去更新仅部分签署了的交易,才具有意义。

    有三个SIGHASH标志:ALL,NONE和SINGLE,如下表所示。

    signhash-flag

    另外还有一个修饰符标志SIGHASH_ANYONECANPAY,它可以与前面的每个标志组合。 当设置ANYONECANPAY时,只有一个输入被签名,其余的(及其序列号)打开以进行修改。 ANYONECANPAY的值为0x80,并通过按位OR运算,得到如下所示的组合标志:

    signhash-flag2 png

    SIGHASH标志在签名和验证期间应用的方式是建立交易的副本和删节其中的某些字段(设置长度为零并清空),继而生成的交易被序列化,SIGHASH标志被添加到序列化交易的结尾,并将结果哈希化 ,得到的哈希值本身即是被签名的“消息”。 基于SIGHASH标志的使用,交易的不同部分被删节。 所得到的哈希值取决于交易中数据的不同子集。 在哈希化前,SIGHASH作为最后一步被包含在内,签名也会对SIGHASH类型进行签署,因此不能更改(例如,被矿工)。

    Public Key

    Public Key有两种表示方式,分别是非压缩格式和压缩格式,这两种方式算出来的比特币地址不同,所以不能混用。

    压缩格式就是数据中只有公钥的X,没有Y。压缩公钥以0x21开头,0x02或0x03代表Y值在X轴的上方还是下方,后面是X点的数据(32 bytes)。示例:

    21 03 0b461bf0f1253c9dacde9992594042d77798c5f0e7c76a1c587518606fb35478
    

    非压缩格式的数据,开头是0x41,0x04,后面紧跟着X点的数据(32 bytes)和Y点的数据(32 bytes)。示例:

    41 04
    0bf69616981e5970c992a0762f441abcadfed9fc4630fa5e1b82ab00e81d1690 // X
    5d3820e073e1bd4a9dcfed336f4bf25edc634c2e174989767d299748359c2daf // Y
    

    示例

    比特币testnet的一笔交易示例:912d470a1178ac09e31c43ee5696138fc51e94c7834864ed5c8eff29e5f54370

    tx912d

    通过blockcypher的API接口可以拿到更加详细的JSON数据

    这笔交易的JSON返回数据:

    {
      "block_hash": "000000003b5f089b739219d8f40ec34ca66b051c627cbb8e5d7a3dd031ff47d2",
      "block_height": 1298300,
      "block_index": 1,
      "hash": "912d470a1178ac09e31c43ee5696138fc51e94c7834864ed5c8eff29e5f54370",
      "hex": "010000000184f3684abd720033ff7a7654b48936088cd22c8d9e96d3a12e64559562e0fd93000000006b483045022100ea03e8414011fffc00f10a25a771076ee1cb4b0b24a02607c67462009e3d2c1d022070a7a1c74e6bb71c32d78043bccf7bbc353334eb6e517162b2270c85d6bb54d20121030b461bf0f1253c9dacde9992594042d77798c5f0e7c76a1c587518606fb35478ffffffff0100a92d01000000001976a914d9c637cc30bb0fe9add3a185c1f5d884d12b7b7888ac00000000",
      "addresses": [
        "mv24N7xJZySdMrLeQHvKTJYWmRyv9DY82Q",
        "n1NSP78VQ5iZqVVRMA9ZZ2r77eeGLnwdj8"
      ],
      "total": 19769600,
      "fees": 134400,
      "size": 192,
      "preference": "high",
      "relayed_by": "35.205.92.62:18333",
      "confirmed": "2018-05-22T03:22:51Z",
      "received": "2018-05-22T03:18:51.259Z",
      "ver": 1,
      "double_spend": false,
      "vin_sz": 1,
      "vout_sz": 1,
      "confirmations": 1,
      "confidence": 1,
      "inputs": [
        {
          "prev_hash": "93fde0629555642ea1d3969e8d2cd28c083689b454767aff330072bd4a68f384",
          "output_index": 0,
          "script": "483045022100ea03e8414011fffc00f10a25a771076ee1cb4b0b24a02607c67462009e3d2c1d022070a7a1c74e6bb71c32d78043bccf7bbc353334eb6e517162b2270c85d6bb54d20121030b461bf0f1253c9dacde9992594042d77798c5f0e7c76a1c587518606fb35478",
          "output_value": 19904000,
          "sequence": 4294967295,
          "addresses": [
            "mv24N7xJZySdMrLeQHvKTJYWmRyv9DY82Q"
          ],
          "script_type": "pay-to-pubkey-hash",
          "age": 1298299
        }
      ],
      "outputs": [
        {
          "value": 19769600,
          "script": "76a914d9c637cc30bb0fe9add3a185c1f5d884d12b7b7888ac",
          "addresses": [
            "n1NSP78VQ5iZqVVRMA9ZZ2r77eeGLnwdj8"
          ],
          "script_type": "pay-to-pubkey-hash"
        }
      ]
    }
    

    解析交易中的hex原始数据(如何解析比特币中的交易原始数据rawData):

    01000000 // version,4字节,倒序
    01 // 输入脚本个数
    84f3684abd720033ff7a7654b48936088cd22c8d9e96d3a12e64559562e0fd93 // UTXO(Unspent Transaction Output,未花费的交易输出),倒序
    00000000 // UTXO的index,从0开始
    6b // 解锁脚本长度
    48 3045022100ea03e8414011fffc00f10a25a771076ee1cb4b0b24a02607c67462009e3d2c1d022070a7a1c74e6bb71c32d78043bccf7bbc353334eb6e517162b2270c85d6bb54d201 21030b461bf0f1253c9dacde9992594042d77798c5f0e7c76a1c587518606fb35478 // 解锁脚本
    ffffffff // sequence,序列号
    01 // 输出脚本个数
    00a92d0100000000 // 转账金额,8字节,倒序
    19 // 锁定脚本长度
    76a914d9c637cc30bb0fe9add3a185c1f5d884d12b7b7888ac // 锁定脚本
    00000000 // lock time,时间戳
    

    这是一笔普通地址转账给普通地址的交易,正好用来解释P2PKH的锁定脚本和解锁脚本。

    锁定脚本

    例子中,锁定脚本为:

    76a914d9c637cc30bb0fe9add3a185c1f5d884d12b7b7888ac // 锁定脚本
    

    对照锁定脚本的格式,可以解析这段脚本如下:

    // OP_DUP OP_HASH160 <Cafe Public Key Hash> OP_EQUALVERIFY OP_CHECKSIG
    
    76 // OP_DUP 
    a9 // OP_HASH160 
    14 // 公钥的HASH值的长度,PUSHDATA
    d9c637cc30bb0fe9add3a185c1f5d884d12b7b78 // 公钥的HASH值
    88 // OP_EQUALVERIFY 
    ac // OP_CHECKSIG
    

    公钥HASH值的计算方法

    按常理来说,公钥HASH值计算方法是拿到公钥的byte数据做SHA256,再做RIPEMD160计算,结果为20字节HASH值。

    但是这里做的是转账,输入项肯定只有转账地址n1NSP78VQ5iZqVVRMA9ZZ2r77eeGLnwdj8。因为地址就是公钥的HASH值再加入一个头的network type和最后的四个字节checkSum后做base58生成的,所以可以反向操作:

    将比特币地址n1NSP78VQ5iZqVVRMA9ZZ2r77eeGLnwdj8进行base58解码,得到:6fd9c637cc30bb0fe9add3a185c1f5d884d12b7b783bcba0fb,其中0x6f代表是测试网络地址。将上面的结果去掉一字节头和尾部4字节校验和,得到:d9c637cc30bb0fe9add3a185c1f5d884d12b7b78,即为公钥HASH值

    解锁脚本

    例子中,解锁脚本为:

    48 3045022100ea03e8414011fffc00f10a25a771076ee1cb4b0b24a02607c67462009e3d2c1d022070a7a1c74e6bb71c32d78043bccf7bbc353334eb6e517162b2270c85d6bb54d201 21030b461bf0f1253c9dacde9992594042d77798c5f0e7c76a1c587518606fb35478 // 解锁脚本
    

    将解锁脚本按照PUSHDATA的规则解析成两个字段:
    Signature=483045022100ea03e8414011fffc00f10a25a771076ee1cb4b0b24a02607c67462009e3d2c1d022070a7a1c74e6bb71c32d78043bccf7bbc353334eb6e517162b2270c85d6bb54d201
    Public Key=21030b461bf0f1253c9dacde9992594042d77798c5f0e7c76a1c587518606fb35478

    解析一下Signature:

    48 // PUSHDATA
    3045022100
    ea03e8414011fffc00f10a25a771076ee1cb4b0b24a02607c67462009e3d2c1d // signature r
    0220
    70a7a1c74e6bb71c32d78043bccf7bbc353334eb6e517162b2270c85d6bb54d2 // signature s
    01 // SIGHASH

    解析一下Public Key:

    // 压缩格式的公钥
    21 03 0b461bf0f1253c9dacde9992594042d77798c5f0e7c76a1c587518606fb35478

    作者:itlgl
    出处:http://www.cnblogs.com/itlgl/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利

  • 相关阅读:
    整数反转
    最长公共前缀
    罗马数字转整数
    单点登录
    VMware Workstation虚拟机密钥
    Pytest 用例内部执行顺序
    判断是不是回文数
    python端口IP字符串是否合法
    python求二叉树深度
    有两个字符串类型的数字,实现一个方法将它们进行相加,并返回相加后的数值。
  • 原文地址:https://www.cnblogs.com/itlgl/p/10419316.html
Copyright © 2011-2022 走看看