@btc-vision/transaction 1.7.12 → 1.7.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser/_version.d.ts +1 -1
- package/browser/index.js +1 -1
- package/browser/keypair/Address.d.ts +1 -0
- package/browser/transaction/mineable/TimelockGenerator.d.ts +3 -0
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/keypair/Address.d.ts +1 -0
- package/build/keypair/Address.js +10 -0
- package/build/keypair/Wallet.js +1 -2
- package/build/mnemonic/Mnemonic.js +3 -3
- package/build/transaction/builders/TransactionBuilder.js +10 -0
- package/build/transaction/mineable/TimelockGenerator.d.ts +3 -0
- package/build/transaction/mineable/TimelockGenerator.js +26 -7
- package/package.json +2 -2
- package/src/_version.ts +1 -1
- package/src/keypair/Address.ts +35 -1
- package/src/keypair/Wallet.ts +2 -2
- package/src/mnemonic/Mnemonic.ts +3 -0
- package/src/transaction/builders/TransactionBuilder.ts +12 -0
- package/src/transaction/mineable/TimelockGenerator.ts +47 -7
|
@@ -44,6 +44,7 @@ export declare class Address extends Uint8Array {
|
|
|
44
44
|
p2tr(network: Network): string;
|
|
45
45
|
p2wda(network: Network): IP2WSHAddress;
|
|
46
46
|
toCSV(duration: bigint | number | string, network: Network): IP2WSHAddress;
|
|
47
|
+
toCSVTweaked(duration: bigint | number | string, network: Network): string;
|
|
47
48
|
p2op(network: Network): string;
|
|
48
49
|
toTweakedHybridPublicKeyHex(): string;
|
|
49
50
|
toTweakedHybridPublicKeyBuffer(): Buffer;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { Network } from '@btc-vision/bitcoin';
|
|
2
2
|
import { IP2WSHAddress } from './IP2WSHAddress.js';
|
|
3
3
|
export declare class TimeLockGenerator {
|
|
4
|
+
private static readonly UNSPENDABLE_INTERNAL_KEY;
|
|
4
5
|
private static readonly CSV_BLOCKS;
|
|
5
6
|
static generateTimeLockAddress(publicKey: Buffer, network?: Network, csvBlocks?: number): IP2WSHAddress;
|
|
7
|
+
static generateTimeLockAddressP2TR(publicKey: Buffer, network?: Network, csvBlocks?: number): string;
|
|
8
|
+
private static generateTimeLockScript;
|
|
6
9
|
}
|
package/build/_version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "1.7.
|
|
1
|
+
export declare const version = "1.7.14";
|
package/build/_version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.7.
|
|
1
|
+
export const version = '1.7.14';
|
|
@@ -44,6 +44,7 @@ export declare class Address extends Uint8Array {
|
|
|
44
44
|
p2tr(network: Network): string;
|
|
45
45
|
p2wda(network: Network): IP2WSHAddress;
|
|
46
46
|
toCSV(duration: bigint | number | string, network: Network): IP2WSHAddress;
|
|
47
|
+
toCSVTweaked(duration: bigint | number | string, network: Network): string;
|
|
47
48
|
p2op(network: Network): string;
|
|
48
49
|
toTweakedHybridPublicKeyHex(): string;
|
|
49
50
|
toTweakedHybridPublicKeyBuffer(): Buffer;
|
package/build/keypair/Address.js
CHANGED
|
@@ -351,6 +351,16 @@ export class Address extends Uint8Array {
|
|
|
351
351
|
const publicKeyBuffer = Buffer.from(__classPrivateFieldGet(this, _Address_originalPublicKey, "f"));
|
|
352
352
|
return TimeLockGenerator.generateTimeLockAddress(publicKeyBuffer, network, n);
|
|
353
353
|
}
|
|
354
|
+
toCSVTweaked(duration, network) {
|
|
355
|
+
const n = Number(duration);
|
|
356
|
+
if (n < 1 || n > 65535) {
|
|
357
|
+
throw new Error('CSV block number must be between 1 and 65535');
|
|
358
|
+
}
|
|
359
|
+
if (!__classPrivateFieldGet(this, _Address_originalPublicKey, "f")) {
|
|
360
|
+
throw new Error('Cannot create CSV address: public key not set');
|
|
361
|
+
}
|
|
362
|
+
return TimeLockGenerator.generateTimeLockAddressP2TR(this.tweakedPublicKeyToBuffer(), network, n);
|
|
363
|
+
}
|
|
354
364
|
p2op(network) {
|
|
355
365
|
if (__classPrivateFieldGet(this, _Address_p2op, "f") && __classPrivateFieldGet(this, _Address_network, "f") === network) {
|
|
356
366
|
return __classPrivateFieldGet(this, _Address_p2op, "f");
|
package/build/keypair/Wallet.js
CHANGED
|
@@ -4,7 +4,6 @@ import { Address } from './Address.js';
|
|
|
4
4
|
import { BitcoinUtils } from '../utils/BitcoinUtils.js';
|
|
5
5
|
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
6
6
|
import { getMLDSAConfig, MLDSASecurityLevel, QuantumBIP32Factory, } from '@btc-vision/bip32';
|
|
7
|
-
import { randomBytes } from 'crypto';
|
|
8
7
|
initEccLib(ecc);
|
|
9
8
|
export class Wallet {
|
|
10
9
|
constructor(privateKeyOrWif, mldsaPrivateKeyOrBase58, network = networks.bitcoin, securityLevel = MLDSASecurityLevel.LEVEL2, chainCode) {
|
|
@@ -41,7 +40,7 @@ export class Wallet {
|
|
|
41
40
|
if (chainCode && chainCode.length !== 32) {
|
|
42
41
|
throw new Error('Chain code must be 32 bytes');
|
|
43
42
|
}
|
|
44
|
-
this._chainCode = chainCode ||
|
|
43
|
+
this._chainCode = chainCode || Buffer.alloc(32);
|
|
45
44
|
this._mldsaKeypair = QuantumBIP32Factory.fromPrivateKey(mldsaPrivateKeyBuffer, this._chainCode, this.network, securityLevel);
|
|
46
45
|
}
|
|
47
46
|
else {
|
|
@@ -55,7 +55,7 @@ export class Mnemonic {
|
|
|
55
55
|
if (!quantumChild.privateKey) {
|
|
56
56
|
throw new Error(`Failed to derive quantum private key at index ${index}`);
|
|
57
57
|
}
|
|
58
|
-
return new Wallet(Buffer.from(classicalChild.privateKey).toString('hex'), Buffer.from(quantumChild.privateKey).toString('hex'), this._network, this._securityLevel);
|
|
58
|
+
return new Wallet(Buffer.from(classicalChild.privateKey).toString('hex'), Buffer.from(quantumChild.privateKey).toString('hex'), this._network, this._securityLevel, Buffer.from(this._quantumRoot.chainCode));
|
|
59
59
|
}
|
|
60
60
|
deriveUnisat(addressType = AddressTypes.P2TR, index = 0, account = 0, isChange = false) {
|
|
61
61
|
let purpose;
|
|
@@ -87,7 +87,7 @@ export class Mnemonic {
|
|
|
87
87
|
if (!quantumChild.privateKey) {
|
|
88
88
|
throw new Error(`Failed to derive quantum private key at path ${quantumPath}`);
|
|
89
89
|
}
|
|
90
|
-
return new Wallet(Buffer.from(classicalChild.privateKey).toString('hex'), Buffer.from(quantumChild.privateKey).toString('hex'), this._network, this._securityLevel);
|
|
90
|
+
return new Wallet(Buffer.from(classicalChild.privateKey).toString('hex'), Buffer.from(quantumChild.privateKey).toString('hex'), this._network, this._securityLevel, Buffer.from(this._quantumRoot.chainCode));
|
|
91
91
|
}
|
|
92
92
|
deriveMultipleUnisat(addressType = AddressTypes.P2TR, count = 5, startIndex = 0, account = 0, isChange = false) {
|
|
93
93
|
const wallets = [];
|
|
@@ -112,7 +112,7 @@ export class Mnemonic {
|
|
|
112
112
|
if (!quantumChild.privateKey) {
|
|
113
113
|
throw new Error(`Failed to derive quantum private key at path ${quantumPath}`);
|
|
114
114
|
}
|
|
115
|
-
return new Wallet(Buffer.from(classicalChild.privateKey).toString('hex'), Buffer.from(quantumChild.privateKey).toString('hex'), this._network, this._securityLevel);
|
|
115
|
+
return new Wallet(Buffer.from(classicalChild.privateKey).toString('hex'), Buffer.from(quantumChild.privateKey).toString('hex'), this._network, this._securityLevel, Buffer.from(this._quantumRoot.chainCode));
|
|
116
116
|
}
|
|
117
117
|
getClassicalRoot() {
|
|
118
118
|
return this._classicalRoot;
|
|
@@ -500,11 +500,16 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
500
500
|
throw new Error('Tweaked signer is not defined');
|
|
501
501
|
}
|
|
502
502
|
const tweakedKey = toXOnly(this.tweakedSigner.publicKey);
|
|
503
|
+
const originalKey = this.signer.publicKey;
|
|
504
|
+
if (originalKey.length !== 33) {
|
|
505
|
+
throw new Error('Original public key must be compressed (33 bytes)');
|
|
506
|
+
}
|
|
503
507
|
const chainId = getChainId(this.network);
|
|
504
508
|
const writer = new BinaryWriter();
|
|
505
509
|
writer.writeU8(MLDSASecurityLevel.LEVEL2);
|
|
506
510
|
writer.writeBytes(this.hashedPublicKey);
|
|
507
511
|
writer.writeBytes(tweakedKey);
|
|
512
|
+
writer.writeBytes(originalKey);
|
|
508
513
|
writer.writeBytes(BITCOIN_PROTOCOL_ID);
|
|
509
514
|
writer.writeBytes(chainId);
|
|
510
515
|
const message = writer.getBuffer();
|
|
@@ -524,6 +529,10 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
524
529
|
throw new Error('Tweaked signer is not defined');
|
|
525
530
|
}
|
|
526
531
|
const tweakedKey = toXOnly(this.tweakedSigner.publicKey);
|
|
532
|
+
const originalKey = this.signer.publicKey;
|
|
533
|
+
if (originalKey.length !== 33) {
|
|
534
|
+
throw new Error('Original public key must be compressed (33 bytes)');
|
|
535
|
+
}
|
|
527
536
|
const chainId = getChainId(this.network);
|
|
528
537
|
const level = getLevelFromPublicKeyLength(this.mldsaSigner.publicKey.length);
|
|
529
538
|
if (level !== MLDSASecurityLevel.LEVEL2) {
|
|
@@ -534,6 +543,7 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
534
543
|
writer.writeBytes(this.hashedPublicKey);
|
|
535
544
|
writer.writeBytes(this.mldsaSigner.publicKey);
|
|
536
545
|
writer.writeBytes(tweakedKey);
|
|
546
|
+
writer.writeBytes(originalKey);
|
|
537
547
|
writer.writeBytes(BITCOIN_PROTOCOL_ID);
|
|
538
548
|
writer.writeBytes(chainId);
|
|
539
549
|
const message = writer.getBuffer();
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { Network } from '@btc-vision/bitcoin';
|
|
2
2
|
import { IP2WSHAddress } from './IP2WSHAddress.js';
|
|
3
3
|
export declare class TimeLockGenerator {
|
|
4
|
+
private static readonly UNSPENDABLE_INTERNAL_KEY;
|
|
4
5
|
private static readonly CSV_BLOCKS;
|
|
5
6
|
static generateTimeLockAddress(publicKey: Buffer, network?: Network, csvBlocks?: number): IP2WSHAddress;
|
|
7
|
+
static generateTimeLockAddressP2TR(publicKey: Buffer, network?: Network, csvBlocks?: number): string;
|
|
8
|
+
private static generateTimeLockScript;
|
|
6
9
|
}
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import bitcoin, { networks, opcodes, script } from '@btc-vision/bitcoin';
|
|
2
2
|
export class TimeLockGenerator {
|
|
3
3
|
static generateTimeLockAddress(publicKey, network = networks.bitcoin, csvBlocks = TimeLockGenerator.CSV_BLOCKS) {
|
|
4
|
-
const witnessScript =
|
|
5
|
-
script.number.encode(csvBlocks),
|
|
6
|
-
opcodes.OP_CHECKSEQUENCEVERIFY,
|
|
7
|
-
opcodes.OP_DROP,
|
|
8
|
-
publicKey,
|
|
9
|
-
opcodes.OP_CHECKSIG,
|
|
10
|
-
]);
|
|
4
|
+
const witnessScript = this.generateTimeLockScript(publicKey, csvBlocks);
|
|
11
5
|
const p2wsh = bitcoin.payments.p2wsh({
|
|
12
6
|
redeem: { output: witnessScript },
|
|
13
7
|
network,
|
|
@@ -20,5 +14,30 @@ export class TimeLockGenerator {
|
|
|
20
14
|
witnessScript: witnessScript,
|
|
21
15
|
};
|
|
22
16
|
}
|
|
17
|
+
static generateTimeLockAddressP2TR(publicKey, network = networks.bitcoin, csvBlocks = TimeLockGenerator.CSV_BLOCKS) {
|
|
18
|
+
if (publicKey.length !== 32) {
|
|
19
|
+
throw new Error('Public key must be 32 bytes for Taproot');
|
|
20
|
+
}
|
|
21
|
+
const witnessScript = this.generateTimeLockScript(publicKey, csvBlocks);
|
|
22
|
+
const taproot = bitcoin.payments.p2tr({
|
|
23
|
+
redeem: { output: witnessScript },
|
|
24
|
+
network,
|
|
25
|
+
internalPubkey: TimeLockGenerator.UNSPENDABLE_INTERNAL_KEY,
|
|
26
|
+
});
|
|
27
|
+
if (!taproot.address) {
|
|
28
|
+
throw new Error('Failed to generate P2TR address');
|
|
29
|
+
}
|
|
30
|
+
return taproot.address;
|
|
31
|
+
}
|
|
32
|
+
static generateTimeLockScript(publicKey, csvBlocks = TimeLockGenerator.CSV_BLOCKS) {
|
|
33
|
+
return script.compile([
|
|
34
|
+
script.number.encode(csvBlocks),
|
|
35
|
+
opcodes.OP_CHECKSEQUENCEVERIFY,
|
|
36
|
+
opcodes.OP_DROP,
|
|
37
|
+
publicKey,
|
|
38
|
+
opcodes.OP_CHECKSIG,
|
|
39
|
+
]);
|
|
40
|
+
}
|
|
23
41
|
}
|
|
42
|
+
TimeLockGenerator.UNSPENDABLE_INTERNAL_KEY = Buffer.from('50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0', 'hex');
|
|
24
43
|
TimeLockGenerator.CSV_BLOCKS = 75;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@btc-vision/transaction",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.7.
|
|
4
|
+
"version": "1.7.14",
|
|
5
5
|
"author": "BlobMaster41",
|
|
6
6
|
"description": "OPNet transaction library allows you to create and sign transactions for the OPNet network.",
|
|
7
7
|
"engines": {
|
|
@@ -95,13 +95,13 @@
|
|
|
95
95
|
"dependencies": {
|
|
96
96
|
"@babel/plugin-proposal-object-rest-spread": "^7.20.7",
|
|
97
97
|
"@bitcoinerlab/secp256k1": "^1.2.0",
|
|
98
|
-
"@noble/curves": "^1.9.7",
|
|
99
98
|
"@btc-vision/bip32": "^6.0.3",
|
|
100
99
|
"@btc-vision/bitcoin": "^6.4.11",
|
|
101
100
|
"@btc-vision/bitcoin-rpc": "^1.0.5",
|
|
102
101
|
"@btc-vision/logger": "^1.0.7",
|
|
103
102
|
"@btc-vision/post-quantum": "^0.5.3",
|
|
104
103
|
"@eslint/js": "^9.39.1",
|
|
104
|
+
"@noble/curves": "^1.9.7",
|
|
105
105
|
"@noble/secp256k1": "^3.0.0",
|
|
106
106
|
"assert": "^2.1.0",
|
|
107
107
|
"babel-loader": "^10.0.0",
|
package/src/_version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.7.
|
|
1
|
+
export const version = '1.7.14';
|
package/src/keypair/Address.ts
CHANGED
|
@@ -645,7 +645,7 @@ export class Address extends Uint8Array {
|
|
|
645
645
|
}
|
|
646
646
|
|
|
647
647
|
/**
|
|
648
|
-
* Generate a P2WSH address with CSV (CheckSequenceVerify)
|
|
648
|
+
* Generate a P2WSH address with CSV (CheckSequenceVerify) time lock
|
|
649
649
|
* The resulting address can only be spent after the specified number of blocks
|
|
650
650
|
* have passed since the UTXO was created.
|
|
651
651
|
*
|
|
@@ -677,6 +677,40 @@ export class Address extends Uint8Array {
|
|
|
677
677
|
return TimeLockGenerator.generateTimeLockAddress(publicKeyBuffer, network, n);
|
|
678
678
|
}
|
|
679
679
|
|
|
680
|
+
/**
|
|
681
|
+
* Generate a P2TR address with CSV (CheckSequenceVerify) time lock
|
|
682
|
+
* The resulting address can only be spent after the specified number of blocks
|
|
683
|
+
* have passed since the UTXO was created.
|
|
684
|
+
*
|
|
685
|
+
* @param {bigint | number | string} duration - The number of blocks that must pass before spending (1-65535)
|
|
686
|
+
* @param {Network} network - The Bitcoin network to use
|
|
687
|
+
* @returns {IP2WSHAddress} The timelocked address and its witness script
|
|
688
|
+
* @throws {Error} If the block number is out of range or public key is not available
|
|
689
|
+
*/
|
|
690
|
+
public toCSVTweaked(duration: bigint | number | string, network: Network): string {
|
|
691
|
+
const n = Number(duration);
|
|
692
|
+
|
|
693
|
+
// First, let's validate the block number to ensure it's within the valid range
|
|
694
|
+
// CSV uses sequence numbers, which have special encoding for block-based locks
|
|
695
|
+
if (n < 1 || n > 65535) {
|
|
696
|
+
throw new Error('CSV block number must be between 1 and 65535');
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// We need the original public key in compressed format for the script
|
|
700
|
+
// Your class stores this in #originalPublicKey when a key is set
|
|
701
|
+
if (!this.#originalPublicKey) {
|
|
702
|
+
throw new Error('Cannot create CSV address: public key not set');
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// Now we can use your TimeLockGenerator to create the timelocked address
|
|
706
|
+
// Converting bigint to number is safe here because we've already validated the range
|
|
707
|
+
return TimeLockGenerator.generateTimeLockAddressP2TR(
|
|
708
|
+
this.tweakedPublicKeyToBuffer(),
|
|
709
|
+
network,
|
|
710
|
+
n,
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
|
|
680
714
|
/**
|
|
681
715
|
* Returns the OPNet address encoded in bech32m format, derived from the SHA256 hash of the ML-DSA public key
|
|
682
716
|
* (which is what the Address internally stores).
|
package/src/keypair/Wallet.ts
CHANGED
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
QuantumBIP32Factory,
|
|
12
12
|
QuantumBIP32Interface,
|
|
13
13
|
} from '@btc-vision/bip32';
|
|
14
|
-
import { randomBytes } from 'crypto';
|
|
15
14
|
|
|
16
15
|
initEccLib(ecc);
|
|
17
16
|
|
|
@@ -168,7 +167,8 @@ export class Wallet {
|
|
|
168
167
|
if (chainCode && chainCode.length !== 32) {
|
|
169
168
|
throw new Error('Chain code must be 32 bytes');
|
|
170
169
|
}
|
|
171
|
-
|
|
170
|
+
|
|
171
|
+
this._chainCode = chainCode || Buffer.alloc(32);
|
|
172
172
|
|
|
173
173
|
// Create QuantumBIP32Interface from private key and chain code
|
|
174
174
|
// Pass network to ensure network-specific derivation
|
package/src/mnemonic/Mnemonic.ts
CHANGED
|
@@ -244,6 +244,7 @@ export class Mnemonic {
|
|
|
244
244
|
Buffer.from(quantumChild.privateKey).toString('hex'),
|
|
245
245
|
this._network,
|
|
246
246
|
this._securityLevel,
|
|
247
|
+
Buffer.from(this._quantumRoot.chainCode),
|
|
247
248
|
);
|
|
248
249
|
}
|
|
249
250
|
|
|
@@ -313,6 +314,7 @@ export class Mnemonic {
|
|
|
313
314
|
Buffer.from(quantumChild.privateKey).toString('hex'),
|
|
314
315
|
this._network,
|
|
315
316
|
this._securityLevel,
|
|
317
|
+
Buffer.from(this._quantumRoot.chainCode),
|
|
316
318
|
);
|
|
317
319
|
}
|
|
318
320
|
|
|
@@ -393,6 +395,7 @@ export class Mnemonic {
|
|
|
393
395
|
Buffer.from(quantumChild.privateKey).toString('hex'),
|
|
394
396
|
this._network,
|
|
395
397
|
this._securityLevel,
|
|
398
|
+
Buffer.from(this._quantumRoot.chainCode),
|
|
396
399
|
);
|
|
397
400
|
}
|
|
398
401
|
|
|
@@ -889,6 +889,11 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
|
|
|
889
889
|
}
|
|
890
890
|
|
|
891
891
|
const tweakedKey = toXOnly(this.tweakedSigner.publicKey);
|
|
892
|
+
const originalKey = this.signer.publicKey;
|
|
893
|
+
if (originalKey.length !== 33) {
|
|
894
|
+
throw new Error('Original public key must be compressed (33 bytes)');
|
|
895
|
+
}
|
|
896
|
+
|
|
892
897
|
const chainId = getChainId(this.network);
|
|
893
898
|
|
|
894
899
|
const writer = new BinaryWriter();
|
|
@@ -897,6 +902,7 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
|
|
|
897
902
|
writer.writeU8(MLDSASecurityLevel.LEVEL2);
|
|
898
903
|
writer.writeBytes(this.hashedPublicKey);
|
|
899
904
|
writer.writeBytes(tweakedKey);
|
|
905
|
+
writer.writeBytes(originalKey);
|
|
900
906
|
writer.writeBytes(BITCOIN_PROTOCOL_ID);
|
|
901
907
|
writer.writeBytes(chainId);
|
|
902
908
|
|
|
@@ -923,6 +929,11 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
|
|
|
923
929
|
}
|
|
924
930
|
|
|
925
931
|
const tweakedKey = toXOnly(this.tweakedSigner.publicKey);
|
|
932
|
+
const originalKey = this.signer.publicKey;
|
|
933
|
+
if (originalKey.length !== 33) {
|
|
934
|
+
throw new Error('Original public key must be compressed (33 bytes)');
|
|
935
|
+
}
|
|
936
|
+
|
|
926
937
|
const chainId = getChainId(this.network);
|
|
927
938
|
const level = getLevelFromPublicKeyLength(this.mldsaSigner.publicKey.length);
|
|
928
939
|
|
|
@@ -935,6 +946,7 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
|
|
|
935
946
|
writer.writeBytes(this.hashedPublicKey);
|
|
936
947
|
writer.writeBytes(this.mldsaSigner.publicKey);
|
|
937
948
|
writer.writeBytes(tweakedKey);
|
|
949
|
+
writer.writeBytes(originalKey);
|
|
938
950
|
writer.writeBytes(BITCOIN_PROTOCOL_ID);
|
|
939
951
|
writer.writeBytes(chainId);
|
|
940
952
|
|
|
@@ -2,6 +2,11 @@ import bitcoin, { Network, networks, opcodes, script } from '@btc-vision/bitcoin
|
|
|
2
2
|
import { IP2WSHAddress } from './IP2WSHAddress.js';
|
|
3
3
|
|
|
4
4
|
export class TimeLockGenerator {
|
|
5
|
+
private static readonly UNSPENDABLE_INTERNAL_KEY = Buffer.from(
|
|
6
|
+
'50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0',
|
|
7
|
+
'hex',
|
|
8
|
+
);
|
|
9
|
+
|
|
5
10
|
private static readonly CSV_BLOCKS = 75;
|
|
6
11
|
|
|
7
12
|
/**
|
|
@@ -13,13 +18,7 @@ export class TimeLockGenerator {
|
|
|
13
18
|
network: Network = networks.bitcoin,
|
|
14
19
|
csvBlocks: number = TimeLockGenerator.CSV_BLOCKS,
|
|
15
20
|
): IP2WSHAddress {
|
|
16
|
-
const witnessScript =
|
|
17
|
-
script.number.encode(csvBlocks),
|
|
18
|
-
opcodes.OP_CHECKSEQUENCEVERIFY,
|
|
19
|
-
opcodes.OP_DROP,
|
|
20
|
-
publicKey,
|
|
21
|
-
opcodes.OP_CHECKSIG,
|
|
22
|
-
]);
|
|
21
|
+
const witnessScript = this.generateTimeLockScript(publicKey, csvBlocks);
|
|
23
22
|
|
|
24
23
|
const p2wsh = bitcoin.payments.p2wsh({
|
|
25
24
|
redeem: { output: witnessScript },
|
|
@@ -35,4 +34,45 @@ export class TimeLockGenerator {
|
|
|
35
34
|
witnessScript: witnessScript,
|
|
36
35
|
};
|
|
37
36
|
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generate a P2TR address with CSV time lock
|
|
40
|
+
* Note: This uses Schnorr signatures
|
|
41
|
+
*/
|
|
42
|
+
public static generateTimeLockAddressP2TR(
|
|
43
|
+
publicKey: Buffer,
|
|
44
|
+
network: Network = networks.bitcoin,
|
|
45
|
+
csvBlocks: number = TimeLockGenerator.CSV_BLOCKS,
|
|
46
|
+
): string {
|
|
47
|
+
if (publicKey.length !== 32) {
|
|
48
|
+
throw new Error('Public key must be 32 bytes for Taproot');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const witnessScript = this.generateTimeLockScript(publicKey, csvBlocks);
|
|
52
|
+
|
|
53
|
+
const taproot = bitcoin.payments.p2tr({
|
|
54
|
+
redeem: { output: witnessScript },
|
|
55
|
+
network,
|
|
56
|
+
internalPubkey: TimeLockGenerator.UNSPENDABLE_INTERNAL_KEY,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (!taproot.address) {
|
|
60
|
+
throw new Error('Failed to generate P2TR address');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return taproot.address;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private static generateTimeLockScript(
|
|
67
|
+
publicKey: Buffer,
|
|
68
|
+
csvBlocks: number = TimeLockGenerator.CSV_BLOCKS,
|
|
69
|
+
): Buffer {
|
|
70
|
+
return script.compile([
|
|
71
|
+
script.number.encode(csvBlocks),
|
|
72
|
+
opcodes.OP_CHECKSEQUENCEVERIFY,
|
|
73
|
+
opcodes.OP_DROP,
|
|
74
|
+
publicKey,
|
|
75
|
+
opcodes.OP_CHECKSIG,
|
|
76
|
+
]);
|
|
77
|
+
}
|
|
38
78
|
}
|