Skip to content

Instantly share code, notes, and snippets.

@sidgwick
Last active February 28, 2021 23:58
Show Gist options
  • Save sidgwick/9ec35a8bf92132096e09d8be0b43b2c5 to your computer and use it in GitHub Desktop.
Save sidgwick/9ec35a8bf92132096e09d8be0b43b2c5 to your computer and use it in GitHub Desktop.

OP_CHECKSIG 详解

预备知识

  • scriptSig 即解锁脚本, 是交易输入里面, 用来证明自己所有权一个私钥签名
  • scriptPubKey 即锁定脚本, 即上一笔交易输出(本次交易的输入)里面用来锁定所有权的字节序列

WIKI

OP_CHECKSIG is Script opcode used to verify that the signature for a tx input is valid. OP_CHECKSIG expects two values to be on the stack. These are, in order of stack depth, the public key and the signature of the script. These two values are normally obtained by running the scriptSig script of the transaction input we are attempting to validate. The scriptSig script is deleted after it is run, but the stack is left as is. Then, the scriptPubKey script from the previous transaction output that is now being spent is run, generally concluding in an OP_CHECKSIG.

The standard scriptPubKey checks that the public key (actually a hash of) is a particular value, and that OP_CHECKSIG passes.

花费者需要能给 ScriptPubKey 提供一个恰当的签名, 就认为这个花费者有权使用此输出.

For normal transaction inputs, if the creator of the current transaction can successfully create a ScriptSig signature that uses the right public key for the ScriptPubKey of the transaction output they are attempting to spend, that transaction input is considered valid.

Parameters

作为栈的辅助参数, 我们需要知道当前交易以及当前交易输入中的索引(index).

In addition to the stack parameters and the script code itself, in order to operate correctly OP_CHECKSIG needs to know the current transaction and the index of current transaction input.

How it works

Firstly always this (the default) procedure is applied:

  • 公钥和签名从栈中依次弹出, 签名格式为 <DER signature> <1 byte hash-type>. 签名最后一字节是签名格式.
  • 脚本剩余的部分, 从 OP_CODESEPARATOR 开始往后知道脚本结束部分作为子脚本, 如果没有 OP_CODESEPARATOR, 整个脚本就是子脚本. 子脚本记做 subScript(A new subscript is created from the instruction from the most recently parsed OP_CODESEPARATOR (last one in script) to the end of the script. If there is no OP_CODESEPARATOR the entire script becomes the subscript (hereby referred to as subScript))
  • 从子脚本里面删除签名(如果有的话).
  • 从子脚本里面移除所有的 OP_CODESEPARATORS
  • 签名最后一字节类型标记被移除, 并保存.
  • 复制当前交易, 记做: txCopy.
  • txCopy 里面所有交易的 script, 都置为空(准确说是 1 字节 0x00)
  • txCopy 里面当前交易的 input 脚本被设定为 subScript, 前缀 var-integer 长度

Now depending on the hashtype various things can happen to txCopy, these will be discussed individually.

Name Value
SIGHASH_ALL 0x00000001
SIGHASH_NONE 0x00000002
SIGHASH_SINGLE 0x00000003
SIGHASH_ANYONECANPAY 0x00000080
  • If (hashtype&31) = SIGHASH_NONE then apply the SIGHASH_NONE-procedure
  • If (hashtype&31) = SIGHASH_SINGLE then apply the SIGHASH_SINGLE-procedure
  • If hashtype & SIGHASH_ANYONECANPAY then apply the SIGHASH_ANYONECANPAY-procedure

于是, SIGHASH_ANYONECANPAY 程序可能被添加到任何其他的 hashtype, 上述四个 hashtype 之外还有一个 0 类型, 在主链中出现过几次, 他们的处理和 SIGHASH_ALL 一致

Hashtype SIGHASH_ALL (default)

No special further handling occurs in the default case. Think of this as "sign all of the outputs." Which is already done by the default procedure.

Procedure for Hashtype SIGHASH_NONE

  • txCopy 的输出被设定成一个空数组
  • 非当前输入索引对应的输入的 nSequence 被设置为 0.

Think of this as "sign none of the outputs-- I don't care where the bitcoins go."

Procedure for Hashtype SIGHASH_SINGLE

  • txCopy 的输出, 北条征程 index + 1 大小
  • 输出序列里面, 索引不是 index 的输出, 设置成空脚本和一个 long 类型的 -1 值(TransactionOutput 的 default 值)
  • 输入的数据, 索引不是 index 的, nSequence 被设置为 0

Think of this as "sign one of the outputs-- I don't care where the other outputs go".

The transaction that uses SIGHASH_SINGLE type of signature should not have more inputs than outputs. However if it does (because of the pre-existing implementation), it shall not be rejected, but instead for every "illegal" input (meaning: an input that has an index bigger than the maximum output index) the node should still verify it, though assuming the hash of 0000000000000000000000000000000000000000000000000000000000000001 [https://bitcointalk.org/index.php?topic=260595.0]

Procedure for Hashtype SIGHASH_ANYONECANPAY

  • The txCopy input vector is resized to a length of one.
  • The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector.

Think of this as "Let other people add inputs to this transaction, I don't care where the rest of the bitcoins come from."

===Final signature check===

An array of bytes is constructed from the serialized txCopy appended by four bytes for the hash type. This array is sha256 hashed twice, then the public key is used to check the supplied signature against the hash. The secp256k1 elliptic curve is used for the verification with the given public key.

== Return values ==

OP_CHECKSIG will push true to the stack if the check passed, false otherwise. OP_CHECKSIG_VERIFY leaves nothing on the stack but will cause the script eval to fail immediately if the check does not pass.

== Code samples and raw dumps ==

Taking the first transaction in Bitcoin which is in block number 170, we would get after serialising the transaction but before we hash+sign (or verify) it:

See also [https://gitorious.org/libbitcoin/libbitcoin libbitcoin] for code samples.

01 00 00 00              version
01                       number of inputs (var_uint)

input 0:
c9 97 a5 e5 6e 10 41 02  input address hash
fa 20 9c 6a 85 2d d9 06
60 a2 0b 2d 9c 35 24 23
ed ce 25 85 7f cd 37 04
00 00 00 00              input index

48                       size of script (var_uint)
47                       push 71 bytes to stack
30 44 02 20 4e 45 e1 69
32 b8 af 51 49 61 a1 d3
a1 a2 5f df 3f 4f 77 32
e9 d6 24 c6 c6 15 48 ab
5f b8 cd 41 02 20 18 15
22 ec 8e ca 07 de 48 60
a4 ac dd 12 90 9d 83 1c
c5 6c bb ac 46 22 08 22
21 a8 76 8d 1d 09 01
ff ff ff ff              sequence

02                       number of outputs (var_uint)

output 0:
00 ca 9a 3b 00 00 00 00  amount = 10.00000000
43                       size of script (var_uint)
script for output 0:
41                       push 65 bytes to stack
04 ae 1a 62 fe 09 c5 f5
1b 13 90 5f 07 f0 6b 99
a2 f7 15 9b 22 25 f3 74
cd 37 8d 71 30 2f a2 84
14 e7 aa b3 73 97 f5 54
a7 df 5f 14 2c 21 c1 b7
30 3b 8a 06 26 f1 ba de
d5 c7 2a 70 4f 7e 6c d8
4c
ac                       OP_CHECKSIG

output 1:
00 28 6b ee 00 00 00 00  amount = 40.00000000
43                       size of script (var_uint)
script for output 1:
41                       push 65 bytes to stack
04 11 db 93 e1 dc db 8a
01 6b 49 84 0f 8c 53 bc
1e b6 8a 38 2e 97 b1 48
2e ca d7 b1 48 a6 90 9a
5c b2 e0 ea dd fb 84 cc
f9 74 44 64 f8 2e 16 0b
fa 9b 8b 64 f9 d4 c0 3f
99 9b 86 43 f6 56 b4 12
a3
ac                       OP_CHECKSIG

00 00 00 00              locktime
01 00 00 00              hash_code_type (added on)

result =
01 00 00 00 01 c9 97 a5 e5 6e 10 41 02 fa 20 9c 6a 85 2d d9 06 60 a2 0b 2d 9c 35
24 23 ed ce 25 85 7f cd 37 04 00 00 00 00 43 41 04 11 db 93 e1 dc db 8a 01 6b 49
84 0f 8c 53 bc 1e b6 8a 38 2e 97 b1 48 2e ca d7 b1 48 a6 90 9a 5c b2 e0 ea dd fb
84 cc f9 74 44 64 f8 2e 16 0b fa 9b 8b 64 f9 d4 c0 3f 99 9b 86 43 f6 56 b4 12 a3
ac ff ff ff ff 02 00 ca 9a 3b 00 00 00 00 43 41 04 ae 1a 62 fe 09 c5 f5 1b 13 90
5f 07 f0 6b 99 a2 f7 15 9b 22 25 f3 74 cd 37 8d 71 30 2f a2 84 14 e7 aa b3 73 97
f5 54 a7 df 5f 14 2c 21 c1 b7 30 3b 8a 06 26 f1 ba de d5 c7 2a 70 4f 7e 6c d8 4c
ac 00 28 6b ee 00 00 00 00 43 41 04 11 db 93 e1 dc db 8a 01 6b 49 84 0f 8c 53 bc
1e b6 8a 38 2e 97 b1 48 2e ca d7 b1 48 a6 90 9a 5c b2 e0 ea dd fb 84 cc f9 74 44
64 f8 2e 16 0b fa 9b 8b 64 f9 d4 c0 3f 99 9b 86 43 f6 56 b4 12 a3 ac 00 00 00 00
01 00 00 00

To understand where that raw dump has come from, it may be useful to examine tests/ec-key.cpp in [https://gitorious.org/libbitcoin/libbitcoin libbitcoin],

[https://gitorious.org/libbitcoin/libbitcoin libbitcoin] has a unit test under tests/ec-key.cpp (make ec-key && ./bin/tests/ec-key). There is also a working OP_CHECKSIG implementation in src/script.cpp under script::op_checksig(). See also the unit test: tests/script-test.cpp

==References==

[[Category:Technical]] [[Category:Developer]] {{DISPLAYTITLE:OP_CHECKSIG}}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment