1. 引言
Bitcoin script可用于实现比特币网络的智能合约,其为stack-based(First in Last out),自左向右执行的。Bitcoin script不具备loops,为非图灵完备的。
a script本质为:
- 记录在交易中的一组指令,用于描述下一个人该如何来花费 在该交易中所发送的bitcoin。
一笔发送bitcoin到目标地址
D
D
D的交易,其script中包含了下一个spender需提供的2个信息:
- 1)一个公钥,当对该公钥进行hash运算,结果与script中嵌入的目标地址
D
D
D一致。
- 2)一个签名,用于证明该spender具有与该公钥对应的私钥。
script可灵活修改spend transferred bitcoin的参数,如可要求具有2个私钥、一组秘钥、甚至可以不要秘钥。
若交易中的script未触发failure,且当script执行完退出时the top stack item为True(非零值)时,即可认为该笔交易是valid的。
script中的opcodes分为如下类型:
- constants
- flowcontrol
- stack
- splice
- bitwise
- logic
- arithmetic
- crypto
- arithmetic
- crypto
- locktime
- pesudo-words
- reserved words
详细参看:Bitcoin wiki——Script
关键opcode有:
Word | Opcode | Hex | Input | Output | Description |
---|
OP_0, OP_FALSE | 0 | 0x00 | Nothing. | (empty value) | An empty array of bytes is pushed onto the stack. (This is not a no-op: an item is added to the stack.) | OP_2-OP_16 | 82-96 | 0x52-0x60 | Nothing. | 2-16 | The number in the word name (2-16) is pushed onto the stack. |
Word | Opcode | Hex | Input | Output | Description |
---|
OP_CHECKLOCKTIMEVERIFY (previously OP_NOP2) | 177 | 0xb1 | x | x / fail | Marks transaction as invalid if the top stack item is greater than the transaction’s nLockTime field, otherwise script evaluation continues as though an OP_NOP was executed. Transaction is also invalid if 1. the stack is empty; or 2. the top stack item is negative; or 3. the top stack item is greater than or equal to 500000000 while the transaction’s nLockTime field is less than 500000000, or vice versa; or 4. the input’s nSequence field is equal to 0xffffffff. The precise semantics are described in BIP 0065. | OP_CHECKSEQUENCEVERIFY (previously OP_NOP3) | 178 | 0xb2 | x | x / fail | Marks transaction as invalid if the relative lock time of the input (enforced by BIP 0068 with nSequence) is not equal to or longer than the value of the top stack item. The precise semantics are described in BIP 0112. |
Word | Opcode | Hex | Input | Output | Description |
---|
OP_DROP | 117 | 0x75 | x | Nothing | Removes the top stack item. | OP_DUP | 118 | 0x76 | x | x x | Duplicates the top stack item. | OP_SWAP | 124 | 0x7c | x1 x2 | x2 x1 | The top two items on the stack are swapped. |
Word | Opcode | Hex | Input | Output | Description |
---|
OP_IF | 99 | 0x63 | <expression> if [statements] [else [statements]]* endif | | If the top stack value is not False, the statements are executed. The top stack value is removed. | OP_ELSE | 103 | 0x67 | <expression> if [statements] [else [statements]]* endif | | If the preceding OP_IF or OP_NOTIF or OP_ELSE was not executed then these statements are and if the preceding OP_IF or OP_NOTIF or OP_ELSE was executed then these statements are not. | OP_ENDIF | 104 | 0x68 | <expression> if [statements] [else [statements]]* endif | | Ends an if/else block. All blocks must end, or the transaction is invalid. An OP_ENDIF without OP_IF earlier is also invalid. | OP_VERIFY | 105 | 0x69 | True / false | Nothing / fail | Marks transaction as invalid if top stack value is not true. The top stack value is removed. | OP_RETURN | 106 | 0x6a | Nothing | fail | Marks transaction as invalid. Since bitcoin 0.9, a standard way of attaching extra data to transactions is to add a zero-value output with a scriptPubKey consisting of OP_RETURN followed by data. Such outputs are provably unspendable and specially discarded from storage in the UTXO set, reducing their cost to the network. Since 0.12, standard relay rules allow a single output with OP_RETURN, that contains any sequence of push statements (or OP_RESERVED[1]) after the OP_RETURN provided the total scriptPubKey length is at most 83 bytes. |
Word | Opcode | Hex | Input | Output | Description |
---|
OP_HASH160 | 169 | 0xa9 | in | hash | The input is hashed twice: first with SHA-256 and then with RIPEMD-160. | OP_CHECKSIG | 172 | 0xac | sig pubkey | True / false | The entire transaction’s outputs, inputs, and script (from the most recently-executed OP_CODESEPARATOR to the end) are hashed. The signature used by OP_CHECKSIG must be a valid signature for this hash and public key. If it is, 1 is returned, 0 otherwise. | OP_CHECKMULTISIG | 174 | 0xae | x sig1 sig2 … <number of signatures> pub1 pub2 <number of public keys> | True / False | Compares the first signature against each public key until it finds an ECDSA match. Starting with the subsequent public key, it compares the second signature against each remaining public key until it finds an ECDSA match. The process is repeated until all signatures have been checked or not enough public keys remain to produce a successful result. All signatures need to match a public key. Because public keys are not checked again if they fail any signature comparison, signatures must be placed in the scriptSig using the same order as their corresponding public keys were placed in the scriptPubKey or redeemScript. If all signatures are valid, 1 is returned, 0 otherwise. Due to a bug, one extra unused value is removed from the stack. |
Word | Opcode | Hex | Input | Output | Description |
---|
OP_EQUAL | 135 | 0x87 | x1 x2 | True / false | Returns 1 if the inputs are exactly equal, 0 otherwise. | OP_EQUALVERIFY | 136 | 0x88 | x1 x2 | Nothing / fail | Same as OP_EQUAL, but runs OP_VERIFY afterward. |
Word | Opcode | Hex | Input | Output | Description |
---|
OP_SIZE | 130 | 0x82 | in | in size | Pushes the string length of the top element of the stack (without popping it). |
2. Bitcoin script debug
可使用 btcdeb 来进行script调试。
btcdeb可提供的功能有:
btcdeb> help
step Execute one instruction and iterate in the script.
rewind Go back in time one instruction.
stack Print stack content.
altstack Print altstack content.
vfexec Print vfexec content.
exec Execute command.
tf Transform a value using a given function.
print Print script.
help Show help information.
btcdeb> tf -h
echo [*] show as-is serialized value
hex [*] convert into a hex string
int [arg] convert into an integer
reverse [arg] reverse the value according to the type
sha256 [message] perform SHA256
ripemd160 [message] perform RIPEMD160
hash256 [message] perform HASH256 (SHA256(SHA256(message))
hash160 [message] perform HASH160 (RIPEMD160(SHA256(message))
base58chk-encode [pubkey] encode [pubkey] using base58 encoding (with checksum)
base58chk-decode [string] decode [string] into a pubkey using base58 encoding (with checksum)
bech32-encode [pubkey] encode [pubkey] using bech32 encoding
bech32-decode [string] decode [string] into a pubkey using bech32 encoding
verify-sig [sighash] [pubkey] [signature] verify the given signature for the given sighash and pubkey (der)
compact-verify-sig [sighash] [pubkey] [signature] verify the given signature for the given sighash and pubkey (compact)
combine-pubkeys [pubkey1] [pubkey2] combine the two pubkeys into one pubkey
tweak-pubkey [value] [pubkey] multiply the pubkey with the given 32 byte value
pubkey-to-xpubkey [pubkey] convert the given pubkey into an x-only pubkey, as those used in taproot/tapscript
addr-to-scriptpubkey [address] convert a base58 encoded address into its corresponding scriptPubKey
scriptpubkey-to-addr [script] convert a scriptPubKey into its corresponding base58 encoded address
add [value1] [value2] add two values together
sub [value1] [value2] subtract value2 from value1
jacobi-symbol [n] ([k]) calculate the Jacobi symbol for n modulo k, where k defaults to the secp256k1 field size
tagged-hash [tag] [message] generate the [tag]ged hash of [message]
taproot-tweak-pubkey [pubkey] [tweak] tweak the pubkey with the tweak
prefix-compact-size [value] prefix [value] with its compact size encoded byte length
The inline operators have slightly different names; they are called: echo, hex, int, reverse, sha256, ripemd160, hash256, hash160, base58chkenc, base58chkdec, bech32enc, bech32dec, verify_sig, combine_pubkeys, tweak_pubkey, pubkey_to_xpubkey, addr_to_spk, spk_to_addr, add, sub, jacobi, tagged_hash, taproot_tweak_pubkey, prefix_compact_size
如对于script:【即判断 6-2=4是否成立?】
OP_6 OP_2 OP_SUB OP_4 OP_EQUAL
采用btcdeb运行的结果为:
$ btcdeb ['OP_6 OP_2 OP_SUB OP_4 OP_EQUAL']
btcdeb 0.4.21 -- type `btcdeb -h` for start up options
LOG: sign segwit taproot
notice: btcdeb has gotten quieter; use --verbose if necessary (this message is temporary)
5 op script loaded. type `help` for usage information
script | stack
---------+--------
6 |
2 |
OP_SUB |
4 |
OP_EQUAL |
#0000 6
btcdeb> step
<> PUSH stack 06
script | stack
---------+--------
2 | 06
OP_SUB |
4 |
OP_EQUAL |
#0001 2
btcdeb> step
<> PUSH stack 02
script | stack
---------+--------
OP_SUB | 02
4 | 06
OP_EQUAL |
#0002 OP_SUB
btcdeb> step
<> POP stack
<> POP stack
<> PUSH stack 04
script | stack
---------+--------
4 | 04
OP_EQUAL |
#0003 4
btcdeb> step
<> PUSH stack 04
script | stack
---------+--------
OP_EQUAL | 04
| 04
#0004 OP_EQUAL
btcdeb> step
<> POP stack
<> POP stack
<> PUSH stack 01
script | stack
---------+--------
| 01
btcdeb>
最终输出结果为01,表示6-2=4 成立。
3. Bitcoin script分类
Bitcoin transactions encodes spending conditions through:
- a scriptPubKey in outputs。
- a witness and scriptSig in inputs。
具体可参看:
- 1)P2PK(Pay To Pubkey):为a script pattern that locks an output to a public key。
- 2)P2PKH(Pay To Pubkey Hash):为the most common script used for locking an output to someone’s public key。、
- 3)P2MS(Pay To Multisig):2012年1月引入,用于lock bitcoins to multiple public keys,且要求signatures for some (or all) of those public keys to unlock it。P2MS并不多见,大多数multisig transactions都使用P2SH script实现。
如,需要3个人中的2个人签名即可花费该bitcoin,则可表示为: - 4)P2SH(Pay To Script Hash):2012年4月引入。用于lock bitcoins to the hash of a script,当unlock时,需提供that original script。该original script 称为 redeem script。redeem script即为P2MS locking script。
实际执行分2个阶段:先判断script hash是否相等,然后执行redeem script。 其中的[20-byte-hash-value] 实际为 push-20-bytes-onto-the-stack opcode (0x14) followed by exactly 20 bytes。因此,P2SH的scriptPubKey一共为23 byte。
OP_HASH160 [20-byte-hash-value] OP_EQUAL
- 5)P2WPKH(Pay To Witness Public Key Hash):witness中必须包含2个items。其scriptPubKey 比 P2PKH 的少占用了3个字节。且将signature和public key由 scriptSig移到了witness。
分为2个阶段:先验证HASH160(pubkey)一致,然后验签<signature> <pubkey> CHECKSIG 。
witness: <signature> <pubkey>
scriptSig: (empty)
scriptPubKey: 0 <20-byte-key-hash>
(0x0014{20-byte-key-hash})
- 6)P2WSH(Pay To Witness Script Hash):P2SH的scriptPubKey为23字节,而P2WSH的为34字节,增加的size可提升security against possible collision attacks。将P2SH sigScript中的内容移至了witness。
分2个阶段执行:首先验证 SHA256(last item in witness) 与 scriptPubKey中的32-byte-hash 值是否一致(即SHA256(<1 <pubkey1> <pubkey2> 2 CHECKMULTISIG>)=<32-byte-hash> );然后执行witness程序(即0 <signature1> 1 <pubkey1> <pubkey2> 2 CHECKMULTISIG )。以1-of-2 multi-signature P2WSH为例:
witness: 0 <signature1> <1 <pubkey1> <pubkey2> 2 CHECKMULTISIG>
scriptSig: (empty)
scriptPubKey: 0 <32-byte-hash>
(0x0020{32-byte-hash})
参考资料
[1] Bitcoin Developer Network—Bitcoin script 101 [2] Bitcoin wiki——Script [3] Bitcoin script debugger
|