@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.
@@ -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
  }
@@ -1 +1 @@
1
- export declare const version = "1.7.13";
1
+ export declare const version = "1.7.14";
package/build/_version.js CHANGED
@@ -1 +1 @@
1
- export const version = '1.7.13';
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;
@@ -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 = script.compile([
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.13",
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": {
package/src/_version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '1.7.13';
1
+ export const version = '1.7.14';
@@ -645,7 +645,7 @@ export class Address extends Uint8Array {
645
645
  }
646
646
 
647
647
  /**
648
- * Generate a P2WSH address with CSV (CheckSequenceVerify) timelock
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 = script.compile([
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
  }