Skip to content

Commit 43cedb1

Browse files
authored
Merge pull request #1285 from ygcl9698/taproot
feat: Add Taproot Transaction Support with Schnorr Signatures
2 parents e7bcf80 + 3a78032 commit 43cedb1

File tree

3 files changed

+323
-0
lines changed

3 files changed

+323
-0
lines changed

Diff for: BTC/Advanced/Taproot/example/README.md

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Bitcoin Taproot Example
2+
3+
A demonstration project showcasing Bitcoin Taproot technology implementation, including address creation, transaction handling, and Schnorr signatures.
4+
5+
## Features
6+
7+
- Taproot address generation
8+
- Schnorr signature implementation
9+
- Transaction creation and signing
10+
- OP_RETURN output support
11+
- Debug information output
12+
13+
## Tech Stack
14+
15+
- bitcoinjs-lib: Bitcoin transaction handling library
16+
- ecpair: Key pair management
17+
- @bitcoinerlab/secp256k1: Elliptic curve cryptography
18+
19+
## Installation
20+
21+
```bash
22+
npm install bitcoinjs-lib ecpair @bitcoinerlab/secp256k1
23+
```
24+
25+
## Usage
26+
27+
1. Create a Taproot address:
28+
```javascript
29+
const taprootData = createTaprootAddress();
30+
console.log('Taproot Address:', taprootData.address);
31+
```
32+
33+
2. Create a transaction:
34+
```javascript
35+
const tx = await createTaprootTransaction(taprootData, recipientAddress, amount);
36+
```
37+
38+
## Core Components
39+
40+
### SchnorrSigner Class
41+
Handles Schnorr signatures required for Taproot transactions:
42+
- Key pair management
43+
- x-only public key conversion
44+
- Schnorr signature generation
45+
46+
### Utility Functions
47+
- `toXOnly`: Converts full public key to x-only format
48+
- `createTaprootAddress`: Generates Taproot address
49+
- `addOpReturnOutput`: Adds OP_RETURN output
50+
- `createTaprootTransaction`: Creates and signs transactions
51+
52+
## Important Notes
53+
54+
- Currently configured for testnet
55+
- Uses fixed transaction fee (1000 satoshis)
56+
- Example UTXO is placeholder (replace with real UTXO in production)
57+
58+
## Debug Features
59+
60+
The project includes comprehensive debug output:
61+
- Address information
62+
- Public key details
63+
- Transaction details
64+
- Signature information
65+
66+
## Example Output
67+
68+
When running the program, you'll see:
69+
- Taproot address
70+
- Public key (hex format)
71+
- Output script
72+
- Network information
73+
- Transaction details
74+
- Witness data
75+
76+
## Development Environment
77+
78+
- Node.js
79+
- npm/yarn
80+
- Bitcoin Testnet (for testing)
81+
82+
## Security Considerations
83+
84+
- This is a demonstration project, not recommended for mainnet use
85+
- Ensure proper private key handling in production
86+
- Implement appropriate error handling for production use
87+
88+
## API Reference
89+
90+
### createTaprootAddress()
91+
Returns an object containing:
92+
- signer: SchnorrSigner instance
93+
- address: Taproot address
94+
- output: Output script
95+
96+
### createTaprootTransaction(taprootData, recipient, satoshis, message)
97+
Parameters:
98+
- taprootData: Object containing signer and output information
99+
- recipient: Recipient's address
100+
- satoshis: Amount to send
101+
- message: Optional OP_RETURN message (default provided)
102+
103+
Returns: Signed transaction in hex format
104+
105+
## References
106+
107+
- [Bitcoin Taproot Documentation](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)
108+
- [bitcoinjs-lib Documentation](https://github.com/bitcoinjs/bitcoinjs-lib)
109+
110+
## Contributing
111+
112+
Feel free to submit issues and enhancement requests.
113+
114+
## License
115+
116+
MIT License
117+
```
118+
119+
This README.md provides:
120+
1. Project overview and key features
121+
2. Installation and usage instructions
122+
3. Detailed component descriptions
123+
4. Development considerations
124+
5. Debug information
125+
6. Security notes
126+
7. API documentation
127+
8. Relevant references
128+
129+
You can customize this content based on your specific needs.

Diff for: BTC/Advanced/Taproot/example/index.js

+174
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
const bitcoin = require('bitcoinjs-lib');
2+
const { ECPairFactory } = require('ecpair');
3+
const secp256k1 = require('@bitcoinerlab/secp256k1');
4+
const ECPair = ECPairFactory(secp256k1);
5+
6+
// Initialize the elliptic curve library for Bitcoin operations
7+
bitcoin.initEccLib(secp256k1);
8+
9+
// Set network to testnet for development purposes
10+
const network = bitcoin.networks.testnet;
11+
12+
/**
13+
* Convert a public key to x-only format required for Taproot
14+
* @param {Buffer} pubKey - The full public key
15+
* @returns {Buffer} The x-only public key (32 bytes)
16+
*/
17+
function toXOnly(pubKey) {
18+
return Buffer.from(pubKey).slice(1, 33);
19+
}
20+
21+
/**
22+
* SchnorrSigner class for handling Taproot signatures
23+
* Implements the Schnorr signature scheme required for Taproot
24+
*/
25+
class SchnorrSigner {
26+
constructor(keyPair) {
27+
this.keyPair = keyPair;
28+
// Convert to x-only public key format required for Taproot
29+
this.publicKey = toXOnly(keyPair.publicKey);
30+
}
31+
32+
// Sign transaction with Schnorr signature
33+
sign(hash) {
34+
return this.keyPair.sign(hash, true); // Enable Schnorr signing
35+
}
36+
37+
signSchnorr(hash) {
38+
return this.sign(hash);
39+
}
40+
}
41+
42+
/**
43+
* Creates a Taproot address and associated data
44+
* @returns {Object} Contains signer, address and output script
45+
*/
46+
function createTaprootAddress() {
47+
// Generate random keypair for Taproot
48+
const keyPair = ECPair.makeRandom({ network });
49+
const schnorrSigner = new SchnorrSigner(keyPair);
50+
51+
// Create P2TR (Pay-to-Taproot) payment object
52+
const { address, output } = bitcoin.payments.p2tr({
53+
pubkey: schnorrSigner.publicKey,
54+
network,
55+
});
56+
57+
return { signer: schnorrSigner, address, output };
58+
}
59+
60+
/**
61+
* Adds OP_RETURN output to transaction
62+
* @param {Psbt} psbt - The Partially Signed Bitcoin Transaction
63+
* @param {string} message - Message to embed in OP_RETURN
64+
*/
65+
function addOpReturnOutput(psbt, message) {
66+
const data = Buffer.from(message, 'utf8');
67+
const embed = bitcoin.payments.embed({ data: [data] });
68+
psbt.addOutput({ script: embed.output, value: 0 });
69+
}
70+
71+
/**
72+
* Creates and signs a Taproot transaction
73+
* @param {Object} taprootData - Contains signer and output information
74+
* @param {string} recipient - Recipient's address
75+
* @param {number} satoshis - Amount to send
76+
* @param {string} message - Optional OP_RETURN message
77+
* @returns {string} Signed transaction in hex format
78+
*/
79+
async function createTaprootTransaction(taprootData, recipient, satoshis, message = "Created with Bitcoin Taproot Demo") {
80+
try {
81+
// Initialize PSBT (Partially Signed Bitcoin Transaction)
82+
const psbt = new bitcoin.Psbt({ network });
83+
84+
// Example transaction ID (replace with actual UTXO in production)
85+
const txid = Buffer.from('a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2', 'hex');
86+
87+
// Add input with Taproot specific fields
88+
psbt.addInput({
89+
hash: txid,
90+
index: 0,
91+
witnessUtxo: {
92+
value: satoshis,
93+
script: taprootData.output,
94+
},
95+
tapInternalKey: taprootData.signer.publicKey,
96+
});
97+
98+
// Add recipient output (with fee deduction)
99+
psbt.addOutput({
100+
address: recipient,
101+
value: satoshis - 1000 // Deduct transaction fee
102+
});
103+
104+
// Add optional OP_RETURN message
105+
addOpReturnOutput(psbt, message);
106+
107+
// Sign and finalize the transaction
108+
await psbt.signInput(0, taprootData.signer);
109+
psbt.finalizeAllInputs();
110+
111+
return psbt.extractTransaction().toHex();
112+
} catch (error) {
113+
console.error('Error in createTaprootTransaction:', error);
114+
throw error;
115+
}
116+
}
117+
118+
// Print debug information
119+
function printDebugInfo(data) {
120+
console.log('\nDebug Information:');
121+
for (const [key, value] of Object.entries(data)) {
122+
if (Buffer.isBuffer(value)) {
123+
console.log(`${key}: ${value.toString('hex')} (Buffer)`);
124+
} else if (typeof value === 'object' && value !== null) {
125+
console.log(`${key}: [Object]`);
126+
} else {
127+
console.log(`${key}: ${value}`);
128+
}
129+
}
130+
}
131+
132+
async function main() {
133+
try {
134+
// Create Taproot address
135+
const taprootData = createTaprootAddress();
136+
137+
console.log('Taproot Details:');
138+
console.log('Taproot Address:', taprootData.address);
139+
console.log('Public Key:', taprootData.signer.publicKey.toString('hex'));
140+
console.log('Output Script:', taprootData.output.toString('hex'));
141+
142+
// Output debug information
143+
printDebugInfo({
144+
network: network.messagePrefix,
145+
publicKeyType: taprootData.signer.publicKey.constructor.name,
146+
publicKeyLength: taprootData.signer.publicKey.length,
147+
hasPrivateKey: !!taprootData.signer.keyPair.privateKey,
148+
outputType: taprootData.output.constructor.name,
149+
outputLength: taprootData.output.length
150+
});
151+
152+
// Create transaction
153+
const recipientAddress = 'tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx';
154+
const tx = await createTaprootTransaction(taprootData, recipientAddress, 100000);
155+
156+
// Parse and print transaction details
157+
const decodedTx = bitcoin.Transaction.fromHex(tx);
158+
console.log('\nTransaction Details:');
159+
console.log('Version:', decodedTx.version);
160+
console.log('Inputs:', decodedTx.ins.length);
161+
console.log('Outputs:', decodedTx.outs.length);
162+
console.log('Transaction Hex:', tx);
163+
164+
// Print signature information
165+
console.log('\nSignature Details:');
166+
decodedTx.ins.forEach((input, index) => {
167+
console.log(`Input #${index} Witness:`, input.witness.map(w => w.toString('hex')));
168+
});
169+
} catch (error) {
170+
console.error('Main Error:', error);
171+
}
172+
}
173+
174+
main().catch(console.error);

Diff for: BTC/Advanced/Taproot/example/package.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "example",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"engines": {
7+
"node": ">=20.0.0"
8+
},
9+
"scripts": {
10+
"test": "echo \"Error: no test specified\" && exit 1"
11+
},
12+
"keywords": [],
13+
"author": "",
14+
"license": "ISC",
15+
"dependencies": {
16+
"@bitcoinerlab/secp256k1": "^1.2.0",
17+
"bitcoinjs-lib": "^6.1.5",
18+
"ecpair": "^3.0.0"
19+
}
20+
}

0 commit comments

Comments
 (0)