Taproot 第1部分:生成 P2TR KeyPath Witness 程序 🚀

发布: (2026年1月20日 GMT+8 03:48)
5 min read
原文: Dev.to

Source: Dev.to

欢迎回来,亲爱的读者们!如果你看到这里,说明你已经顺利完成了环境搭建,准备好动手玩点真正的比特币内部魔法了。

在我的上一篇日志中,我们已经把 Signet 节点启动、运行,并且为 BOSS(Bitcoin Open Source Software)挑战 暗中“伪装”。现在,是时候迎接第一个真正的考验了:Challenge 1 – Recover Wallet State

任务是什么?手动扫描区块链来计算你的钱包余额。难点在哪里?评估环境中已 禁用 Bitcoin Core 钱包。没有 getbalance,也没有任何捷径。只有你、你的代码和原始区块。出发吧! ⚡️

🛠 步骤 0:“合理性检查”(CLI)

在我们开始自动化之前,需要一个“北极星”来确保我们没有在追逐幻影。我们将使用 Bitcoin CLI 查看我们的余额 应该 是多少。这是你的基准!

1. 创建本地钱包

docker exec bitcoin_signet bitcoin-cli -signet -datadir=/home/bitcoin/.bitcoin \
  createwallet "boss" false false "" false true true

2. 导入该描述符

docker exec bitcoin_signet bitcoin-cli -signet -datadir=/home/bitcoin/.bitcoin \
  -rpcwallet=boss importdescriptors '[{ "desc": "tr(tprv8ZgxMBicQKsPfCkDmSmHXuKAVA8zwwXLWTHEgyyisMioZ8gn3nTmmNwUzBMKWnNMkhRYN3E9qVCZrZ6MC645cqYdpy9jTYHhBwG1KhpNAMd/86h/1h/0h/0/*)#hjz07900", "timestamp": 0, "active": true }]'

3. 查看结果

docker exec bitcoin_signet bitcoin-cli -signet -datadir=/home/bitcoin/.bitcoin \
  -rpcwallet=boss getwalletinfo

如果你看到余额,轰!💥 现在你已经知道要达到的数值了。让我们手动编写代码来找到它。

🧬 深入探讨:P2TR 推导

要找到我们的币,需要 Witness Program。在 Taproot(BIP 341)的世界里,地址是由“调校”(tweaked)后的公钥派生的。即使我们只使用 Key Path(不涉及复杂脚本),也必须对脚本路径进行一次提交。

“黄金”基准脚本

我们使用 bitcoinjs-libtiny-secp256k1 来处理繁重的椭圆曲线运算。

重要提示:不能 在最终提交中使用此 JavaScript 程序!BOSS 挑战严格要求使用 Python、C、C++ 或 Rust。仅将此 JS 脚本用作 基准,用来验证你的逻辑。如果你的 Python/Rust 代码生成的字符串与此脚本相同,说明你走在正确的道路上!

import { BIP32Factory } from "bip32";
import ECPairFactory from "ecpair";
import * as ecc from "tiny-secp256k1";
import * as bitcoin from "bitcoinjs-lib";
import fs from "fs";

// Initialize the ECC library—Taproot needs this!
bitcoin.initEccLib(ecc);

export const bip32 = BIP32Factory(ecc);
export const ECPair = ECPairFactory(ecc);

const net = bitcoin.networks.testnet;
const xpriv = bip32.fromBase58(
  "tprv8ZgxMBicQKsPfF4wki3EzsSYvohmC5z2B92m74LscyiFAHwZUtn4Lhbfosc1zCQRzN4aXiPqiH64xbTbsnRCgTskJWspELwAYSdyZSekqyp",
  net,
);

// Derive the path from the descriptor: m/86'/1'/0'/0
const derived = xpriv.derivePath("m/86'/1'/0'/0");
const addresses = [];
const internals = [];
const witnesses = [];

console.log("🚀 Starting derivation for 2000 keys...");

for (let i = 0; i < 2000; i++) {
  const current = derived.derive(i);

  // Extract the X‑only internal pubkey (32 bytes)
  const internalPubkey = current.publicKey.slice(1, 33);

  // Generate the P2TR payment—bitcoinjs handles the TapTweak automatically!
  const { address, pubkey } = bitcoin.payments.p2tr({
    internalPubkey,
    network: net,
  });

  addresses.push(address);
  witnesses.push(pubkey?.toHex()); // This is your Witness Program!
  internals.push(internalPubkey.toHex());
}

// Write to files so you can 'diff' against your main submission code
fs.writeFileSync("addresses.json", JSON.stringify(addresses, null, 2));
fs.writeFileSync("witnesses.json", JSON.stringify(witnesses, null, 2));
fs.writeFileSync("internals.json", JSON.stringify(internals, null, 2));

console.log("✅ Done! Check your JSON files and compare them with your challenge code.");

🎯 为什么这是你的秘密武器

BOSS 挑战要求你:

  • 推导 2000 个密钥
  • 为每个密钥计算 P2TR 见证程序
  • 扫描 Signet 链的前 300 个区块

Witness Program(上面脚本中的 pubkey)是你将在链上交易输出的 scriptPubKey 中寻找的实际十六进制字符串。使用此 JS 基准测试可以帮助你将推导错误与扫描错误分离。

⏭ 接下来是什么?

现在你已经在 witnesses.json 中拥有了 2000 条见证程序的列表,是时候去搜寻了!获取区块,遍历交易,并开始统计余额。

你是用 Rust 还是 Python 来攻克这个挑战的?在下方留言,让我们一起聊聊 TapTweak 数学! 🚀🔥

Back to Blog

相关文章

阅读更多 »