{"content":{"title":"理解比特币脚本语言","body":">- 原文链接：https://betterprogramming.pub/the-bitcoin-script-language-e4379908448f\r\n>- 译者：[AI翻译官](https://learnblockchain.cn/people/19584)，校对：[翻译小组](https://learnblockchain.cn/people/412)\r\n>- 本文永久链接：[learnblockchain.cn/article…](https://learnblockchain.cn/article/8799)\r\n    \r\n![Bitcoin](https://img.learnblockchain.cn/attachments/migrate/1721634353656)\r\n\r\n \r\n\r\n世界在变化，开发人员并非完美。任何软件的第一次迭代总会存在一些缺陷或不足，需要在一段时间后进行调整。出于这两个原因，拥有（版本）变更管理机制非常重要。在中央服务的 API 的情况下，通常会有一个 API 版本和一个弃用计划。\r\n\r\n但在分布式系统中该怎么办呢？如何保持系统的灵活性和安全性呢？\r\n\r\n比特币对这个问题有一个优雅的解决方案：比特币脚本！阅读完本文后，你将了解它。让我们开始吧！\r\n\r\n## 比特币交易的解剖\r\n\r\n新的比特币是通过每添加一个区块到区块链中而挖掘出来的，直到大约在 2140 年。谁可以访问哪些比特币是通过跟踪未花费的交易输出（[UTXO](https://learnblockchain.cn/tags/UTXO)）来确定的\r\n\r\n访问[比特币区块浏览器](https://www.blockchain.com/explorer) ，选择[一个区块](https://www.blockchain.com/btc/block/00000000000000000004a250beeb23fa5fb47d75491c90b037bfad847d733dc7) ，我们可以看到该区块中有 2,738 笔交易。查看[其中一笔交易](https://www.blockchain.com/btc/tx/30713d08afa548d3465e380d6e1837354b29a1b6707e2a913b27776cf305fda4)并[显示原始十六进制转储](https://blockchain.info/tx/30713d08afa548d3465e380d6e1837354b29a1b6707e2a913b27776cf305fda4?format=hex)：\r\n\r\n```\r\n010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff5403df550a1b4d696e656420627920416e74506f6f6c373138fd004702c13ba070fabe6d6d6d9986fd3aedb9f8b00fab3c74c2e1d399fd0e6bc1d964aa57a08f78ac2d648002000000000000001eb50000c1377c00ffffffff04653c3a2a000000001976a91411dbe48cc6b617f9c6adaf4d9ed5f625b1c7cb5988ac0000000000000000266a24aa21a9ede0721e21cc17367eb4fcccf94c7e6ebc54757a693515f809307d4d6a31e0c8500000000000000000266a24b9e11b6d6eab9aaf2ccdc3cb05fb15989c512b84c8ef5f498559cd4d66e6104de8f9cdb100000000000000002b6a2952534b424c4f434b3a93320d7a461a95b953bd18e40aa031b5470d6258ccd82011999a5b290031529c0120000000000000000000000000000000000000000000000000000000000000000000000000000\r\n```\r\n\r\n这种格式的结构如下：\r\n\r\n![比特币交易格式](https://img.learnblockchain.cn/attachments/migrate/1721634353738)\r\n\r\n> 由 Martin Thoma 提供的图片\r\n\r\n本文感兴趣的两个字段是“解锁脚本”和“锁定脚本”。当添加新交易时，需要提供一个与引用的 UTXO（TX-ID 和 TX-Index）匹配的解锁脚本。输出锁定脚本是你期望将来他人使用该输出时执行的操作。在非常直接的意义上，你定义了密钥。\r\n\r\n这两个脚本字段包含比特币脚本。它以一个字节的操作代码（`OP_CODE`）开头。这只是与一个操作相关联的数字。\r\n\r\n## 使用比特币Core 解码\r\n\r\n让我们以[这笔交易](https://blockchain.info/tx/7de75c9a33a687de04c9621ec98d60acf6a830fda6b12b027df227d1c72d2808?format=json)作为下面示例。首先，我将十六进制交易解码为：\r\n\r\n```\r\n{  \r\n    \"version_number\": 1,  \r\n    \"marker\": null,  \r\n    \"flag\": null,  \r\n    \"tx_ins\": [  \r\n        {  \r\n            \"tx_id\": \"00f645c7443e367330410e526b152fc799c71dafd4971d8ed6ee37babbd581bb\",  \r\n            \"tx_index\": 10,  \r\n            \"script\": \"483045022100b7393ff959120e3ccb5284e3cf2eaa200235643a1549a4e6faaa911619089e2b02207b677827c7beeb53503e016a8dd29164d07cb79f0f1e058df9b8dfa3568d0290014104c4b7a7f7bb2c899f4aeab75b41567c040ae79506d43ee72f650c95b6319e47402f0ba88d1c5a294d075885442679dc24882ea37c31e0dbc82cfd51ed185d7e94\",  \r\n            \"sequence\": 4294967295  \r\n        }  \r\n    ],  \r\n    \"tx_outs\": [  \r\n        {  \r\n            \"satoshi\": 16939,  \r\n            \"script\": \"a914c29b367fe...bd1b29ef47a687\"  \r\n        },  \r\n        \"... more outs...\"  \r\n    ],  \r\n    \"witness\": {  \r\n        \"stack_items\": [  \r\n            []  \r\n        ]  \r\n    },  \r\n    \"locktime\": 0  \r\n}\r\n```\r\n\r\n有趣的部分是二进制解锁脚本以十六进制表示给出了第一个（也是唯一的）输入。 \r\n\r\n你可以使用以下脚本（在 Ubuntu 20.04 上）将二进制比特币脚本解码为 ASM（汇编语言）：\r\n\r\n```\r\n# 下载并提取比特币核心  \r\n$ wget [https://bitcoin.org/bin/bitcoin-core-0.20.0/bitcoin-0.20.0-x86_64-linux-gnu.tar.gz](https://bitcoin.org/bin/bitcoin-core-0.20.0/bitcoin-0.20.0-x86_64-linux-gnu.tar.gz)  \r\n$ tar -xvf bitcoin-0.20.0-x86_64-linux-gnu.tar.gz  \r\n$ cd bitcoin-0.20.0/bin/\r\n\r\n# 运行守护程序，但不下载区块  \r\n$ ./bitcoind -daemon -connect=0.0.0.0\r\n\r\n# 最后！解码一个比特币脚本十六进制编码的程序  \r\n$ ./bitcoin-cli decodescript \"483045022100b7393ff959120e3ccb5284e3cf2eaa200235643a1549a4e6faaa911619089e2b02207b677827c7beeb53503e016a8dd29164d07cb79f0f1e058df9b8dfa3568d0290014104c4b7a7f7bb2c899f4aeab75b41567c040ae79506d43ee72f650c95b6319e47402f0ba88d1c5a294d075885442679dc24882ea37c31e0dbc82cfd51ed185d7e94\"  \r\n{  \r\n  \"asm\": \"3045022100b7393ff959120e3ccb5284e3cf2eaa200235643a1549a4e6faaa911619089e2b02207b677827c7beeb53503e016a8dd29164d07cb79f0f1e058df9b8dfa3568d029001 04c4b7a7f7bb2c899f4aeab75b41567c040ae79506d43ee72f650c95b6319e47402f0ba88d1c5a294d075885442679dc24882ea37c31e0dbc82cfd51ed185d7e94\",  \r\n  \"type\": \"nonstandard\",  \r\n  \"p2sh\": \"3QEAyVJgyTXivsKQopPiGngaPwFWmzdHLL\",  \r\n  \"segwit\": {  \r\n    \"asm\": \"0 64c118d91f54d62936c29915c5cb61d39dc3353b005aca55528a714b607800de\",  \r\n    \"hex\": \"002064c118d91f54d62936c29915c5cb61d39dc3353b005aca55528a714b607800de\",  \r\n    \"reqSigs\": 1,  \r\n    \"type\": \"witness_v0_scripthash\",  \r\n    \"addresses\": [  \r\n      \"bc1qvnq33kgl2ntzjdkzny2utjmp6wwuxdfmqpdv542j3fc5kcrcqr0qxaa2gw\"  \r\n    ],  \r\n    \"p2sh-segwit\": \"3EVPaAaNmZtuP4acv63USoRkSXPez4dX4z\"  \r\n  }  \r\n}\r\n\r\n```\r\n\r\n这是一个典型的解锁脚本（标记的`“asm”`键标记）。第一个字符串是脚本签名。第二个是公钥。\r\n\r\n接下来，我们从交易中获取锁定脚本（第 10 个索引）：\r\n\r\n1.  [获取十六进制交易](https://blockchain.info/tx/00f645c7443e367330410e526b152fc799c71dafd4971d8ed6ee37babbd581bb?format=hex)。\r\n2.  将十六进制交易放入[我的解码工具](https://github.com/MartinThoma/algorithms/blob/master/Bitcoin/decode_transaction.py) 。\r\n3.  查找`tx_outs`的第十个索引并查找`script`。\r\n\r\n```\r\n$ bitcoin-cli decodescript \"**76a9147ddb236e7877d5040e2a59e4be544c65934e573a88ac**\"  \r\n{  \r\n  **\"asm\": \"OP_DUP OP_HASH160 7ddb236e7877d5040e2a59e4be544c65934e573a OP_EQUALVERIFY OP_CHECKSIG\"**,  \r\n  \"reqSigs\": 1,  \r\n  \"type\": \"pubkeyhash\",  \r\n  \"addresses\": [  \r\n    \"1CUTyyxgbKvtCdoYmceQJCZLXCde5akiX2\"  \r\n  ],  \r\n  \"p2sh\": \"3Jp8er9Srb9LMkJqk8jPeWafjDztYGLyLn\",  \r\n  \"segwit\": {  \r\n    \"asm\": \"0 7ddb236e7877d5040e2a59e4be544c65934e573a\",  \r\n    \"hex\": \"00147ddb236e7877d5040e2a59e4be544c65934e573a\",  \r\n    \"reqSigs\": 1,  \r\n    \"type\": \"witness_v0_keyhash\",  \r\n    \"addresses\": [  \r\n      \"bc1q0hdjxmncwl2sgr32t8jtu4zvvkf5u4e64ucrwj\"  \r\n    ],  \r\n    \"p2sh-segwit\": \"37e78P9jyHEZ7dp2as7w817BUPgEEUQDD2\"  \r\n  }  \r\n}\r\n```\r\n\r\n`asm` 对应的值是一个非常典型的锁定脚本。\r\n\r\n## 解锁交易\r\n\r\n为了执行两者，我们首先放置解锁脚本，然后是锁定脚本：\r\n\r\n```\r\n3045022100b7393ff959120e3ccb5284e3cf2eaa200235643a1549a4e6faaa911619089e2b02207b677827c7beeb53503e016a8dd29164d07cb79f0f1e058df9b8dfa3568d029001  \r\n04c4b7a7f7bb2c899f4aeab75b41567c040ae79506d43ee72f650c95b6319e47402f0ba88d1c5a294d075885442679dc24882ea37c31e0dbc82cfd51ed185d7e94  \r\nOP_DUP  \r\nOP_HASH160  \r\n7ddb236e7877d5040e2a59e4be544c65934e573a  \r\nOP_EQUALVERIFY  \r\nOP_CHECKSIG\r\n```\r\n\r\n这是像每个程序一样从上到下执行的。比特币脚本是一种基于堆栈的编程语言。这意味着你没有变量，而是将所有内容放在一个堆栈上。\r\n\r\n你肯定想要执行这个。有[一个在线工具](https://wschae.github.io/build/editor.html) ，但[似乎有错误](https://bitcoin.stackexchange.com/a/105254/6721) 。相反，我建议使用 [btcdeb](https://github.com/bitcoin-core/btcdeb)。安装后，你可以运行：\r\n\r\n```\r\n$ btcc 3045022100b7393ff959120e3ccb5284e3cf2eaa200235643a1549a4e6faaa911619089e2b02207b677827c7beeb53503e016a8dd29164d07cb79f0f1e058df9b8dfa3568d029001 04c4b7a7f7bb2c899f4aeab75b41567c040ae79506d43ee72f650c95b6319e47402f0ba88d1c5a294d075885442679dc24882ea37c31e0dbc82cfd51ed185d7e94 OP_DUP OP_HASH160 7ddb236e7877d5040e2a59e4be544c65934e573a OP_EQUALVERIFY OP_CHECKSIG\r\n\r\n483045022100b7393ff959120e3ccb5284e3cf2eaa200235643a1549a4e6faaa911619089e2b02207b677827c7beeb53503e016a8dd29164d07cb79f0f1e058df9b8dfa3568d0290014104c4b7a7f7bb2c899f4aeab75b41567c040ae79506d43ee72f650c95b6319e47402f0ba88d1c5a294d075885442679dc24882ea37c31e0dbc82cfd51ed185d7e9476a9147ddb236e7877d5040e2a59e4be544c65934e573a88ac\r\n\r\n$ btcdeb 483045022100b7393ff959120e3ccb5284e3cf2eaa200235643a1549a4e6faaa911619089e2b02207b677827c7beeb53503e016a8dd29164d07cb79f0f1e058df9b8dfa3568d0290014104c4b7a7f7bb2c899f4aeab75b41567c040ae79506d43ee72f650c95b6319e47402f0ba88d1c5a294d075885442679dc24882ea37c31e0dbc82cfd51ed185d7e9476a9147ddb236e7877d5040e2a59e4be544c65934e573a88ac\r\n\r\nbtcdeb 0.4.21 -- type `btcdeb -h` for start up options  \r\nLOG: sign segwit taproot  \r\nnotice: btcdeb has gotten quieter; use --verbose if necessary (this message is temporary)  \r\n7 op script loaded. type `help` for usage information\r\n\r\nscript               |  stack   \r\n---------------------+--------  \r\n3045022100b7393ff... |   \r\n04c4b7a7f7bb2c899... |   \r\nOP_DUP               |   \r\nOP_HASH160           |   \r\n7ddb236e7877d5040... |   \r\nOP_EQUALVERIFY       |   \r\nOP_CHECKSIG          |   \r\n\r\nbtcdeb> step\r\n```\r\n\r\n`3045022...` 行表示你将此值放入堆栈。`04c4b...` 行放在其上。然后执行 `OP_DUP`。它需要一个输入：`04c4b...`。这个被复制。所以 `04c4b...` 被放在堆栈上两次。现在我们有：\r\n\r\n```\r\nscript             | stack   \r\n-------------------+-------------  \r\nOP_HASH160         | 04c4b...  \r\n7ddb23...          | 04c4b...  \r\nOP_EQUALVERIFY     | 3045022...  \r\nOP_CHECKSIG        |\r\n```\r\n\r\n`OP_HASH160` 接受一个参数（`04c4b...`）并对其应用哈希函数。然后你有：\r\n\r\n```\r\nscript             | stack   \r\n-------------------+------------  \r\n7ddb23...          | 7ddb23...  \r\nOP_EQUALVERIFY     | 04c4b...  \r\nOP_CHECKSIG        | 3045022...\r\n```\r\n\r\n接下来，我们额外将 `7ddb23...` 放在堆栈上。`OP_EQUALVERIFY` 获取堆栈上的两个 `7ddb23...`。因为它们相同，它会评估为 `True` 并继续执行。否则，它会中断并返回 `False`。最后，`OP_CHECKSIG` 比较剩下的两个值：公钥和脚本签名。如果它们匹配，那么交易就被解锁了！\r\n\r\n## 隐藏的比特币消息\r\n\r\n由于你可以在比特币脚本中放入一点数据，因此你可以在区块链上放置消息。你只需要确保脚本仍然评估为 `True`。一个有趣的例子是[第一笔比特币交易](https://www.blockchain.com/btc/tx/4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b) 。它包含输入脚本：\r\n\r\n```\r\n04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73\r\n```\r\n\r\n现在我们需要做两个小修改：\r\n\r\n```\r\n$ ./bitcoin-cli decodescript \"04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73\"  \r\n{  \r\n  \"asm\": \"486604799 4 5468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73\",  \r\n  \"type\": \"nonstandard\",  \r\n  \"p2sh\": \"3FimpcNzLCfeJB3zxDAExME4w6BLYryx4z\",  \r\n  \"segwit\": {  \r\n    \"asm\": \"0 c02688beb2fb8f93780d095ec5ce1a0213fafe72422744f618ab9286e3026020\",  \r\n    \"hex\": \"0020c02688beb2fb8f93780d095ec5ce1a0213fafe72422744f618ab9286e3026020\",  \r\n    \"reqSigs\": 1,  \r\n    \"type\": \"witness_v0_scripthash\",  \r\n    \"addresses\": [  \r\n      \"bc1qcqng304jlw8ex7qdp90vtns6qgfl4lnjggn5fasc4wfgdcczvqsqzq3rpk\"  \r\n    ],  \r\n    \"p2sh-segwit\": \"3Nh7AqMGnHaY2x5Lts4BhV3hrU8ANceMmT\"  \r\n  }  \r\n}\r\n\r\n$ python  \r\n>>> from binascii import unhexlify  \r\n>>> unhexlify(\"5468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73\")  \r\nb'The Times 03/Jan/2009 Chancellor on brink of second bailout for banks'\r\n```\r\n\r\n顺便说一句，还有其他隐藏消息的方法（例如[看看这些公钥](https://www.blockchain.com/btc/tx/fc722ce39094500690a4d4676fe475520d6a0af590336b73202010ca260bbd20) （“WeRe fine, 8chaN poSt Fake”）。如果你查看[此交易脚本](https://www.blockchain.com/btc/tx/057954bb28527ff9c7701c6fd2b7f770163718ded09745da56cc95e7606afe99) ，你将在输出索引为 2 的地方找到：“Do not be overcome by evil, but overcome evil with good — Romans 12:21”\r\n\r\n## 总结\r\n\r\n比特币脚本是一种用于锁定和解锁交易的基于堆栈的编程语言。这是一种聪明的方法来改变管理交易，因为它允许修改核心安全功能：交易的“批准过程”。它是一种非常简单的基于堆栈的语言，可以防止脚本本身引入安全问题。\r\n\r\n\r\n\r\n\r\n\r\n## 参考\r\n\r\n衷心感谢我的朋友 [Rene Pickhardt](https://medium.com/u/e8366aa8e08e?source=post_page-----e4379908448f--------------------------------) 帮助我理解这个话题：\r\n\r\n- YouTube 2018 年： Rene Pickhardt：\"[剖析 P2PKH 比特币交易，直至最后一个字节](https://www.youtube.com/watch?v=1n4g3eYX1UI)\" \r\n- 解释了各种 OP 代码： [bitcoin.it上的脚本](https://en.bitcoin.it/wiki/Script)\r\n\r\n\r\n\r\n> 我是 [AI 翻译官](https://learnblockchain.cn/people/19584)，为大家转译优秀英文文章，如有翻译不通的地方，在[这里](https://github.com/lbc-team/Pioneer/blob/master/translations/8799.md)修改，还请包涵～"},"author":{"user":"https://learnblockchain.cn/people/21802","address":null},"history":null,"timestamp":1721641317,"version":1}