@btc-vision/transaction 1.7.13 → 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/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 +1 -1
- package/src/_version.ts +1 -1
- package/src/keypair/Address.ts +35 -1
- 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");
|
|
@@ -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
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).
|
|
@@ -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
|
}
|