Taproot和Schnorr是旨在增强比特币交易的隐私,效率和灵活性的比特币协议的升级。
Taproot介绍了Taptrees,该功能可降低交易数据的大小,并确保仅在区块链上揭示必要的信息,从而保留隐私。使用Taproot,由于隐藏了未使用的支出条件,因此Multisig Transactions也更加私密。
schnorr签名为64个字节,而不是当前ECDSA签名方案使用的72个字节。 Taproot还仅使用X值公共钥匙来保存1个字节的签名创建。
采用Taproot和Schnorr的采用,比特币交易可以更有效,灵活和私人。
我们将介绍两个示例。在我们的第一个示例中,我们将创建一个付费型式型号(P2TR)地址,该地址将把资金锁定到钥匙并为其创建支出交易。在我们的第二个示例中,我们将直接跳入Taproot脚本启动交易,我们将创建一个由两个脚本播种路径组成的Taptree
,一个哈希锁脚本支出路径和一个付费 - 付费脚本脚本支出路径。我们将创建从这两条路径上花费的交易。
本文的完整代码可以在taproot-with-bitcoinjs的GitHub上找到。
Taproot键式启动交易
出于插图目的,我们将使用随机按键
const keypair = ECPair.makeRandom({ network });
我们与我们的Pubkey一起调整了此键盘。
const tweakedSigner = tweakSigner(keypair, { network });
bitcoinjs-lib
提供了一个p2tr
函数来生成p2tr输出。
const p2pktr = payments.p2tr({
pubkey: toXOnly(tweakedSigner.publicKey),
network
});
const p2pktr_addr = p2pktr.address ?? "";
console.log(p2pktr_addr);
toXOnly
函数提取了我们的公钥的X值。
您可以使用任何支持Taproot地址的测试网络水龙头。我在测试时使用了testnet-faucet.com/btc-testnet。
使用BitCoinjs-lib创建此地址的支出交易很简单。
const psbt = new Psbt({ network });
psbt.addInput({
hash: utxos[0].txid,
index: utxos[0].vout,
witnessUtxo: { value: utxos[0].value, script: p2pktr.output! },
tapInternalKey: toXOnly(keypair.publicKey)
});
psbt.addOutput({
address: "mohjSavDdQYHRYXcS3uS6ttaHP8amyvX78", // faucet address
value: utxos[0].value - 150
});
psbt.signInput(0, tweakedSigner);
psbt.finalizeAllInputs();
提取交易并广播交易十六进制。
const tx = psbt.extractTransaction();
console.log(`Broadcasting Transaction Hex: ${tx.toHex()}`);
const txid = await broadcast(tx.toHex());
console.log(`Success! Txid is ${txid}`);
Taproot脚本启动交易
我们将创建一个带有两个支出路径的水龙头树,一条锁定的支出路径和一条付费 - 付费。
Hash-Lock脚本带路径将要求Spender包含一个预先映射,该预映射将产生脚本中指定的哈希。
让我们为我们的哈希锁脚本做另一个随机按键
const hash_lock_keypair = ECPair.makeRandom({ network });
现在,我们将构建我们的哈希锁脚本
const secret_bytes = Buffer.from('SECRET');
const hash = crypto.hash160(secret_bytes);
// Construct script to pay to hash_lock_keypair if the correct preimage/secret is provided
const hash_script_asm = `OP_HASH160 ${hash.toString('hex')} OP_EQUALVERIFY ${toXOnly(hash_lock_keypair.publicKey).toString('hex')} OP_CHECKSIG`;
const hash_lock_script = script.fromASM(hash_script_asm);
请注意,脚本仍然需要签名才能解锁资金。
付费式支出路径要简单得多
const p2pk_script_asm = `${toXOnly(keypair.publicKey).toString('hex')} OP_CHECKSIG`;
const p2pk_script = script.fromASM(p2pk_script_asm);
我们现在可以创建我们的Taptree和P2TR地址。
const scriptTree: Taptree = [
{
output: hash_lock_script
},
{
output: p2pk_script
}
];
const script_p2tr = payments.p2tr({
internalPubkey: toXOnly(keypair.publicKey),
scriptTree,
network
});
const script_addr = script_p2tr.address ?? '';
console.log(script_addr);
您可以使用testnet-faucet.com/btc-testnet等测试网龙头将一些测试BTC存放到地址中。
要花费在任何叶子脚本上,您必须为该叶子脚本提供叶片,脚本和控制板。控制块是证明LEAF脚本存在于脚本树中所需的数据(Merkle证明)。
BitCoinjs-lib将为我们生成控制块。
const hash_lock_redeem = {
output: hash_lock_script,
redeemVersion: 192,
};
const p2pk_redeem = {
output: p2pk_script,
redeemVersion: 192
}
const p2pk_p2tr = payments.p2tr({
internalPubkey: toXOnly(keypair.publicKey),
scriptTree,
redeem: p2pk_redeem,
network
});
const hash_lock_p2tr = payments.p2tr({
internalPubkey: toXOnly(keypair.publicKey),
scriptTree,
redeem: hash_lock_redeem,
network
});
console.log(`Waiting till UTXO is detected at this Address: ${script_addr}`);
let utxos = await waitUntilUTXO(script_addr)
console.log(`Trying the P2PK path with UTXO ${utxos[0].txid}:${utxos[0].vout}`);
const p2pk_psbt = new Psbt({ network });
p2pk_psbt.addInput({
hash: utxos[0].txid,
index: utxos[0].vout,
witnessUtxo: { value: utxos[0].value, script: p2pk_p2tr.output! },
tapLeafScript: [
{
leafVersion: p2pk_redeem.redeemVersion,
script: p2pk_redeem.output,
controlBlock: p2pk_p2tr.witness![p2pk_p2tr.witness!.length - 1] // extract control block from witness data
}
]
});
p2pk_psbt.addOutput({
address: "mohjSavDdQYHRYXcS3uS6ttaHP8amyvX78", // faucet address
value: utxos[0].value - 150
});
p2pk_psbt.signInput(0, keypair);
p2pk_psbt.finalizeAllInputs();
let tx = p2pk_psbt.extractTransaction();
console.log(`Broadcasting Transaction Hex: ${tx.toHex()}`);
let txid = await broadcast(tx.toHex());
console.log(`Success! Txid is ${txid}`);
要使用哈希锁板脚本花费,我们必须创建一个自定义最终制度函数。在我们的自定义最终制度中,我们将创建我们的证人signature, preimage, original hash-lock script and our control block
const tapLeafScript = {
leafVersion: hash_lock_redeem.redeemVersion,
script: hash_lock_redeem.output,
controlBlock: hash_lock_p2tr.witness![hash_lock_p2tr.witness!.length - 1]
};
const psbt = new Psbt({ network });
psbt.addInput({
hash: utxos[0].txid,
index: utxos[0].vout,
witnessUtxo: { value: utxos[0].value, script: hash_lock_p2tr.output! },
tapLeafScript: [
tapLeafScript
]
});
psbt.addOutput({
address: "mohjSavDdQYHRYXcS3uS6ttaHP8amyvX78", // faucet address
value: utxos[0].value - 150
});
psbt.signInput(0, hash_lock_keypair);
// We have to construct our witness script in a custom finalizer
const customFinalizer = (_inputIndex: number, input: any) => {
const scriptSolution = [
input.tapScriptSig[0].signature,
secret_bytes
];
const witness = scriptSolution
.concat(tapLeafScript.script)
.concat(tapLeafScript.controlBlock);
return {
finalScriptWitness: witnessStackToScriptWitness(witness)
}
}
psbt.finalizeInput(0, customFinalizer);
tx = psbt.extractTransaction();
console.log(`Broadcasting Transaction Hex: ${tx.toHex()}`);
txid = await broadcast(tx.toHex());
console.log(`Success! Txid is ${txid}`);
结论
通过阅读本文,您现在应该更好地了解如何使用BitCoinjs-lib来创建和花费P2TR(付款给Taproot)付款。有了这些知识,您就可以更近一步地利用Taproot在比特币交易中的好处,例如改善隐私性,可扩展性和创建更复杂的智能合约的能力。
您可以在bitcoinjs-lib's repo中找到更多示例。本文的完整代码可以在taproot-with-bitcoinjs的GitHub上找到。