风险提示:防范以"数字货币""区块链"名义进行非法集资的风险
比特币脚本(Bitcoin Script)是一种用于控制交易的简单编程语言。在比特币网络中,交易需要满足一定条件才能被确认,比特币脚本就是用来规定这些条件的。比特币脚本允许用户定义各种复杂的交易条件,例如多重签名(Multi-Sig)、时间锁(Time Locks)、条件支付(Conditional Payments)等。

全球三大交易所之一,注册领50U数币盲盒,币圈常用的交易平台!

币安是世界领先的数字货币交易平台,注册领100U。
什么是比特币脚本
比特币脚本(Bitcoin Script)是一种用于控制交易的简单编程语言。在比特币网络中,交易需要满足一定条件才能被确认,比特币脚本就是用来规定这些条件的。比特币脚本允许用户定义各种复杂的交易条件,例如多重签名(Multi-Sig)、时间锁(Time Locks)、条件支付(Conditional Payments)等。
比特币脚本的设计初衷是为了增加交易的安全性和灵活性。通过比特币脚本,用户可以定义多种不同的支付条件,例如只有在特定时间内才能花费比特币、需要多个私钥签名才能完成交易等。这为用户提供了更多的选择和控制权,从而增强了交易的安全性。
比特币脚本是基于堆栈的脚本语言,类似于编程语言中的逆波兰表达式。比特币脚本由一系列操作码(Opcode)组成,每个操作码执行不同的操作,例如将数据推入堆栈、对堆栈中的数据进行运算等。通过组合不同的操作码,用户可以创建各种不同的支付条件。
比特币脚本的执行是在比特币网络中的每个节点上进行的,节点需要验证交易是否满足脚本中定义的条件。如果交易符合脚本规定的条件,节点就会将其纳入区块中,从而完成交易确认。
比特币交易概览
一个比特币交易的实例:来源

这个交易的JSON:
{
"txid": "c07534fe61e1d7a0cfa81326f5efa8533c8a93ad7cfcba3767123ff0b4d2e7c3",
"size": 371,
"version": 1,
"locktime": 0,
"fee": 188100,
"inputs": [
{
"coinbase": false,
"txid": "a53788308b127d44eaf5d549b7626e2fb5b4ee34df581d65b508e8f5271cd6c0",
"output": 2,
"sigscript": "",
"sequence": 4294967295,
"pkscript": "0014e70986d7a0401b2c9ac354487d56c7e82b89b4b5",
"value": 33023251,
"address": "bc1quuycd4aqgqdjexkr23y864k8aq4cnd94jy6u8u",
"witness": [
"304502210084a4f4277134913ce3d8fcceafc771e32bd9fdcb7b8b43314ec337ab901ad9f502201f06c6d683fc7f3fb4d04848730bd9e09a3f800cbe833b21ebf4feb8c2d41a3501",
"038a1997fd115368187cee688d285398e74714e7e2f42a65480f0f8960fa6a02ff"
]
},
{
"coinbase": false,
"txid": "ef3775c685181ee24d6274c25e359e4002cafba35d76fbd4459a8107b16c1f64",
"output": 1,
"sigscript": "",
"sequence": 4294967295,
"pkscript": "00147fe8e29b2835fdaf2efde03f0c8d6ef480594858",
"value": 63767807,
"address": "bc1q0l5w9xegxh767thauqlsertw7jq9jjzcj69lw2",
"witness": [
"304402207eed4a2709c62f2916bbb3c24b7f9f4cac239a89b6de99e8012033396f8897bd0220566573564fe7f9b11b0d9ff7d9eb2cbc40fd97f92a6ef9f2f5acffe870638c2601",
"027ebe84f812272370a3b210eeb6f488c67d178dbf18a94e41622edafe333846ce"
]
}
],
"outputs": [
{
"address": "bc1qh0a08hs7ytqhq5dhv3p4fug2pfq36zncnnf4v9",
"pkscript": "0014bbfaf3de1e22c17051b7644354f10a0a411d0a78",
"value": 30343663,
"spent": true,
"spender": {
"txid": "b832b21f9078d07906ce233589a0f0764f52b44e230df0797a05d2040b6f6aac",
"input": 0
}
},
{
"address": "bc1qyd9rar2ngmffersqahj3z4lc8lvkw00s5e30e3",
"pkscript": "0014234a3e8d5346d29c8e00ede51157f83fd9673df0",
"value": 66259295,
"spent": true,
"spender": {
"txid": "bc20fd44855f7861b08a4f26e1f0b0838a99f87e9caf8d0b09110a29b5232383",
"input": 50
}
}
],
"block": {
"height": 792216,
"position": 1
},
"deleted": false,
"time": 1685518790,
"rbf": false,
"weight": 833
}
交易脚本结构
交易脚本整体结构:
{
"txid": "c07534f.....d2e7c3", // 交易的Hash值
"size": 371, // 交易的大小
"version": 1, // 使用的BTC协议的版本号
"locktime": 0, // 指用来设置交易的生效时间。绝大多数时候该值都为0,表示立即生效。如果设置为非0值,表示需要等一段时间才能生效,例如等10个区块之后才能写入区块链。
"fee": 188100,
"inputs": [ ........ ], // 输入部分
"outputs": [ ........ ], // 输出部分
"block": { // 该交易所在区块
"height": 792216,
"position": 1
},
"deleted": false,
"time": 1685518790, // 该交易产生的时间
"rbf": false, // Replace-by-fee,用一笔交易替代另一笔交易
"weight": 833
}
交易的输入:
{
"inputs": [ // 输入是一个数组,可以有多个输入
{
"coinbase": false,
// 表示本次交易该输入的币的来自txid交易的第output个输出
"txid": "a53788308...cd6c0", // 提供币的来源的交易Hash值
"output": 2, // 提供币的来源的交易中,对应的第几个输出(下标从0开始)。
"sigscript": "", // 签名(解锁脚本),本笔交易采用的隔离见证,sigscript为空
"sequence": 4294967295,
"pkscript": "0014...9b4b5", // 从币的来源output复制的pkscript加锁脚本。
"value": 33023251, // 金额,单位为“聪”,即0.33023251 BTC
"address": "bc1q...6u8u", // A账户的地址(即A的公钥)
"witness": [ // 隔离见证
"3045...a3501",
"038a....02ff"
]
},
{
"coinbase": false,
"txid": "ef37....1f64",
"output": 1,
"sigscript": "",
"sequence": 4294967295,
"pkscript": "0014....4858",
"value": 63767807,
"address": "bc1q....9lw2",
"witness": [
"3044....2601",
"027e....46ce"
]
}
]
}
“聪”是比特币中最小的单位,也写作Satoshi(中本聪的名字)、SAT,等于一个比特币的一亿分之一:1 聪 = 0.00000001 BTC
交易的输出:
{
"outputs": [ // 输出是一个数组,一个交易可以有多个输出
{
"address": "bc1q....f4v9", // B账户的地址(B的公钥)
"pkscript": "0014....d0a78", // 加锁脚本。加锁脚本在交易验证过程中非常重要。它的作用是在用户花费这笔钱的时候,需要证明这笔钱是他的。它其实是一个栈结构,里面是一条条的命令,验证过程就是依次执行这些命令,都通过了则验证通过。在花费这笔UTXO时,各个矿工会验证该加锁脚本,验证通过后才允许交易。
"value": 30343663, // 金额,单位“聪”
"spent": true, // 该笔输出是否已经被花掉了
"spender": { // 花掉这笔输出的交易
"txid": "b832...6aac", // 花掉这笔输出的交易hash值
"input": 0 // 花掉这笔输出的交易中的第几个输入
}
},
{
"address": "bc1q...30e3",
"pkscript": "001...3df0",
"value": 66259295,
"spent": true,
"spender": {
"txid": "bc20....2383",
"input": 50
}
}
]
}
交易的具体操作:

A转账给B,B再转给C。
B转给C时,输入的来源对应A转给B时的交易的输出。
验证B转给C这个交易合法性时,将B转给C的输入脚本和A转给B的输出脚本拼接在一起,执行没有出错即可。早期的比特币协议中,会将两个脚本拼接在一起之后,作为一个脚本一次执行;后来的比特币协议中,会将两个脚本分别执行,先执行B转给C的输入脚本,再执行A转给B的输出脚本,最后的执行结果没有出错,且栈顶的结果为非0值(即true)则验证通过。
如果一个交易中包含多个输入、输出,则每个输入和它对应来源的输出的脚本都验证通过后,才证明这个交易是合法的。
输入输出脚本形式
比特币使用的脚本是非常简单的,唯一能够访问的内存空间就是一个堆栈,不像其他语言那样有全局变量、局部变量、可动态分配的内存空间。所以比特币脚本语言也称为基于栈的语言。
比特币中使用的脚本语言是比较简单的,甚至连固定的名字都没有,被称为BitCoin Script Language。
比特币脚本语言不支持循环等功能。不支持循环也就不会有死循环,不用担心停机问题(halting problem)。而像以太坊使用的智能合约语言,是图灵完备的,表达能力很强,就需要使用“汽油费机制”来防止程序陷入死循环。
比特币脚本语言虽然在有些方面功能很有限,但是在和密码学相关的功能是很强大的。例如使用一条语句OP_CHECKMULTISIG就可以完成检查多重签名。
下面的示例中,操作符省略了OP_前缀,例如OP_DUP写成了DUP、OP_CHECKSIG写成了CHECKSIG。
P2PK
P2PK(Pay to Public Key):最简的形式,直接在输出脚本中给出收款人公钥
输入脚本:
●PUSHDATA(Sig):将Sig入栈。Sig是付款人用自己的私钥对整个交易加密生成的签名
输出脚本:
●PUSHDATA(PubKey):将PubKey入栈。PubKey是收款人的公钥
●CHECKSIG:检查签名
那么,A转给B,B再转给C时,验证B转给C交易合法性的脚本形式就是,将本交易的输入脚本、上一个交易的输出脚本拼接在一起:
1.PUSHDATA(Sig):将B再转给C交易的付款人B的签名压入栈
2.PUSHDATA(PubKey):将A转给B交易的收款人(还是B)的公钥压入栈
3.CHECKSIG:弹出栈顶的两个元素(公钥、签名),验证签名是否合法。如果相等则入栈TRUE
4.如果程序全程无报错,且栈顶的结果为非0值(即true),验证通过

P2PKH
P2PKH(Pay to Public Key Hash):输出脚本中没有直接给出收款人的公钥,而是给出了收款人公钥的Hash,在下一次交易时在输入脚本中给出公钥。最常用的形式。
输入脚本:
●PUSHDATA(Sig)
●PUSHDATA(PubKey)
输出脚本:
●DUP
●HASH160
●PUSHDATA(PubKeyHash)
●EQUALVERIFY
●CHECKSIG
将本交易的输入脚本、上一个交易的输出脚本拼接在一起:
1.PUSHDATA(Sig):将签名压入栈
2.PUSHDATA(PubKey):将公钥压入栈
3.DUP:将栈顶的元素复制一份
4.HASH160:将栈顶元素弹出来求hash,然后将得到的hash再压入栈
5.PUSHDATA(PubKeyHash):将输出脚本中提供的公钥哈希压入栈
6.EQUALVERIFY:弹出栈顶的两个元素,比较是否相等。即验证栈顶的两个哈希是否相等。如果不等则直接退出
7.CHECKSIG:弹出栈顶的两个元素(公钥、签名),验证签名是否合法。如果相等则入栈TRUE
8.如果程序全程无报错,且栈顶的结果为非0值(即true),验证通过

实例:

P2SH
P2SH(Pay to Script Hash):输出脚本不是收款人公钥或收款人公钥哈希,而是收款人提供的一个脚本的哈希,这个脚本叫redeemScript(赎回脚本)。将来要花这笔钱时,输入中需要给出赎回脚本的内容,同时还要给出让这个赎回脚本可以正确运行的签名。最复杂的一个。
P2SH在早起的比特币版本中没有该功能,后来通过软分叉的形式添加了进去。它的最常见的场景是对多重签名的支持。
输入脚本:
●……
●PUSHDATA(Sig)
●…….
●PUSHDATA(serialized redeemScript)
输出脚本:
●HASH160
●PUSHDATA(redeemScriptHash)
●EQUAL
输入脚本要给出一些签名(数目不定),以及一段序列化的redeemScript。验证分为两步:
1.验证序列化的redeemScript是否与输出脚本中的哈希值相匹配
2.反序列化redeemScript,将其当做操作指令执行一遍,验证输入脚本给出的签名是否正确
redeemScript的形式:
●P2PK形式
●P2PKH形式
●多重签名形式
用P2SH来实现P2PK
赎回脚本(redeemScript):
●PUSHDATA(PubKey)
●CHECKSIG
输入脚本:
●PUSHDATA(Sig)
●PUSHDATA(serialized redeemScript)
输出脚本:
●HASH160
●PUSHDATA(redeemScriptHash)
●EQUAL
第一阶段的验证:(同样的,将输入脚本和输出脚本拼接在一起)
1.PUSHDATA(Sig):将输入脚本的签名入栈
2.PUSHDATA(serialized redeemScript):将输入脚本中的赎回脚本入栈
3.HASH160:将赎回脚本取哈希
4.PUSHDATA(redeemScriptHash):将输出脚本中给出的赎回脚本哈希值入栈
5.EQUAL:对比两个哈希值是否相等。如果不相等则直接退出

第二阶段验证:
6.反序列化serialized redeemScript,还原成可执行脚本,然后执行该脚本
7.PUSHDATA(PubKey):将输入脚本的公钥入栈
8.CHECKSIG:验证签名

比特币多重签名
比特币的一个输出中,可能有些场景需要有多个人的签名才可以进行交易。多重签名是P2SH形式的脚本最常见的一种使用场景。
例如一个公司账户,需要进行转账时需要5个高管中任意3个高管的签名才可以交易。
这样也是一种对私钥泄漏的保护,假如一个高管的私钥被泄漏了,也不能把账户中的资产盗走,还需要2个高管的私钥才可以。
同时也是对私钥丢失的一种冗余,假如5个高管中有2个人忘记了自己的私钥,使用剩余3个人的私钥依然可以进行交易,将资产转到一个新的安全的账户。
早期多重签名
最早的多重签名(已不推荐使用)。
输入脚本:
●x:往栈中压入一个没用的元素。
●PUSHDATA(Sig_1):给出第一个签名
●PUSHDATA(Sig_2):给出第二个签名
●……
●PUSHDATA(Sig_M):给出第M个签名
多重签名时,需要往栈中压入一个没用的元素(即第一步的x):比特币中CHECKMULTISIG的代码实现有一个bug,执行时会从堆栈中多弹出一个元素。因为比特币是一个去中心化系统,如果想通过软件升级的方式修复该bug,需要做硬分叉,代价是很大的,所以该bug无法进行修复。为了迁就代码中的这个bug,所以需要往堆栈上多压入一个没有实际作用的元素。
输出脚本:
●M:花掉该输出时需要的签名个数
●PUSHDATA(pubKey_1):第一个公钥
●PUSHDATA(pubkey_2):第二个公钥
●……
●PUSHDATA(pubkey_N):第N个公钥
●N:公钥的个数
●CHECKMULTISIG:检查多重签名
多重签名时,输入脚本中给出的这M个签名的顺序,需要和它们在输出脚本中的公钥顺序保持一致。例如3个签名中给2个即可交易的脚本中,输出脚本中的公钥顺序为:pubkey_1、pubkey_2、pubkey_3,输入脚本中给出1和3的签名时,顺序就需要保持为:Sig_1、Sig_3,不能颠倒。
将本交易的输入脚本、上一个交易的输出脚本拼接在一起,进行验证。
示例3个签名中给出2个签名即可交易(N=3,M=2):
1.FALSE:将FLASE(可以为任意值)压入栈,无实际意义,只是为了迁就CHECKMULTISIG代码的bug
2.PUSHDATA(Sig_1):将账户1的签名压入栈
3.PUSHDATA(Sig_2):将账户2的签名压入栈
4.2:将花掉该输出需要的签名个数2压入栈
5.PUSHDATA(pubkey_1):将账户1的公钥压入栈
6.PUSHDATA(pubkey_2):将账户2的公钥压入栈
7.PUSHDATA(pubkey_3):将账户3的公钥压入栈
8.3:将公钥数量3压入栈
9.CHECKMULTISIG:检查多重签名

该种签名在实际中不常用,因为把复杂性暴露给了用户:当用户需要交易购买商品时,有的商户需要的是3个签名检查2个,有的商户需要6个签名检查3个,这样对用户生成转账交易非常不方便。
使用P2SH实现多重签名
使用P2SH实现多重签名,它的本质是将复杂度从输出脚本转移到了输入脚本。
输出脚本中只需要给出赎回脚本的哈希即可,原来的复杂度转移到了赎回脚本中。赎回脚本需要给出M的值、N的值、N个公钥。这个赎回脚本是在输入脚本中提供的,也就是说这个赎回脚本是由收款人商家提供的。
这样,店商在网站上公布自己的赎回脚本的哈希值,用户付款时,只需要将这个赎回脚本的哈希值放到自己的输出脚本中即可。
至于这个店商是使用5选3还是6选4或其他的签名规则,对于用户是不可见的,用户无需关心。从用户角度看,使用这种方式,和使用P2PKH没有多大区别,只是将原本公钥的哈希值换成了赎回脚本的哈希值而已。虽然输出脚本的内容和P2PKH稍有不同,但不是本质性的。
输入脚本是店商在花掉这笔输出时提供的,其中包含:赎回脚本的序列化版本、让这个赎回脚本验证通过所需的M个签名。
将来如果这个店商的签名验证策略从6选4变成了5选2,只需要修改店商自己的输入脚本、赎回脚本,然后将新的赎回脚本的哈希值公布出去即可。对用户来说,只不过需要修改包含的赎回脚本的哈希值,其他的无需知道。
输入脚本:
●X
●PUSHDATA(Sig_1)
●PUSHDATA(Sig_2)
●……
●PUSHDATA(Sig_M)
●PUSHDATA(serialized RedeemScript)
输出脚本:
●HASH160
●PUSHDATA(RedeemScriptHash)
●EQUAL
赎回脚本RedeemScript:
●M
●PUSHDATA(pubkey_1)
●PUSHDATA(pubkey_2)
●…….
●PUSHDATA(pubkey_N)
●N
●CHECKMULTISIG
3个公钥选2个签名的最终的验证过程示例(输入脚本、反序列化执行赎回脚本、输出脚本):
第一阶段验证:
1.FALSE
2.PUSHDATA(Sig_1)
3.PUSHDATA(Sig_2)
4.PUSHDATA(serialized RedeemScript)
5.HASH160
6.PUSHDATA(RedeemScriptHash)
7.EQUAL

第二阶段验证(赎回脚本展开后验证):
8.2
9.PUSHDATA(pubkey_1)
10.PUSHDATA(pubkey_2)
11.PUSHDATA(pubkey_3)
12.3
13.CHECKMULTISIG

Proof of Burn销毁比特币
输出脚本:
●…….
●RETURN
●……..
这种形式的输出脚本被称为:Provably Unspendable/Prunable Outputs。
在这个输出脚本中,会包含一个RETURN操作,而RETURN操作会无条件的返回错误。所以无论该输出脚本的RETURN后面还有什么指令,都不会进行执行。如果输入脚本指向这个输出,则不论输入脚本如何设计,都会因为该输出脚本中的RETURN出错而返回false,所以这个输出无法再被花出去,其对应的UTXO也就可以被剪枝了,无需保存。
这种脚本是证明销毁比特币的一种方法。一般有两种使用场景:
●有一些小币种,要求销毁一定数量的比特币才能得到该币种。这种小币种被称为Alt Coin(Alternative Coin)。除了比特币之外的其他加密货币都可以被称为Alt Coin,例如有些币种规定销毁一个比特币可以得到1000个该种加密货币。需要用这种销毁比特币的方式来证明自己付出的代价。
●可以向RETURN后面写入一些永久保存的内容。因为RETURN后面的内容不会被执行,且写入区块链中的内容无法被篡改,所以可以向RETURN后面写入一些自己想写的内容。例如可以当做digital commitment用来保护自己的知识产权,将自己需要保护的知识的内容取哈希值,然后写到RETURN语句的后面,将来如果发生了知识产权纠纷,可以将自己的知识内容再次取哈希和RETURN后面的哈希值对比,来证明自己曾经在这个时间已经发现了这个知识。
区块链的区块中,铸币交易的CoinBase域内也可以当做digital commitment写入任意内容。但是想要在CoinBase中写内容,必须要挖矿得到记账权。
而只要有比特币的账户,都可以通过销毁很少一点比特币的形式在输出脚本中的RETURN后面写入任意内容。
销毁0个比特币
有些交易虽然写了RETURN,但是实际上没有销毁比特币(销毁比特币的金额为0),而是支付了交易费。
示例1:CoinBase Transaction销毁0个比特币。
该交易有两个输出:
●第一个输出是正常的block reward+transaction
●第二个输出的金额是0,形式就是RETURN+任意自定义内容,第二个输出的作用就是向区块链中写入一些内容。

示例2:转账交易销毁0个比特币
该交易的输入金额是0.05 BTC,输出金额是0 BTC,说明输入的金额全部用于支付交易费了。
该交易的输出脚本是RETURN+任意自定义内容,但是这笔交易其实销毁比特币的个数为0,只不过将输入中的比特币作为交易费全部转给了挖到矿的矿工了。

温馨提示:仅提供区块链&数字货币平台信息分享服务,所有产品及展示信息均来源于发行方或者互联网。炒币属于投资行为,不等同于银行存款。市场有风险,投资需谨慎。投资虚拟货币有极大的风险,本网站提供的任何信息都不构成投资建议、财务咨询、交易咨询,或任何其他建议的依据,领域OK并不推荐您购买、售出或持有任何虚拟货币。在做出任何投资决定前,请先充分衡量风险。如有损失,请自行承担后果。






