Taproot 파트1: P2TR KeyPath Witness Programs 생성하기 🚀
Source: Dev.to
다시 찾아와 주신 독자 여러분, 환영합니다! 여기까지 오셨다면 설정 과정을 무사히 마치고, 실제 비트코인 내부 마법을 직접 다룰 준비가 되었다는 뜻이죠.
제 이전 로그에서는 Signet 노드를 구축하고 실행시킨 뒤, BOSS (Bitcoin Open Source Software) 챌린지를 위해 은밀히 “클로킹”했습니다. 이제 첫 번째 실전 시험이 찾아옵니다: Challenge 1 – 지갑 상태 복구.
퀘스트는? 블록체인을 직접 스캔해서 지갑 잔액을 계산하는 것입니다. 함정은? 평가 환경에서 비트코인 코어 지갑이 비활성화되어 있다는 점입니다. getbalance도 없고, 편법도 없습니다. 오직 여러분과 코드, 그리고 원시 블록만이 남았습니다. 시작해볼까요! ⚡️
Source:
🛠 Step 0: “Sanity Check” (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
잔액이 표시된다면, BOOM! 💥 이제 목표로 삼아야 할 숫자를 알게 되었습니다. 이제 그 숫자를 수동으로 찾는 코드를 작성해 봅시다.
🧬 심층 탐구: P2TR 파생
우리의 코인을 찾기 위해서는 Witness Program이 필요합니다. Taproot (BIP 341) 세계에서는 주소가 “tweaked”된 공개키에서 파생됩니다. Key Path만 사용하더라도(복잡한 스크립트 없이) 스크립트 경로에 커밋해야 합니다.
“골든” 벤치마크 스크립트
우리는 bitcoinjs-lib와 tiny-secp256k1을 사용해 무거운 타원곡선 연산을 처리합니다.
CRITICAL NOTE: 이 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.");
🎯 Why This Is Your Secret Weapon
The BOSS Challenge requires you to:
- Derive 2000 keys.
- Compute the P2TR witness program for each.
- Scan the first 300 blocks of the Signet chain.
The Witness Program (the pubkey in the script above) is the actual hex string you’ll be looking for inside the scriptPubKey of transaction outputs on‑chain. Using this JS benchmark helps you isolate derivation bugs from scanning bugs.
⏭ 다음은?
이제 witnesses.json에 2000개의 witness 프로그램 목록이 생겼으니, 사냥을 시작할 시간입니다! 블록을 가져오고, 트랜잭션을 반복하며, 잔액을 합산하세요.
Rust 혹은 Python으로 도전을 진행하고 있나요? 아래에 댓글을 남겨서 TapTweak 수학에 대해 이야기해요! 🚀🔥