惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Engineering at Meta
Engineering at Meta
Cloudbric
Cloudbric
云风的 BLOG
云风的 BLOG
A
About on SuperTechFans
The GitHub Blog
The GitHub Blog
IT之家
IT之家
F
Full Disclosure
B
Blog RSS Feed
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Hugging Face - Blog
Hugging Face - Blog
B
Blog
H
Help Net Security
The Cloudflare Blog
Recorded Future
Recorded Future
P
Proofpoint News Feed
P
Proofpoint News Feed
C
Cisco Blogs
T
Tailwind CSS Blog
P
Palo Alto Networks Blog
D
Docker
爱范儿
爱范儿
Know Your Adversary
Know Your Adversary
博客园 - 聂微东
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Y
Y Combinator Blog
雷峰网
雷峰网
AWS News Blog
AWS News Blog
D
DataBreaches.Net
博客园 - 司徒正美
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - Franky
C
Cybersecurity and Infrastructure Security Agency CISA
Blog — PlanetScale
Blog — PlanetScale
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Latest news
Latest news
Google DeepMind News
Google DeepMind News
Martin Fowler
Martin Fowler
MongoDB | Blog
MongoDB | Blog
C
CERT Recently Published Vulnerability Notes
阮一峰的网络日志
阮一峰的网络日志
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
C
CXSECURITY Database RSS Feed - CXSecurity.com
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Cyber Attacks, Cyber Crime and Cyber Security
腾讯CDC
小众软件
小众软件
G
Google Developers Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
Scott Helme
Scott Helme
O
OpenAI News

smallyu的博客

为什么买的加密货币一定要去中心化 RISC-V 虚拟机无法代替 EVM 虚拟机 Polkadot 的 Existential Deposit 机制错在哪里 利息,套利,交易策略,金融市场 LayerZero 从来不挑战比特币的地位 默认参数引起的以太坊节点运行错误 硬件钱包与资产安全 预测市场比币圈更赌场 为什么去中心化的跨链桥不可能实现 我对于 AI 时代的答案 不要投资任何隐私币 区块链技术世界的三大真理 以太坊 AA 钱包的致命问题是什么 基于 AI 语义执行的 MCP 区块链的设计 一个集成 Geth 和 CometBFT 的兼容层 我的加密货币定投策略(二) DeFi 基础: 理解 AMM 定价机制 Go 语言 GMP 调度器的原理是什么 Web3 项目分析计划 对 Psyche Network 项目的分析 continuation 教程: 理解 CPS 如何开发一个比特币符文(Runes)协议 我从王垠的计算机科学视频班学到了什么 区块链技术面试题(2025年版) Rust 语言容易让新手困惑的一个“过度优化” Solana 智能合约开发教程 (1) 尝试开发一个最小 EVM 虚拟机 基于 ZK 的链上身份系统设计 一个 Web3 打赏系统的设计 鼓吹 Cursor 的人技术能力都差 关于 Code Review 的礼节 假如启动一个新的以太坊 PoS 网络 发行加密货币的最好方式 所有 BFT 共识的区块链都是中心化的 对 2025 年区块链行业的预测 Restaking 项目的经济难题 如何看懂任意区块链项目的技术架构 为什么不要做以太坊的二次开发 为什么不要做智能合约开发和 DeFi 开发 我的加密货币定投策略(一) PoS 类型的区块链如何处理分叉 Ethereum Casper 为什么需要 EIP-7251 区块链中的 PBFT 不需要第二次投票 开发者的思维方式 发币的核心要点 炒币投资的小 tips 为什么说 PoW 比 PoS 更加去中心化 牛市对普通人来说意味着什么 如何健康地远程工作 为什么比特币不用概率加密函数 程序员的 “服从权威” 心理 区块链技术面试题 如何区分公有链和联盟链 对 Layer 2 项目创业想法的回复 对区块链共识机制的理解 Pebbling Game 鹅卵石游戏 PDP 文件证明的局限性 不要小瞧 ChatGPT 为什么炒币不是一个好主意 一种在区块链上生成随机数的机制 为什么以太坊的私钥计算不可逆 关于以太坊的私钥碰撞 “猜均值的2/3” dApp 游戏设计 Proof of Storage/Space/Replication 的区别 Proofs of Retrievability 文件证明的含义 对 S-PDP 文件证明的示例和解释 我的加密货币交易机器人 对区块链行业的见闻 随机确认块的共识机制 VRF + BFT 共识引起交易失败的问题 为什么要重视编程思想 对 Web 3.0 的理解 GitBook 好用吗? 一种区块链节点存储扩容的方式 一种基于“自我中心主义”的共识机制 链表常见算法题及解析 基于 Multi-Linked List 的区块链设想 理解哈希函数与序列化 联盟链比公有链差在哪儿 在 Dijkstra 算法中保存路径 为什么数字货币使用区块链是政治问题 区块链:下一代数字身份认证体系的基石 网页技术能实现 3D 建模吗? 给区块链一个定义 从 Erlang 开始了解 Actor 模型 一种侧边导航栏的交互方式 Rust 的 ownership 是什么? Haskell 中的 Monad 是什么? 浅析 Libra 背后的区块链技术 对区块链的理性认识 Rust 基础语法概述 基于 Java 的爬虫框架 WebCollector Kotlin:简化版的 Scala JavaScript 有关联数组吗? 主流编程语言的异常处理机制 Go 语言基本语法 Scala 语法基础 用 Scala 改写 Java 浅度实践 Python 获取海贼王更新信息 HTML5 音乐可视化
比特币脚本开发教程
2025-07-09 · via smallyu的博客

比特币脚本开发教程

2025-07-10

比特币脚本有点像房间里的大象,大家都知道这个东西,但是大家都看不见,或者不在乎。这个教程将从最基本的操作开始,理解比特币脚本的原理,学会自己写比特币脚本。因为比特币脚本不是图灵完备的,所以包含很多命令行操作,以及需要观察输出结果。

1. 启动本地节点

运行这个命令安装 bitcoind 的二进制,然后用 bitcoind --help 来测试是否安装成功:

brew install bitcoin

创建一个用于测试使用的目录,比如我的目录名称是 bitcoin-regtest

mkdir ./bitcoin-regtest
cd ./bitcoin-regtest

在这个目录下新建一个叫 bitcoin.conf 文件,复制这些配置内容进去:

regtest=1
txindex=1
fallbackfee=0.0001

这是本地节点的配置文件,后续我们的比特币脚本将基于本地启动的开发节点来测试。这个配置文件中的 regtest=1 比较关键,指明了节点的类型是本地开发网络,不会真的到公网上同步区块数据,本地节点的块高度将从 0 开始。另外两个配置 txindex=1 是指启动本地节点对所有交易的索引,方便我们后续查看交易,fallbackfee=0.0001 则是指明交易手续费的大小。

停留在包含配置文件的当前目录下,执行这个命令来启动节点。这里的命令行,以及后续的命令行,都会带上 -datadir 参数,因为我们希望节点数据是隔离的,每一个工作目录都是一份新的环境,不至于污染电脑的全局环境,而且默认环境的路径比较长,不同操作系统不一致,虽然我们在后续的命令里都需要带上这么一个参数,看起来有点麻烦,但同时也避免了很多其他问题,比如找不到系统默认目录在哪儿之类:

bitcoind -datadir=./ -daemon

命令成功执行会看到 Bitcoin Core starting 的字样。为了测试节点是否真的在运行,可以用这个命令查看节点的状,会得到一个 json 数据:

bitcoin-cli -datadir=./ getblockchaininfo

如果还是对节点的运行状态不放心,可以直接查看节点的日志文件。这就是我们指定了数据目录的好处,日志文件在这个位置:

cat ./regtest/debug.log

如果想要停掉节点,避免后台进程一直在电脑上运行,用这个命令来停止节点:

bitcoin-cli -datadir=./ stop

注意启动节点用的是 bitcoind,停止节点用的是 bitcoin-cli。前者属于 server 端的命令,后者属于 client 端的命令。

另外,如果在停止节点后重启节点,发现钱包(下一小节内容)不能用了,可以用这个命令来导入钱包:

bitcoin-cli -datadir=./ loadwallet learn-script 

2. 创建钱包

运行这个命令来创建一个比特币钱包:

bitcoin-cli -datadir=./ createwallet "learn-script"

我们刚提到命令行中使用 -datadir 参数来指定数据目录,那么钱包的文件其实也会在数据目录下保存,可以看一下 ./regtest/wallets 目录,有一个 learn-script 的文件夹,我们刚才创建的钱包就在这个文件夹内。

查看钱包地址的命令,比如我的地址是 bcrt1q6c8d9vw62rdee72xcqx3d97w8qh8mfg8ky8zjw

bitcoin-cli -datadir=./ getnewaddress

接着在本地节点上,给钱包地址挖一些钱出来,这里的参数 101 是指挖 101 个区块。为什么是 101 个区块呢?一般我们挖的区块数量会大于 100,因为比特币网络有 100 个区块的成熟期,也就是区块奖励需要在 100 个区块之后,才可以消费。假如我们只挖了 99 个区块,虽然理论上应该得到很多区块奖励,但实际上是不能花费的。

bitcoin-cli -datadir=./ generatetoaddress 101 bcrt1q6c8d9vw62rdee72xcqx3d97w8qh8mfg8ky8zjw

这个命令运行输出的是每个块的区块哈希。运行结束后,我们就可以查看钱包地址的余额了,余额应该是 50:

bitcoin-cli -datadir=./ getbalance

为什么是 50?因为比特币的区块奖励每 4 年减半,第一次减半之前的块奖励,每个区块都是 50 BTC。为什么挖了 101 个块,但只能查到 50 BTC 的余额?因为后 100 个区块的成熟期,奖励是不到账的。

3. 发送交易

那么现在我们已经有了本地在运行的节点,以及有余额的钱包,接下来可以发起一笔普通的转账交易。先生成一个用于接收转账的新地址,我生成的地址是 bcrt1qgq99zusgk3ekrzucs9uyqv5vpxnh66cjtwl6zc

bitcoin-cli -datadir=./ getnewaddress

可以查看验证一下,新生成的地址余额为 0。这个命令中的参数 0 意味着查询结果包含未确认的交易。

bitcoin-cli -datadir=./ getreceivedbyaddress bcrt1qgq99zusgk3ekrzucs9uyqv5vpxnh66cjtwl6zc 0

接着使用发起交易的命令,来向新生成的地址转账 0.01 BTC:

bitcoin-cli -datadir=./ sendtoaddress bcrt1qgq99zusgk3ekrzucs9uyqv5vpxnh66cjtwl6zc 0.01

这个命令会返回交易哈希,比如我的哈希值是 81be2e97507d7a274029ec4d5ce9728a54fe6d885aa0f12a13ec6f54eee66c26。我们需要用这个交易哈希来查询交易结果和交易详情,像这样:

bitcoin-cli -datadir=./ gettransaction 81be2e97507d7a274029ec4d5ce9728a54fe6d885aa0f12a13ec6f54eee66c26

这笔交易此时就已经提交到链上了,但是也许你会注意到,查询交易详情返回的交易状态中,有一个 "confirmations": 0,意味着交易还没有被确认,而且区块高度还停留在 lastprocessedblock: 101 上。因为比特币不会自动出块,这个时候查询接收地址的余额,能看出差异:

# 查询到余额是 0.01
bitcoin-cli -datadir=./ getreceivedbyaddress bcrt1qgq99zusgk3ekrzucs9uyqv5vpxnh66cjtwl6zc 0
# 查询到余额是 0
bitcoin-cli -datadir=./ getreceivedbyaddress bcrt1qgq99zusgk3ekrzucs9uyqv5vpxnh66cjtwl6zc 1

因为我们之前有说明,最后一个参数是 0 代表包含未确认的交易,否则只查询确认的交易。我们刚刚发送的交易就还没有确认。如果想确认下来,就得用之前的 generatetoaddress 命令再挖一个区块出来:

bitcoin-cli -datadir=./ generatetoaddress 1 bcrt1q6c8d9vw62rdee72xcqx3d97w8qh8mfg8ky8zjw

现在再去查询交易状态,无论是确认数还是钱包余额,就都符合预期了。

4. 查看交易脚本

我们刚才发送的是一笔 P2WPKH 交易,因为现在比特币客户端默认使用原生 SegWit 的地址格式。

先了解一下 P2PKH 是什么,全称是 Pay to Public-Key Hash,我们使用的比特币地址本身就是一个公钥的子集,而 P2PKH 交易以账户地址为接收参数,所以命名为 P2PKH。我们常说的比特币原生地址,就是指 P2PKH 格式,一般以 1 开头,

相比 P2PKH,原生 SegWit 的地址格式叫 P2WPKH,中间多了个字母 W,全称是 Pay to Witness Public-Key Hash,特点是会把签名数据放在 witness 字段里,而不是每一笔 UTXO 的输出里,我们可以具体看一下,首先根据交易哈希,查询得到交易的全部数据:

bitcoin-cli -datadir=./ getrawtransaction 81be2e97507d7a274029ec4d5ce9728a54fe6d885aa0f12a13ec6f54eee66c26

会得到一大段编码后的数据,用这个命令来解码交易数据:

bitcoin-cli -datadir=./ decoderawtransaction 020000000001018f4e8514038b93d6cc1d4f77b011f4726ba765d338bfdf1e6724d1844bc5d36e0000000000fdffffff0240420f0000000000160014400a517208b473618b98817840328c09a77d6b123eaaf629010000001600147ef4555b42b71e6ebecd687170c92ab64cce35500247304402202417ff3f6959a7d449849ae78fd5272826339cd7096ab02cdd7eccfc7779fb14022077e43ce155259a602b6172261b1d830d30e0de8b06cd6479cac02ea7c6928ff10121020b396a9dfa1655feef066fe03b403d3e4bdee41ef9b26551497c0921acbf6bc196000000

查询得到的数据结构是这样:

{
  // ...
  "vin": [
    {
      // ...
      "txinwitness": [
        "304402202417ff3f6959a7d449849ae78fd5272826339cd7096ab02cdd7eccfc7779fb14022077e43ce155259a602b6172261b1d830d30e0de8b06cd6479cac02ea7c6928ff101",
        "020b396a9dfa1655feef066fe03b403d3e4bdee41ef9b26551497c0921acbf6bc1"
      ],
    }
  ],
  "vout": [
    {
      "value": 0.01000000,
      "scriptPubKey": {
        "asm": "0 400a517208b473618b98817840328c09a77d6b12",
        "desc": "addr(bcrt1qgq99zusgk3ekrzucs9uyqv5vpxnh66cjtwl6zc)#nry368tt",
        "hex": "0014400a517208b473618b98817840328c09a77d6b12",
        "address": "bcrt1qgq99zusgk3ekrzucs9uyqv5vpxnh66cjtwl6zc",
        "type": "witness_v0_keyhash"
      }
    },
    {
      "value": 49.98998590,
      "scriptPubKey": {
        // ...
      }
    }
  ]
}

首先关注 txinwitness 这个字段,它是一个数字,有两个部分,第一个部分是签名数据,第二个部分是公钥,这就是我们之前提到的 SegWit,对金额的签名不放在 vout 里,而是放在了 vin 里。

然后再关注 scriptPubKey 里的 asm,ASM 是 RedeemScript 的意思,表示满足什么样的条件就可以消费脚本中锁定的金额。是的我们即使是发起普通转账,实际上也是一种比特币脚本,金额锁定在了脚本中。我们查询到的脚本内容分为两段,第一段是 0,表示比特币脚本中的一个操作码 OP_0,第二段是 400a517208b473618b98817840328c09a77d6b12,其实就是钱包地址,经过 bech32 编码后会变成熟悉的样子 bcrt1qgq99zusgk3ekrzucs9uyqv5vpxnh66cjtwl6zc

5. 用 btcdeb 调试

刚才提到了 OP_0 这个操作码,它具体是什么呢?操作码是比特币脚本的关键,我们可以用 btcdeb 工具调试和观察一下。btcdeb 没有提供一键式的安装命令,可以按照 官方的教程 先下载源码,然后编译安装。验证安装结果:

btcdeb --version

OP_0 这个操作码本身干的事情很简单,就是把空数据压进栈结构里,尝试运行命令:

btcdeb OP_0

会看到这样的输出:

script  |  stack 
--------+--------
0       | 
#0000 0

前面的 script 表示有一个操作 0, 也就是 OP_0,这里显示的时候自动隐去了 OP_ 前缀。后面 #0000 0 则表示目前栈里内容为 0(空)。接下来的输入 step 命令,让 btcdeb 真正运行 OP_0 这个步骤,运行结果是这样,可以看到推了一个空数据到栈里,这就是 OP_0 干的事情:

step
        <> PUSH stack 

为了增加理解,我们举一个别的操作码例子来观察栈内数据的变化,尝试这个命令:

btcdeb '[OP_2 OP_3 OP_ADD]'

然后输出 step 命令,一直按回车直到脚本结束,输出内容的过程像是这样。默认内容是这样,此时脚本里有 3 个操作码等待执行,分别是 OP_2OP_3OP_ADD

script  |  stack 
--------+--------
2       | 
3       | 
OP_ADD  | 
#0000 2

第一次回车执行了脚本的第一个步骤 OP_2,对应操作把数字 2 压入栈,执行结束后脚本里剩 2 个操作码了,同时 stack 中有了数字 2:

step
        <> PUSH stack 02

btcdeb> script  |  stack 
--------+--------
3       |      02
OP_ADD  | 
#0001 3

第二次回车继续执行了 OP_3 操作码,把数字 3 压入栈,此时脚本里只剩 1 个操作码,栈中有数字 2 和数字 3:

        <> PUSH stack 03

btcdeb> script  |  stack 
--------+--------
OP_ADD  |      03
        |      02
#0002 OP_ADD

第三次回车执行 OP_ADD 操作码,这个操作码会从栈里弹出两个数字,计算加法后把结果推回栈内,得到结果 5:

        <> POP  stack
        <> POP  stack
        <> PUSH stack 05
btcdeb> script  |  stack 
--------+--------
        |      05

因为 btcdeb 的命令行输出并不是特别直观,所以这里尽管占用篇幅,也有必要把整个过程的输出都复制过来,还拆分了步骤,方便理解每一步在干什么。可以看到每一个操作码都会对应一些行为,这个行为是比特币程序里定义的,包括加法、减法等各种运算,也有一些行为更复杂的操作,或者对简单的操作码进行排列组合,达到实现更复杂功能的目的。我们还看到比特币脚本的执行是基于栈的,全部行为都发生在栈结构里,栈结构也就意味着完全没有动态内存分配之类的东西。

6. 自己编写比特币脚本 (1)

刚才尝试了在 btcdeb 调试工具里运算加法,现在试着在实际的比特币交易中,写入脚本代码,并且在链上运算。这段是原始的操作码形式的脚本,要注意这个脚本是不安全的,属于自验证的脚本,任何人都可以花费这个脚本中的金额,只是在花费过程中,脚本表示的数字运算会在链上执行:

[OP_2 OP_3 OP_ADD OP_5 OP_EQUAL]

首先需要把操作码转变为十六进制形式,这个编码过程需要手动,或者写代码来操作。我们使用手动的方式,这个 比特币文档 中列出了全部支持的操作码,以及对应的十六进制字符,到我们这个小脚本这里,对应关系就是:

操作码 十六进制
OP_2 52
OP_3 53
OP_ADD 93
OP_5 55
OP_EQUAL 87

因此我们按照依次拼接的顺序,得到了的十六进制脚本:

5253935587

接着生成 P2SH 地址。P2SH 的全称是 Pay to Script Hash,意思是支付到脚本哈希,或者说锁定金额到脚本中,相当于链上脚本的地址:

bitcoin-cli -datadir=./ decodescript 5253935587

命令输出中有一个 p2sh-segwit 字段,值是 2NAzGPjCcg8DiykVTKLJRYbU2fejCEbdPbX,把这个 P2SH 地址用作参数生成脚本的校验和,校验和是构造比特币交易必须要的一个参数:

bitcoin-cli -datadir=./ getdescriptorinfo "addr(2NAzGPjCcg8DiykVTKLJRYbU2fejCEbdPbX)"

得到 descriptor 的值为 addr(2NAzGPjCcg8DiykVTKLJRYbU2fejCEbdPbX)#s260u65e,后续用这个值作为脚本参数构造交易。

不过到这里还有个坑,比特币的 P2SH 脚本,只能用观察模式的钱包导入,所以需要新创建一个没有私钥的钱包:

bitcoin-cli -datadir=./ createwallet "arith-watch" true true "" true

用刚刚创建的新钱包,导入 P2SH 脚本。看到这个命令返回 "success": true,才表示导入成功:

bitcoin-cli -datadir=./ -rpcwallet=arith-watch importdescriptors '[{"desc":"addr(2NAzGPjCcg8DiykVTKLJRYbU2fejCEbdPbX)#s260u65e","timestamp":"now","label":"arith-2+3=5"}]'

现在有了 P2SH 的脚本地址,并且已经把脚本导入到钱包,接下来可以给脚本打钱了。这个命令从 learn-script 钱包转账 0.01 BTC 给脚本:

bitcoin-cli -datadir=./ -rpcwallet=learn-script sendtoaddress 2NAzGPjCcg8DiykVTKLJRYbU2fejCEbdPbX 0.01

挖一个区块让交易确认:

bitcoin-cli -datadir=./ generatetoaddress 1 bcrt1q6c8d9vw62rdee72xcqx3d97w8qh8mfg8ky8zjw

现在,这个脚本就上链并且有余额了。

7. 自己编写比特币脚本 (2)

目前这个脚本地址里的钱,任何人都可以消费,消费的同时会运算一下 2+3 这个表达式,并且判断结果是否为 5。接下来构建一笔花费脚本金额的交易,真正花掉刚才存进脚本的钱。准备一个收款地址:

bitcoin-cli -datadir=./ -rpcwallet=learn-script getnewaddress

我新建的地址是 bcrt1q0n2x7030x59j5ql9pp6mw0tps74ag0znrdp45r。用这个地址构建一笔交易,注意这里 inputs 中的 txid,是刚才给 P2SH 转账的那一笔交易哈希:

bitcoin-cli -datadir=./ -named createrawtransaction \
  inputs='[{"txid":"b952acd06a4f7edd7b2d5da0d509d01dfbb8e49fa15123d9cd5d3d23f944cdc2","vout":0}]' \
  outputs='{"bcrt1q0n2x7030x59j5ql9pp6mw0tps74ag0znrdp45r":0.009}'

在构建的交易中添加自动找零参数:

bitcoin-cli -datadir=./ -rpcwallet=learn-script \
  fundrawtransaction 0200000001c2cd44f9233d5dcdd92351a19fe4b8fb1dd009d5a05d2d7bdd7e4f6ad0ac52b90000000000fdffffff01a0bb0d00000000001600147cd46f3e2f350b2a03e50875b73d6187abd43c5300000000

关键的一步,用钱包给这笔交易签名,注意这里是给找零之后的交易数据进行签名,如果不找零,节点会把找零金额当作手续费,而节点默认还有手续费的上限值,如果这一步没找零,下一步会触发手续费上限报错:

bitcoin-cli -datadir=./ -rpcwallet=learn-script \
  signrawtransactionwithwallet 0200000001c2cd44f9233d5dcdd92351a19fe4b8fb1dd009d5a05d2d7bdd7e4f6ad0ac52b90000000000fdffffff02a0bb0d00000000001600147cd46f3e2f350b2a03e50875b73d6187abd43c5360a0d92901000000160014a3e136e24d5a8db14f15016b99fb21ea4b0b69da00000000

最后,把签名好的交易数据广播出去就行了:

bitcoin-cli -datadir=./ sendrawtransaction 02000000000101c2cd44f9233d5dcdd92351a19fe4b8fb1dd009d5a05d2d7bdd7e4f6ad0ac52b90000000017160014c2d5ade24c1d0b9f27f651a71c3fe49d23d0ae13fdffffff02a0bb0d00000000001600147cd46f3e2f350b2a03e50875b73d6187abd43c5360a0d92901000000160014a3e136e24d5a8db14f15016b99fb21ea4b0b69da024730440220406a51d43ade05b240fcf2d14b58c90f31ebc705ab262189949355cac54d0431022051b592c570ef960a35e8509766e903ba836e3bcd1fb3c5cc211f0ff3442283550121021ff283ca8c9ecb45c8e19eacb7e8ae6fcb27d8addd38011d633e396487db44e300000000

记得再挖一个区块让交易确认:

bitcoin-cli -datadir=./ generatetoaddress 1 bcrt1q6c8d9vw62rdee72xcqx3d97w8qh8mfg8ky8zjw

查看交易状态,验证交易已被花费,如果返回空值,说明已被花费。这里查的交易哈希是当时用钱包给脚本转账 0.01 BTC 那一笔交易的哈希:

bitcoin-cli -datadir=./ gettxout b952acd06a4f7edd7b2d5da0d509d01dfbb8e49fa15123d9cd5d3d23f944cdc2 0

8. Troubshooting

我本地的操作环境以及软件脚本是:

OS: MacOS
bitcoind: v29.0.0
btcdeb:5.0.24