首页 > 技术文章 > 比特币中P2PKH(pay-to-public-key-hash)的锁定脚本和解锁脚本

itlgl 2019-02-22 16:48 原文

脚本格式

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

推荐阅读