理解比特币脚本语言

理解比特币脚本语言

Bitcoin

世界在变化,开发人员并非完美。任何软件的第一次迭代总会存在一些缺陷或不足,需要在一段时间后进行调整。出于这两个原因,拥有(版本)变更管理机制非常重要。在中央服务的 API 的情况下,通常会有一个 API 版本和一个弃用计划。

但在分布式系统中该怎么办呢?如何保持系统的灵活性和安全性呢?

比特币对这个问题有一个优雅的解决方案:比特币脚本!阅读完本文后,你将了解它。让我们开始吧!

比特币交易的解剖

新的比特币是通过每添加一个区块到区块链中而挖掘出来的,直到大约在 2140 年。谁可以访问哪些比特币是通过跟踪未花费的交易输出(UTXO)来确定的

访问比特币区块浏览器 ,选择一个区块 ,我们可以看到该区块中有 2,738 笔交易。查看其中一笔交易显示原始十六进制转储

010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff5403df550a1b4d696e656420627920416e74506f6f6c373138fd004702c13ba070fabe6d6d6d9986fd3aedb9f8b00fab3c74c2e1d399fd0e6bc1d964aa57a08f78ac2d648002000000000000001eb50000c1377c00ffffffff04653c3a2a000000001976a91411dbe48cc6b617f9c6adaf4d9ed5f625b1c7cb5988ac0000000000000000266a24aa21a9ede0721e21cc17367eb4fcccf94c7e6ebc54757a693515f809307d4d6a31e0c8500000000000000000266a24b9e11b6d6eab9aaf2ccdc3cb05fb15989c512b84c8ef5f498559cd4d66e6104de8f9cdb100000000000000002b6a2952534b424c4f434b3a93320d7a461a95b953bd18e40aa031b5470d6258ccd82011999a5b290031529c0120000000000000000000000000000000000000000000000000000000000000000000000000000

这种格式的结构如下:

比特币交易格式

由 Martin Thoma 提供的图片

本文感兴趣的两个字段是“解锁脚本”和“锁定脚本”。当添加新交易时,需要提供一个与引用的 UTXO(TX-ID 和 TX-Index)匹配的解锁脚本。输出锁定脚本是你期望将来他人使用该输出时执行的操作。在非常直接的意义上,你定义了密钥。

这两个脚本字段包含比特币脚本。它以一个字节的操作代码(OP_CODE)开头。这只是与一个操作相关联的数字。

使用比特币Core 解码

让我们以这笔交易作为下面示例。首先,我将十六进制交易解码为:

{  
    "version_number": 1,  
    "marker": null,  
    "flag": null,  
    "tx_ins": [  
        {  
            "tx_id": "00f645c7443e367330410e526b152fc799c71dafd4971d8ed6ee37babbd581bb",  
            "tx_index": 10,  
            "script": "483045022100b7393ff959120e3ccb5284e3cf2eaa200235643a1549a4e6faaa911619089e2b02207b677827c7beeb53503e016a8dd29164d07cb79f0f1e058df9b8dfa3568d0290014104c4b7a7f7bb2c899f4aeab75b41567c040ae79506d43ee72f650c95b6319e47402f0ba88d1c5a294d075885442679dc24882ea37c31e0dbc82cfd51ed185d7e94",  
            "sequence": 4294967295  
        }  
    ],  
    "tx_outs": [  
        {  
            "satoshi": 16939,  
            "script": "a914c29b367fe...bd1b29ef47a687"  
        },  
        "... more outs..."  
    ],  
    "witness": {  
        "stack_items": [  
            []  
        ]  
    },  
    "locktime": 0  
}

有趣的部分是二进制解锁脚本以十六进制表示给出了第一个(也是唯一的)输入。

你可以使用以下脚本(在 Ubuntu 20.04 上)将二进制比特币脚本解码为 ASM(汇编语言):

# 下载并提取比特币核心  
$ 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)  
$ tar -xvf bitcoin-0.20.0-x86_64-linux-gnu.tar.gz  
$ cd bitcoin-0.20.0/bin/

# 运行守护程序,但不下载区块  
$ ./bitcoind -daemon -connect=0.0.0.0

# 最后!解码一个比特币脚本十六进制编码的程序  
$ ./bitcoin-cli decodescript "483045022100b7393ff959120e3ccb5284e3cf2eaa200235643a1549a4e6faaa911619089e2b02207b677827c7beeb53503e016a8dd29164d07cb79f0f1e058df9b8dfa3568d0290014104c4b7a7f7bb2c899f4aeab75b41567c040ae79506d43ee72f650c95b6319e47402f0ba88d1c5a294d075885442679dc24882ea37c31e0dbc82cfd51ed185d7e94"  
{  
  "asm": "3045022100b7393ff959120e3ccb5284e3cf2eaa200235643a1549a4e6faaa911619089e2b02207b677827c7beeb53503e016a8dd29164d07cb79f0f1e058df9b8dfa3568d029001 04c4b7a7f7bb2c899f4aeab75b41567c040ae79506d43ee72f650c95b6319e47402f0ba88d1c5a294d075885442679dc24882ea37c31e0dbc82cfd51ed185d7e94",  
  "type": "nonstandard",  
  "p2sh": "3QEAyVJgyTXivsKQopPiGngaPwFWmzdHLL",  
  "segwit": {  
    "asm": "0 64c118d91f54d62936c29915c5cb61d39dc3353b005aca55528a714b607800de",  
    "hex": "002064c118d91f54d62936c29915c5cb61d39dc3353b005aca55528a714b607800de",  
    "reqSigs": 1,  
    "type": "witness_v0_scripthash",  
    "addresses": [  
      "bc1qvnq33kgl2ntzjdkzny2utjmp6wwuxdfmqpdv542j3fc5kcrcqr0qxaa2gw"  
    ],  
    "p2sh-segwit": "3EVPaAaNmZtuP4acv63USoRkSXPez4dX4z"  
  }  
}

这是一个典型的解锁脚本(标记的“asm”键标记)。第一个字符串是脚本签名。第二个是公钥。

接下来,我们从交易中获取锁定脚本(第 10 个索引):

  1. 获取十六进制交易
  2. 将十六进制交易放入我的解码工具
  3. 查找tx_outs的第十个索引并查找script
$ bitcoin-cli decodescript "**76a9147ddb236e7877d5040e2a59e4be544c65934e573a88ac**"  
{  
  **"asm": "OP_DUP OP_HASH160 7ddb236e7877d5040e2a59e4be544c65934e573a OP_EQUALVERIFY OP_CHECKSIG"**,  
  "reqSigs": 1,  
  "type": "pubkeyhash",  
  "addresses": [  
    "1CUTyyxgbKvtCdoYmceQJCZLXCde5akiX2"  
  ],  
  "p2sh": "3Jp8er9Srb9LMkJqk8jPeWafjDztYGLyLn",  
  "segwit": {  
    "asm": "0 7ddb236e7877d5040e2a59e4be544c65934e573a",  
    "hex": "00147ddb236e7877d5040e2a59e4be544c65934e573a",  
    "reqSigs": 1,  
    "type": "witness_v0_keyhash",  
    "addresses": [  
      "bc1q0hdjxmncwl2sgr32t8jtu4zvvkf5u4e64ucrwj"  
    ],  
    "p2sh-segwit": "37e78P9jyHEZ7dp2as7w817BUPgEEUQDD2"  
  }  
}

asm 对应的值是一个非常典型的锁定脚本。

解锁交易

为了执行两者,我们首先放置解锁脚本,然后是锁定脚本:

3045022100b7393ff959120e3ccb5284e3cf2eaa200235643a1549a4e6faaa911619089e2b02207b677827c7beeb53503e016a8dd29164d07cb79f0f1e058df9b8dfa3568d029001  
04c4b7a7f7bb2c899f4aeab75b41567c040ae79506d43ee72f650c95b6319e47402f0ba88d1c5a294d075885442679dc24882ea37c31e0dbc82cfd51ed185d7e94  
OP_DUP  
OP_HASH160  
7ddb236e7877d5040e2a59e4be544c65934e573a  
OP_EQUALVERIFY  
OP_CHECKSIG

这是像每个程序一样从上到下执行的。比特币脚本是一种基于堆栈的编程语言。这意味着你没有变量,而是将所有内容放在一个堆栈上。

你肯定想要执行这个。有一个在线工具 ,但似乎有错误 。相反,我建议使用 btcdeb。安装后,你可以运行:

$ btcc 3045022100b7393ff959120e3ccb5284e3cf2eaa200235643a1549a4e6faaa911619089e2b02207b677827c7beeb53503e016a8dd29164d07cb79f0f1e058df9b8dfa3568d029001 04c4b7a7f7bb2c899f4aeab75b41567c040ae79506d43ee72f650c95b6319e47402f0ba88d1c5a294d075885442679dc24882ea37c31e0dbc82cfd51ed185d7e94 OP_DUP OP_HASH160 7ddb236e7877d5040e2a59e4be544c65934e573a OP_EQUALVERIFY OP_CHECKSIG

483045022100b7393ff959120e3ccb5284e3cf2eaa200235643a1549a4e6faaa911619089e2b02207b677827c7beeb53503e016a8dd29164d07cb79f0f1e058df9b8dfa3568d0290014104c4b7a7f7bb2c899f4aeab75b41567c040ae79506d43ee72f650c95b6319e47402f0ba88d1c5a294d075885442679dc24882ea37c31e0dbc82cfd51ed185d7e9476a9147ddb236e7877d5040e2a59e4be544c65934e573a88ac

$ btcdeb 483045022100b7393ff959120e3ccb5284e3cf2eaa200235643a1549a4e6faaa911619089e2b02207b677827c7beeb53503e016a8dd29164d07cb79f0f1e058df9b8dfa3568d0290014104c4b7a7f7bb2c899f4aeab75b41567c040ae79506d43ee72f650c95b6319e47402f0ba88d1c5a294d075885442679dc24882ea37c31e0dbc82cfd51ed185d7e9476a9147ddb236e7877d5040e2a59e4be544c65934e573a88ac

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)  
7 op script loaded. type `help` for usage information

script               |  stack   
---------------------+--------  
3045022100b7393ff... |   
04c4b7a7f7bb2c899... |   
OP_DUP               |   
OP_HASH160           |   
7ddb236e7877d5040... |   
OP_EQUALVERIFY       |   
OP_CHECKSIG          |   

btcdeb> step

3045022... 行表示你将此值放入堆栈。04c4b... 行放在其上。然后执行 OP_DUP。它需要一个输入:04c4b...。这个被复制。所以 04c4b... 被放在堆栈上两次。现在我们有:

script             | stack   
-------------------+-------------  
OP_HASH160         | 04c4b...  
7ddb23...          | 04c4b...  
OP_EQUALVERIFY     | 3045022...  
OP_CHECKSIG        |

OP_HASH160 接受一个参数(04c4b...)并对其应用哈希函数。然后你有:

script             | stack   
-------------------+------------  
7ddb23...          | 7ddb23...  
OP_EQUALVERIFY     | 04c4b...  
OP_CHECKSIG        | 3045022...

接下来,我们额外将 7ddb23... 放在堆栈上。OP_EQUALVERIFY 获取堆栈上的两个 7ddb23...。因为它们相同,它会评估为 True 并继续执行。否则,它会中断并返回 False。最后,OP_CHECKSIG 比较剩下的两个值:公钥和脚本签名。如果它们匹配,那么交易就被解锁了!

隐藏的比特币消息

由于你可以在比特币脚本中放入一点数据,因此你可以在区块链上放置消息。你只需要确保脚本仍然评估为 True。一个有趣的例子是第一笔比特币交易 。它包含输入脚本:

04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73

现在我们需要做两个小修改:

$ ./bitcoin-cli decodescript "04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73"  
{  
  "asm": "486604799 4 5468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73",  
  "type": "nonstandard",  
  "p2sh": "3FimpcNzLCfeJB3zxDAExME4w6BLYryx4z",  
  "segwit": {  
    "asm": "0 c02688beb2fb8f93780d095ec5ce1a0213fafe72422744f618ab9286e3026020",  
    "hex": "0020c02688beb2fb8f93780d095ec5ce1a0213fafe72422744f618ab9286e3026020",  
    "reqSigs": 1,  
    "type": "witness_v0_scripthash",  
    "addresses": [  
      "bc1qcqng304jlw8ex7qdp90vtns6qgfl4lnjggn5fasc4wfgdcczvqsqzq3rpk"  
    ],  
    "p2sh-segwit": "3Nh7AqMGnHaY2x5Lts4BhV3hrU8ANceMmT"  
  }  
}

$ python  
>>> from binascii import unhexlify  
>>> unhexlify("5468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73")  
b'The Times 03/Jan/2009 Chancellor on brink of second bailout for banks'

顺便说一句,还有其他隐藏消息的方法(例如看看这些公钥 (“WeRe fine, 8chaN poSt Fake”)。如果你查看此交易脚本 ,你将在输出索引为 2 的地方找到:“Do not be overcome by evil, but overcome evil with good — Romans 12:21”

总结

比特币脚本是一种用于锁定和解锁交易的基于堆栈的编程语言。这是一种聪明的方法来改变管理交易,因为它允许修改核心安全功能:交易的“批准过程”。它是一种非常简单的基于堆栈的语言,可以防止脚本本身引入安全问题。

参考

衷心感谢我的朋友 Rene Pickhardt 帮助我理解这个话题:

我是 AI 翻译官,为大家转译优秀英文文章,如有翻译不通的地方,在这里修改,还请包涵~

点赞 2
收藏 3
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Martin Thoma
Martin Thoma
江湖只有他的大名,没有他的介绍。