@btc-vision/transaction 1.7.19 → 1.7.22

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.
Files changed (92) hide show
  1. package/LICENSE +190 -21
  2. package/README.md +1 -1
  3. package/browser/_version.d.ts +1 -1
  4. package/browser/generators/builders/HashCommitmentGenerator.d.ts +49 -0
  5. package/browser/index.js +1 -1
  6. package/browser/keypair/Address.d.ts +3 -1
  7. package/browser/opnet.d.ts +6 -1
  8. package/browser/signer/AddressRotation.d.ts +12 -0
  9. package/browser/transaction/TransactionFactory.d.ts +14 -0
  10. package/browser/transaction/builders/ConsolidatedInteractionTransaction.d.ts +44 -0
  11. package/browser/transaction/enums/TransactionType.d.ts +3 -1
  12. package/browser/transaction/interfaces/IConsolidatedTransactionParameters.d.ts +31 -0
  13. package/browser/transaction/interfaces/ITransactionParameters.d.ts +2 -0
  14. package/browser/transaction/offline/OfflineTransactionManager.d.ts +69 -0
  15. package/browser/transaction/offline/TransactionReconstructor.d.ts +28 -0
  16. package/browser/transaction/offline/TransactionSerializer.d.ts +50 -0
  17. package/browser/transaction/offline/TransactionStateCapture.d.ts +52 -0
  18. package/browser/transaction/offline/index.d.ts +5 -0
  19. package/browser/transaction/offline/interfaces/ISerializableState.d.ts +62 -0
  20. package/browser/transaction/offline/interfaces/ITypeSpecificData.d.ts +62 -0
  21. package/browser/transaction/offline/interfaces/index.d.ts +2 -0
  22. package/browser/transaction/shared/TweakedTransaction.d.ts +12 -1
  23. package/browser/utxo/interfaces/IUTXO.d.ts +2 -0
  24. package/build/_version.d.ts +1 -1
  25. package/build/_version.js +1 -1
  26. package/build/generators/builders/HashCommitmentGenerator.d.ts +49 -0
  27. package/build/generators/builders/HashCommitmentGenerator.js +229 -0
  28. package/build/keypair/Address.d.ts +3 -1
  29. package/build/keypair/Address.js +87 -54
  30. package/build/opnet.d.ts +6 -1
  31. package/build/opnet.js +6 -1
  32. package/build/signer/AddressRotation.d.ts +12 -0
  33. package/build/signer/AddressRotation.js +16 -0
  34. package/build/transaction/TransactionFactory.d.ts +14 -0
  35. package/build/transaction/TransactionFactory.js +36 -0
  36. package/build/transaction/builders/ConsolidatedInteractionTransaction.d.ts +44 -0
  37. package/build/transaction/builders/ConsolidatedInteractionTransaction.js +259 -0
  38. package/build/transaction/builders/TransactionBuilder.js +2 -0
  39. package/build/transaction/enums/TransactionType.d.ts +3 -1
  40. package/build/transaction/enums/TransactionType.js +2 -0
  41. package/build/transaction/interfaces/IConsolidatedTransactionParameters.d.ts +31 -0
  42. package/build/transaction/interfaces/IConsolidatedTransactionParameters.js +1 -0
  43. package/build/transaction/interfaces/ITransactionParameters.d.ts +2 -0
  44. package/build/transaction/offline/OfflineTransactionManager.d.ts +69 -0
  45. package/build/transaction/offline/OfflineTransactionManager.js +255 -0
  46. package/build/transaction/offline/TransactionReconstructor.d.ts +28 -0
  47. package/build/transaction/offline/TransactionReconstructor.js +243 -0
  48. package/build/transaction/offline/TransactionSerializer.d.ts +50 -0
  49. package/build/transaction/offline/TransactionSerializer.js +700 -0
  50. package/build/transaction/offline/TransactionStateCapture.d.ts +52 -0
  51. package/build/transaction/offline/TransactionStateCapture.js +275 -0
  52. package/build/transaction/offline/index.d.ts +5 -0
  53. package/build/transaction/offline/index.js +5 -0
  54. package/build/transaction/offline/interfaces/ISerializableState.d.ts +62 -0
  55. package/build/transaction/offline/interfaces/ISerializableState.js +2 -0
  56. package/build/transaction/offline/interfaces/ITypeSpecificData.d.ts +62 -0
  57. package/build/transaction/offline/interfaces/ITypeSpecificData.js +19 -0
  58. package/build/transaction/offline/interfaces/index.d.ts +2 -0
  59. package/build/transaction/offline/interfaces/index.js +2 -0
  60. package/build/transaction/shared/TweakedTransaction.d.ts +12 -1
  61. package/build/transaction/shared/TweakedTransaction.js +75 -8
  62. package/build/utxo/interfaces/IUTXO.d.ts +2 -0
  63. package/documentation/README.md +5 -0
  64. package/documentation/offline-transaction-signing.md +650 -0
  65. package/documentation/transaction-building.md +603 -0
  66. package/package.json +2 -2
  67. package/src/_version.ts +1 -1
  68. package/src/generators/builders/HashCommitmentGenerator.ts +495 -0
  69. package/src/keypair/Address.ts +123 -70
  70. package/src/opnet.ts +8 -1
  71. package/src/signer/AddressRotation.ts +72 -0
  72. package/src/transaction/TransactionFactory.ts +90 -0
  73. package/src/transaction/builders/CancelTransaction.ts +4 -2
  74. package/src/transaction/builders/ConsolidatedInteractionTransaction.ts +568 -0
  75. package/src/transaction/builders/CustomScriptTransaction.ts +4 -2
  76. package/src/transaction/builders/MultiSignTransaction.ts +4 -2
  77. package/src/transaction/builders/TransactionBuilder.ts +8 -2
  78. package/src/transaction/enums/TransactionType.ts +2 -0
  79. package/src/transaction/interfaces/IConsolidatedTransactionParameters.ts +78 -0
  80. package/src/transaction/interfaces/ITransactionParameters.ts +8 -0
  81. package/src/transaction/offline/OfflineTransactionManager.ts +630 -0
  82. package/src/transaction/offline/TransactionReconstructor.ts +402 -0
  83. package/src/transaction/offline/TransactionSerializer.ts +920 -0
  84. package/src/transaction/offline/TransactionStateCapture.ts +469 -0
  85. package/src/transaction/offline/index.ts +8 -0
  86. package/src/transaction/offline/interfaces/ISerializableState.ts +141 -0
  87. package/src/transaction/offline/interfaces/ITypeSpecificData.ts +172 -0
  88. package/src/transaction/offline/interfaces/index.ts +2 -0
  89. package/src/transaction/shared/TweakedTransaction.ts +156 -9
  90. package/src/utxo/interfaces/IUTXO.ts +8 -0
  91. package/test/address-rotation.test.ts +553 -0
  92. package/test/offline-transaction.test.ts +2065 -0
@@ -0,0 +1,49 @@
1
+ import { Network } from '@btc-vision/bitcoin';
2
+ import { IHashCommittedP2WSH } from '../../transaction/interfaces/IConsolidatedTransactionParameters.js';
3
+ import { IP2WSHAddress } from '../../transaction/mineable/IP2WSHAddress.js';
4
+ import { Logger } from '@btc-vision/logger';
5
+ export declare class HashCommitmentGenerator extends Logger {
6
+ static readonly MAX_CHUNK_SIZE: number;
7
+ static readonly MAX_STACK_ITEMS: number;
8
+ static readonly MAX_WITNESS_SIZE: number;
9
+ static readonly MAX_STANDARD_WEIGHT: number;
10
+ static readonly MIN_OUTPUT_VALUE: bigint;
11
+ private static readonly BYTES_PER_COMMITMENT;
12
+ private static readonly SIG_CHECK_BYTES;
13
+ private static readonly WITNESS_FIXED_OVERHEAD;
14
+ private static readonly WITNESS_PER_CHUNK_OVERHEAD;
15
+ static readonly MAX_CHUNKS_PER_OUTPUT: number;
16
+ private static readonly INPUT_BASE_WEIGHT;
17
+ private static readonly INPUT_WITNESS_WEIGHT_MAX;
18
+ static readonly WEIGHT_PER_INPUT: number;
19
+ readonly logColor: string;
20
+ private readonly publicKey;
21
+ private readonly network;
22
+ constructor(publicKey: Buffer, network?: Network);
23
+ static calculateMaxInputsPerTx(): number;
24
+ static calculateMaxDataPerTx(): number;
25
+ static estimateOutputCount(dataSize: number): number;
26
+ static estimateChunkCount(dataSize: number): number;
27
+ static validateHashCommittedScript(witnessScript: Buffer): boolean;
28
+ static extractDataHashes(witnessScript: Buffer): Buffer[] | null;
29
+ static extractPublicKey(witnessScript: Buffer): Buffer | null;
30
+ static verifyChunkCommitments(dataChunks: Buffer[], witnessScript: Buffer): boolean;
31
+ static estimateFees(dataSize: number, feeRate: number, compressionRatio?: number): {
32
+ compressedSize: number;
33
+ outputCount: number;
34
+ chunkCount: number;
35
+ setupVBytes: number;
36
+ revealVBytes: number;
37
+ setupFee: bigint;
38
+ revealFee: bigint;
39
+ totalFee: bigint;
40
+ outputsValue: bigint;
41
+ totalCost: bigint;
42
+ };
43
+ hashChunk(data: Buffer): Buffer;
44
+ generateWitnessScript(dataHashes: Buffer[]): Buffer;
45
+ generateP2WSHAddress(witnessScript: Buffer): IP2WSHAddress & {
46
+ scriptPubKey: Buffer;
47
+ };
48
+ prepareChunks(data: Buffer, maxChunkSize?: number): IHashCommittedP2WSH[];
49
+ }
@@ -0,0 +1,229 @@
1
+ import { crypto, networks, opcodes, payments, script } from '@btc-vision/bitcoin';
2
+ import { Logger } from '@btc-vision/logger';
3
+ export class HashCommitmentGenerator extends Logger {
4
+ constructor(publicKey, network = networks.bitcoin) {
5
+ super();
6
+ this.logColor = '#4a90d9';
7
+ if (publicKey.length !== 33) {
8
+ throw new Error('Public key must be 33 bytes (compressed)');
9
+ }
10
+ this.publicKey = publicKey;
11
+ this.network = network;
12
+ }
13
+ static calculateMaxInputsPerTx() {
14
+ const txOverhead = 40;
15
+ const outputOverhead = 200;
16
+ const availableWeight = HashCommitmentGenerator.MAX_STANDARD_WEIGHT - txOverhead - outputOverhead;
17
+ return Math.floor(availableWeight / HashCommitmentGenerator.WEIGHT_PER_INPUT);
18
+ }
19
+ static calculateMaxDataPerTx() {
20
+ return (HashCommitmentGenerator.calculateMaxInputsPerTx() *
21
+ HashCommitmentGenerator.MAX_CHUNKS_PER_OUTPUT *
22
+ HashCommitmentGenerator.MAX_CHUNK_SIZE);
23
+ }
24
+ static estimateOutputCount(dataSize) {
25
+ return Math.ceil(dataSize /
26
+ (HashCommitmentGenerator.MAX_CHUNKS_PER_OUTPUT *
27
+ HashCommitmentGenerator.MAX_CHUNK_SIZE));
28
+ }
29
+ static estimateChunkCount(dataSize) {
30
+ return Math.ceil(dataSize / HashCommitmentGenerator.MAX_CHUNK_SIZE);
31
+ }
32
+ static validateHashCommittedScript(witnessScript) {
33
+ try {
34
+ const decompiled = script.decompile(witnessScript);
35
+ if (!decompiled || decompiled.length < 5) {
36
+ return false;
37
+ }
38
+ const lastIdx = decompiled.length - 1;
39
+ if (decompiled[lastIdx] !== opcodes.OP_CHECKSIG) {
40
+ return false;
41
+ }
42
+ const pubkey = decompiled[lastIdx - 1];
43
+ if (!Buffer.isBuffer(pubkey) || pubkey.length !== 33) {
44
+ return false;
45
+ }
46
+ const hashParts = decompiled.slice(0, -2);
47
+ if (hashParts.length % 3 !== 0 || hashParts.length === 0) {
48
+ return false;
49
+ }
50
+ for (let i = 0; i < hashParts.length; i += 3) {
51
+ const hash = hashParts[i + 1];
52
+ if (hashParts[i] !== opcodes.OP_HASH160 ||
53
+ !Buffer.isBuffer(hash) ||
54
+ hash.length !== 20 ||
55
+ hashParts[i + 2] !== opcodes.OP_EQUALVERIFY) {
56
+ return false;
57
+ }
58
+ }
59
+ return true;
60
+ }
61
+ catch {
62
+ return false;
63
+ }
64
+ }
65
+ static extractDataHashes(witnessScript) {
66
+ try {
67
+ const decompiled = script.decompile(witnessScript);
68
+ if (!decompiled ||
69
+ !HashCommitmentGenerator.validateHashCommittedScript(witnessScript)) {
70
+ return null;
71
+ }
72
+ const hashParts = decompiled.slice(0, -2);
73
+ const hashes = [];
74
+ for (let i = 0; i < hashParts.length; i += 3) {
75
+ hashes.push(hashParts[i + 1]);
76
+ }
77
+ return hashes.reverse();
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ }
83
+ static extractPublicKey(witnessScript) {
84
+ try {
85
+ const decompiled = script.decompile(witnessScript);
86
+ if (!decompiled ||
87
+ !HashCommitmentGenerator.validateHashCommittedScript(witnessScript)) {
88
+ return null;
89
+ }
90
+ return decompiled[decompiled.length - 2];
91
+ }
92
+ catch {
93
+ return null;
94
+ }
95
+ }
96
+ static verifyChunkCommitments(dataChunks, witnessScript) {
97
+ const committedHashes = HashCommitmentGenerator.extractDataHashes(witnessScript);
98
+ if (!committedHashes || committedHashes.length !== dataChunks.length) {
99
+ return false;
100
+ }
101
+ for (let i = 0; i < dataChunks.length; i++) {
102
+ const actualHash = crypto.hash160(dataChunks[i]);
103
+ if (!committedHashes[i].equals(actualHash)) {
104
+ return false;
105
+ }
106
+ }
107
+ return true;
108
+ }
109
+ static estimateFees(dataSize, feeRate, compressionRatio = 0.7) {
110
+ const compressedSize = Math.ceil(dataSize * compressionRatio);
111
+ const outputCount = HashCommitmentGenerator.estimateOutputCount(compressedSize);
112
+ const chunkCount = HashCommitmentGenerator.estimateChunkCount(compressedSize);
113
+ const setupInputVBytes = 2 * 58;
114
+ const setupOutputVBytes = outputCount * 43 + 43;
115
+ const setupOverhead = 11;
116
+ const setupVBytes = setupOverhead + setupInputVBytes + setupOutputVBytes;
117
+ const revealWeight = 40 + outputCount * HashCommitmentGenerator.WEIGHT_PER_INPUT + 200;
118
+ const revealVBytes = Math.ceil(revealWeight / 4);
119
+ const setupFee = BigInt(Math.ceil(setupVBytes * feeRate));
120
+ const revealFee = BigInt(Math.ceil(revealVBytes * feeRate));
121
+ const totalFee = setupFee + revealFee;
122
+ const outputsValue = BigInt(outputCount) * HashCommitmentGenerator.MIN_OUTPUT_VALUE;
123
+ const totalCost = totalFee + outputsValue;
124
+ return {
125
+ compressedSize,
126
+ outputCount,
127
+ chunkCount,
128
+ setupVBytes,
129
+ revealVBytes,
130
+ setupFee,
131
+ revealFee,
132
+ totalFee,
133
+ outputsValue,
134
+ totalCost,
135
+ };
136
+ }
137
+ hashChunk(data) {
138
+ return crypto.hash160(data);
139
+ }
140
+ generateWitnessScript(dataHashes) {
141
+ if (dataHashes.length === 0) {
142
+ throw new Error('At least one data hash is required');
143
+ }
144
+ if (dataHashes.length > HashCommitmentGenerator.MAX_CHUNKS_PER_OUTPUT) {
145
+ throw new Error(`Too many chunks: ${dataHashes.length} exceeds limit of ${HashCommitmentGenerator.MAX_CHUNKS_PER_OUTPUT}`);
146
+ }
147
+ for (const hash of dataHashes) {
148
+ if (hash.length !== 20) {
149
+ throw new Error(`HASH160 requires 20-byte hash, got ${hash.length}`);
150
+ }
151
+ }
152
+ const scriptParts = [];
153
+ for (let i = dataHashes.length - 1; i >= 0; i--) {
154
+ scriptParts.push(opcodes.OP_HASH160);
155
+ scriptParts.push(dataHashes[i]);
156
+ scriptParts.push(opcodes.OP_EQUALVERIFY);
157
+ }
158
+ scriptParts.push(this.publicKey);
159
+ scriptParts.push(opcodes.OP_CHECKSIG);
160
+ return script.compile(scriptParts);
161
+ }
162
+ generateP2WSHAddress(witnessScript) {
163
+ const p2wsh = payments.p2wsh({
164
+ redeem: { output: witnessScript },
165
+ network: this.network,
166
+ });
167
+ if (!p2wsh.address || !p2wsh.output) {
168
+ throw new Error('Failed to generate P2WSH address');
169
+ }
170
+ return {
171
+ address: p2wsh.address,
172
+ witnessScript,
173
+ scriptPubKey: p2wsh.output,
174
+ };
175
+ }
176
+ prepareChunks(data, maxChunkSize = HashCommitmentGenerator.MAX_CHUNK_SIZE) {
177
+ if (maxChunkSize > HashCommitmentGenerator.MAX_CHUNK_SIZE) {
178
+ throw new Error(`Chunk size ${maxChunkSize} exceeds P2WSH stack item limit of ${HashCommitmentGenerator.MAX_CHUNK_SIZE}`);
179
+ }
180
+ if (data.length === 0) {
181
+ throw new Error('Data cannot be empty');
182
+ }
183
+ const allChunks = [];
184
+ let offset = 0;
185
+ while (offset < data.length) {
186
+ const chunkSize = Math.min(maxChunkSize, data.length - offset);
187
+ allChunks.push(Buffer.from(data.subarray(offset, offset + chunkSize)));
188
+ offset += chunkSize;
189
+ }
190
+ const outputs = [];
191
+ let chunkIndex = 0;
192
+ while (chunkIndex < allChunks.length) {
193
+ const chunksForThisOutput = allChunks.slice(chunkIndex, chunkIndex + HashCommitmentGenerator.MAX_CHUNKS_PER_OUTPUT);
194
+ const dataChunks = chunksForThisOutput;
195
+ const dataHashes = dataChunks.map((chunk) => this.hashChunk(chunk));
196
+ const witnessScript = this.generateWitnessScript(dataHashes);
197
+ const p2wsh = this.generateP2WSHAddress(witnessScript);
198
+ outputs.push({
199
+ address: p2wsh.address,
200
+ witnessScript: p2wsh.witnessScript,
201
+ scriptPubKey: p2wsh.scriptPubKey,
202
+ dataHashes,
203
+ dataChunks,
204
+ chunkStartIndex: chunkIndex,
205
+ });
206
+ chunkIndex += chunksForThisOutput.length;
207
+ }
208
+ const totalChunks = allChunks.length;
209
+ this.log(`Prepared ${outputs.length} P2WSH outputs with ${totalChunks} chunks ` +
210
+ `(${data.length} bytes, ~${Math.ceil(data.length / outputs.length)} bytes/output)`);
211
+ return outputs;
212
+ }
213
+ }
214
+ HashCommitmentGenerator.MAX_CHUNK_SIZE = 80;
215
+ HashCommitmentGenerator.MAX_STACK_ITEMS = 100;
216
+ HashCommitmentGenerator.MAX_WITNESS_SIZE = 1650;
217
+ HashCommitmentGenerator.MAX_STANDARD_WEIGHT = 400000;
218
+ HashCommitmentGenerator.MIN_OUTPUT_VALUE = 330n;
219
+ HashCommitmentGenerator.BYTES_PER_COMMITMENT = 23;
220
+ HashCommitmentGenerator.SIG_CHECK_BYTES = 35;
221
+ HashCommitmentGenerator.WITNESS_FIXED_OVERHEAD = 1 + 73 + 3 + 35;
222
+ HashCommitmentGenerator.WITNESS_PER_CHUNK_OVERHEAD = HashCommitmentGenerator.MAX_CHUNK_SIZE + 1 + HashCommitmentGenerator.BYTES_PER_COMMITMENT;
223
+ HashCommitmentGenerator.MAX_CHUNKS_PER_OUTPUT = Math.floor((HashCommitmentGenerator.MAX_WITNESS_SIZE -
224
+ HashCommitmentGenerator.WITNESS_FIXED_OVERHEAD) /
225
+ HashCommitmentGenerator.WITNESS_PER_CHUNK_OVERHEAD);
226
+ HashCommitmentGenerator.INPUT_BASE_WEIGHT = 164;
227
+ HashCommitmentGenerator.INPUT_WITNESS_WEIGHT_MAX = HashCommitmentGenerator.MAX_WITNESS_SIZE;
228
+ HashCommitmentGenerator.WEIGHT_PER_INPUT = HashCommitmentGenerator.INPUT_BASE_WEIGHT +
229
+ HashCommitmentGenerator.INPUT_WITNESS_WEIGHT_MAX;
@@ -3,7 +3,6 @@ import { IP2WSHAddress } from '../transaction/mineable/IP2WSHAddress.js';
3
3
  import { MLDSASecurityLevel } from '@btc-vision/bip32';
4
4
  export declare class Address extends Uint8Array {
5
5
  #private;
6
- private legacyPublicKey;
7
6
  constructor(mldsaPublicKey?: ArrayLike<number>, publicKeyOrTweak?: ArrayLike<number>);
8
7
  get mldsaLevel(): MLDSASecurityLevel | undefined;
9
8
  set mldsaLevel(level: MLDSASecurityLevel);
@@ -11,6 +10,7 @@ export declare class Address extends Uint8Array {
11
10
  set originalMDLSAPublicKey(key: Buffer | Uint8Array);
12
11
  get originalPublicKey(): Uint8Array | undefined;
13
12
  get mldsaPublicKey(): Uint8Array | undefined;
13
+ private get legacyPublicKey();
14
14
  private get keyPair();
15
15
  static dead(): Address;
16
16
  static fromString(mldsaPublicKey: string, legacyPublicKey?: string): Address;
@@ -48,5 +48,7 @@ export declare class Address extends Uint8Array {
48
48
  p2op(network: Network): string;
49
49
  toTweakedHybridPublicKeyHex(): string;
50
50
  toTweakedHybridPublicKeyBuffer(): Buffer;
51
+ private setMldsaKey;
52
+ private ensureLegacyProcessed;
51
53
  private autoFormat;
52
54
  }
@@ -1,15 +1,15 @@
1
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
- };
6
1
  var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
7
2
  if (kind === "m") throw new TypeError("Private method is not writable");
8
3
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
9
4
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
5
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
6
  };
12
- var _Address_p2tr, _Address_p2op, _Address_network, _Address_originalPublicKey, _Address_keyPair, _Address_uncompressed, _Address_tweakedUncompressed, _Address_p2wda, _Address_mldsaPublicKey, _Address_cachedBigInt, _Address_cachedUint64Array, _Address_originalMDLSAPublicKey, _Address_mldsaLevel;
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _Address_p2tr, _Address_p2op, _Address_network, _Address_originalPublicKey, _Address_keyPair, _Address_uncompressed, _Address_tweakedUncompressed, _Address_p2wda, _Address_mldsaPublicKey, _Address_cachedBigInt, _Address_cachedUint64Array, _Address_originalMDLSAPublicKey, _Address_mldsaLevel, _Address_pendingLegacyKey, _Address_legacyProcessed, _Address_legacyPublicKey;
13
13
  import { decompressPublicKey, toXOnly } from '@btc-vision/bitcoin';
14
14
  import { ADDRESS_BYTE_LENGTH } from '../utils/lengths.js';
15
15
  import { AddressVerificator } from './AddressVerificator.js';
@@ -35,14 +35,17 @@ export class Address extends Uint8Array {
35
35
  _Address_cachedUint64Array.set(this, void 0);
36
36
  _Address_originalMDLSAPublicKey.set(this, void 0);
37
37
  _Address_mldsaLevel.set(this, void 0);
38
+ _Address_pendingLegacyKey.set(this, void 0);
39
+ _Address_legacyProcessed.set(this, false);
40
+ _Address_legacyPublicKey.set(this, void 0);
38
41
  if (!mldsaPublicKey) {
39
42
  return;
40
43
  }
41
44
  if (publicKeyOrTweak) {
42
- this.legacyPublicKey = new Uint8Array(publicKeyOrTweak.length);
43
- this.legacyPublicKey.set(publicKeyOrTweak);
45
+ __classPrivateFieldSet(this, _Address_pendingLegacyKey, new Uint8Array(publicKeyOrTweak.length), "f");
46
+ __classPrivateFieldGet(this, _Address_pendingLegacyKey, "f").set(publicKeyOrTweak);
44
47
  }
45
- this.set(mldsaPublicKey);
48
+ this.setMldsaKey(mldsaPublicKey);
46
49
  }
47
50
  get mldsaLevel() {
48
51
  return __classPrivateFieldGet(this, _Address_mldsaLevel, "f");
@@ -57,12 +60,18 @@ export class Address extends Uint8Array {
57
60
  __classPrivateFieldSet(this, _Address_originalMDLSAPublicKey, new Uint8Array(key), "f");
58
61
  }
59
62
  get originalPublicKey() {
63
+ this.ensureLegacyProcessed();
60
64
  return __classPrivateFieldGet(this, _Address_originalPublicKey, "f");
61
65
  }
62
66
  get mldsaPublicKey() {
63
67
  return __classPrivateFieldGet(this, _Address_mldsaPublicKey, "f");
64
68
  }
69
+ get legacyPublicKey() {
70
+ this.ensureLegacyProcessed();
71
+ return __classPrivateFieldGet(this, _Address_legacyPublicKey, "f");
72
+ }
65
73
  get keyPair() {
74
+ this.ensureLegacyProcessed();
66
75
  if (!__classPrivateFieldGet(this, _Address_keyPair, "f")) {
67
76
  throw new Error('Legacy public key not set for address');
68
77
  }
@@ -148,45 +157,52 @@ export class Address extends Uint8Array {
148
157
  return '0x' + Buffer.from(this).toString('hex');
149
158
  }
150
159
  tweakedToHex() {
151
- if (!this.legacyPublicKey) {
160
+ const key = this.legacyPublicKey;
161
+ if (!key) {
152
162
  throw new Error('Legacy public key not set');
153
163
  }
154
- return '0x' + Buffer.from(this.legacyPublicKey).toString('hex');
164
+ return '0x' + Buffer.from(key).toString('hex');
155
165
  }
156
166
  toBuffer() {
157
167
  return Buffer.from(this);
158
168
  }
159
169
  tweakedPublicKeyToBuffer() {
160
- if (!this.legacyPublicKey) {
170
+ const key = this.legacyPublicKey;
171
+ if (!key) {
161
172
  throw new Error('Legacy public key not set');
162
173
  }
163
- return Buffer.from(this.legacyPublicKey);
174
+ return Buffer.from(key);
164
175
  }
165
176
  toUncompressedHex() {
177
+ this.ensureLegacyProcessed();
166
178
  if (!__classPrivateFieldGet(this, _Address_uncompressed, "f")) {
167
179
  throw new Error('Legacy public key not set');
168
180
  }
169
181
  return '0x' + __classPrivateFieldGet(this, _Address_uncompressed, "f").uncompressed.toString('hex');
170
182
  }
171
183
  toUncompressedBuffer() {
184
+ this.ensureLegacyProcessed();
172
185
  if (!__classPrivateFieldGet(this, _Address_uncompressed, "f")) {
173
186
  throw new Error('Legacy public key not set');
174
187
  }
175
188
  return __classPrivateFieldGet(this, _Address_uncompressed, "f").uncompressed;
176
189
  }
177
190
  toHybridPublicKeyHex() {
191
+ this.ensureLegacyProcessed();
178
192
  if (!__classPrivateFieldGet(this, _Address_uncompressed, "f")) {
179
193
  throw new Error('Legacy public key not set');
180
194
  }
181
195
  return '0x' + __classPrivateFieldGet(this, _Address_uncompressed, "f").hybrid.toString('hex');
182
196
  }
183
197
  toHybridPublicKeyBuffer() {
198
+ this.ensureLegacyProcessed();
184
199
  if (!__classPrivateFieldGet(this, _Address_uncompressed, "f")) {
185
200
  throw new Error('Legacy public key not set');
186
201
  }
187
202
  return __classPrivateFieldGet(this, _Address_uncompressed, "f").hybrid;
188
203
  }
189
204
  originalPublicKeyBuffer() {
205
+ this.ensureLegacyProcessed();
190
206
  if (!__classPrivateFieldGet(this, _Address_originalPublicKey, "f")) {
191
207
  throw new Error('Legacy public key not set');
192
208
  }
@@ -244,44 +260,14 @@ export class Address extends Uint8Array {
244
260
  return false;
245
261
  }
246
262
  set(mldsaPublicKey) {
247
- if (this.legacyPublicKey) {
248
- const validLengths = [ADDRESS_BYTE_LENGTH, 33, 65];
249
- if (!validLengths.includes(this.legacyPublicKey.length)) {
250
- throw new Error(`Invalid public key length ${this.legacyPublicKey.length}`);
251
- }
252
- if (this.legacyPublicKey.length === ADDRESS_BYTE_LENGTH) {
253
- const buf = Buffer.alloc(ADDRESS_BYTE_LENGTH);
254
- buf.set(this.legacyPublicKey);
255
- __classPrivateFieldSet(this, _Address_tweakedUncompressed, ContractAddress.generateHybridKeyFromHash(buf), "f");
256
- }
257
- else {
258
- this.autoFormat(this.legacyPublicKey);
259
- }
260
- }
261
- if (mldsaPublicKey.length === ADDRESS_BYTE_LENGTH) {
262
- const buf = new Uint8Array(ADDRESS_BYTE_LENGTH);
263
- buf.set(mldsaPublicKey);
264
- super.set(buf);
265
- }
266
- else {
267
- const validMLDSALengths = [1312, 1952, 2592];
268
- if (!validMLDSALengths.includes(mldsaPublicKey.length)) {
269
- throw new Error(`Invalid ML-DSA public key length: ${mldsaPublicKey.length}. ` +
270
- `Expected 1312 (ML-DSA-44/LEVEL2), 1952 (ML-DSA-65/LEVEL3), or 2592 (ML-DSA-87/LEVEL5) bytes.`);
271
- }
272
- __classPrivateFieldSet(this, _Address_mldsaPublicKey, new Uint8Array(mldsaPublicKey.length), "f");
273
- __classPrivateFieldGet(this, _Address_mldsaPublicKey, "f").set(mldsaPublicKey);
274
- const hashedPublicKey = sha256(new Uint8Array(mldsaPublicKey));
275
- const buf = new Uint8Array(ADDRESS_BYTE_LENGTH);
276
- buf.set(hashedPublicKey);
277
- super.set(buf);
278
- }
263
+ this.setMldsaKey(mldsaPublicKey);
279
264
  }
280
265
  isValidLegacyPublicKey(network) {
281
- if (!this.legacyPublicKey) {
266
+ const key = this.legacyPublicKey;
267
+ if (!key) {
282
268
  throw new Error(`Legacy key not set.`);
283
269
  }
284
- return AddressVerificator.isValidPublicKey(Buffer.from(this.legacyPublicKey).toString('hex'), network);
270
+ return AddressVerificator.isValidPublicKey(Buffer.from(key).toString('hex'), network);
285
271
  }
286
272
  p2pk() {
287
273
  return this.toHex();
@@ -302,13 +288,14 @@ export class Address extends Uint8Array {
302
288
  return this.toHex();
303
289
  }
304
290
  p2tr(network) {
305
- if (!this.legacyPublicKey) {
306
- throw new Error('Legacy public key not set');
307
- }
308
291
  if (__classPrivateFieldGet(this, _Address_p2tr, "f") && __classPrivateFieldGet(this, _Address_network, "f") === network) {
309
292
  return __classPrivateFieldGet(this, _Address_p2tr, "f");
310
293
  }
311
- const p2trAddy = EcKeyPair.tweakedPubKeyBufferToAddress(this.legacyPublicKey, network);
294
+ const key = this.legacyPublicKey;
295
+ if (!key) {
296
+ throw new Error('Legacy public key not set');
297
+ }
298
+ const p2trAddy = EcKeyPair.tweakedPubKeyBufferToAddress(key, network);
312
299
  if (p2trAddy) {
313
300
  __classPrivateFieldSet(this, _Address_network, network, "f");
314
301
  __classPrivateFieldSet(this, _Address_p2tr, p2trAddy, "f");
@@ -320,6 +307,7 @@ export class Address extends Uint8Array {
320
307
  if (__classPrivateFieldGet(this, _Address_p2wda, "f") && __classPrivateFieldGet(this, _Address_network, "f") === network) {
321
308
  return __classPrivateFieldGet(this, _Address_p2wda, "f");
322
309
  }
310
+ this.ensureLegacyProcessed();
323
311
  if (!__classPrivateFieldGet(this, _Address_originalPublicKey, "f")) {
324
312
  throw new Error('Cannot create P2WDA address: public key not set');
325
313
  }
@@ -345,6 +333,7 @@ export class Address extends Uint8Array {
345
333
  if (n < 1 || n > 65535) {
346
334
  throw new Error('CSV block number must be between 1 and 65535');
347
335
  }
336
+ this.ensureLegacyProcessed();
348
337
  if (!__classPrivateFieldGet(this, _Address_originalPublicKey, "f")) {
349
338
  throw new Error('Cannot create CSV address: public key not set');
350
339
  }
@@ -356,6 +345,7 @@ export class Address extends Uint8Array {
356
345
  if (n < 1 || n > 65535) {
357
346
  throw new Error('CSV block number must be between 1 and 65535');
358
347
  }
348
+ this.ensureLegacyProcessed();
359
349
  if (!__classPrivateFieldGet(this, _Address_originalPublicKey, "f")) {
360
350
  throw new Error('Cannot create CSV address: public key not set');
361
351
  }
@@ -374,17 +364,60 @@ export class Address extends Uint8Array {
374
364
  throw new Error('ML-DSA public key not set');
375
365
  }
376
366
  toTweakedHybridPublicKeyHex() {
367
+ this.ensureLegacyProcessed();
377
368
  if (!__classPrivateFieldGet(this, _Address_tweakedUncompressed, "f")) {
378
369
  throw new Error('Legacy public key not set');
379
370
  }
380
371
  return '0x' + __classPrivateFieldGet(this, _Address_tweakedUncompressed, "f").toString('hex');
381
372
  }
382
373
  toTweakedHybridPublicKeyBuffer() {
374
+ this.ensureLegacyProcessed();
383
375
  if (!__classPrivateFieldGet(this, _Address_tweakedUncompressed, "f")) {
384
376
  throw new Error('Legacy public key not set');
385
377
  }
386
378
  return __classPrivateFieldGet(this, _Address_tweakedUncompressed, "f");
387
379
  }
380
+ setMldsaKey(mldsaPublicKey) {
381
+ if (mldsaPublicKey.length === ADDRESS_BYTE_LENGTH) {
382
+ const buf = new Uint8Array(ADDRESS_BYTE_LENGTH);
383
+ buf.set(mldsaPublicKey);
384
+ super.set(buf);
385
+ }
386
+ else {
387
+ const validMLDSALengths = [1312, 1952, 2592];
388
+ if (!validMLDSALengths.includes(mldsaPublicKey.length)) {
389
+ throw new Error(`Invalid ML-DSA public key length: ${mldsaPublicKey.length}. ` +
390
+ `Expected 1312 (ML-DSA-44/LEVEL2), 1952 (ML-DSA-65/LEVEL3), or 2592 (ML-DSA-87/LEVEL5) bytes.`);
391
+ }
392
+ __classPrivateFieldSet(this, _Address_mldsaPublicKey, new Uint8Array(mldsaPublicKey.length), "f");
393
+ __classPrivateFieldGet(this, _Address_mldsaPublicKey, "f").set(mldsaPublicKey);
394
+ const hashedPublicKey = sha256(new Uint8Array(mldsaPublicKey));
395
+ const buf = new Uint8Array(ADDRESS_BYTE_LENGTH);
396
+ buf.set(hashedPublicKey);
397
+ super.set(buf);
398
+ }
399
+ }
400
+ ensureLegacyProcessed() {
401
+ if (__classPrivateFieldGet(this, _Address_legacyProcessed, "f"))
402
+ return;
403
+ __classPrivateFieldSet(this, _Address_legacyProcessed, true, "f");
404
+ const pending = __classPrivateFieldGet(this, _Address_pendingLegacyKey, "f");
405
+ if (!pending)
406
+ return;
407
+ const validLengths = [ADDRESS_BYTE_LENGTH, 33, 65];
408
+ if (!validLengths.includes(pending.length)) {
409
+ throw new Error(`Invalid public key length ${pending.length}`);
410
+ }
411
+ if (pending.length === ADDRESS_BYTE_LENGTH) {
412
+ const buf = Buffer.alloc(ADDRESS_BYTE_LENGTH);
413
+ buf.set(pending);
414
+ __classPrivateFieldSet(this, _Address_tweakedUncompressed, ContractAddress.generateHybridKeyFromHash(buf), "f");
415
+ __classPrivateFieldSet(this, _Address_legacyPublicKey, pending, "f");
416
+ }
417
+ else {
418
+ this.autoFormat(pending);
419
+ }
420
+ }
388
421
  autoFormat(publicKey) {
389
422
  const firstByte = publicKey[0];
390
423
  if (firstByte === 0x03 || firstByte === 0x02) {
@@ -397,8 +430,8 @@ export class Address extends Uint8Array {
397
430
  __classPrivateFieldSet(this, _Address_uncompressed, decompressPublicKey(__classPrivateFieldGet(this, _Address_originalPublicKey, "f")), "f");
398
431
  const tweakedBytes = toXOnly(EcKeyPair.tweakPublicKey(Buffer.from(__classPrivateFieldGet(this, _Address_originalPublicKey, "f"))));
399
432
  __classPrivateFieldSet(this, _Address_tweakedUncompressed, ContractAddress.generateHybridKeyFromHash(tweakedBytes), "f");
400
- this.legacyPublicKey = new Uint8Array(ADDRESS_BYTE_LENGTH);
401
- this.legacyPublicKey.set(tweakedBytes);
433
+ __classPrivateFieldSet(this, _Address_legacyPublicKey, new Uint8Array(ADDRESS_BYTE_LENGTH), "f");
434
+ __classPrivateFieldGet(this, _Address_legacyPublicKey, "f").set(tweakedBytes);
402
435
  }
403
436
  }
404
- _Address_p2tr = new WeakMap(), _Address_p2op = new WeakMap(), _Address_network = new WeakMap(), _Address_originalPublicKey = new WeakMap(), _Address_keyPair = new WeakMap(), _Address_uncompressed = new WeakMap(), _Address_tweakedUncompressed = new WeakMap(), _Address_p2wda = new WeakMap(), _Address_mldsaPublicKey = new WeakMap(), _Address_cachedBigInt = new WeakMap(), _Address_cachedUint64Array = new WeakMap(), _Address_originalMDLSAPublicKey = new WeakMap(), _Address_mldsaLevel = new WeakMap();
437
+ _Address_p2tr = new WeakMap(), _Address_p2op = new WeakMap(), _Address_network = new WeakMap(), _Address_originalPublicKey = new WeakMap(), _Address_keyPair = new WeakMap(), _Address_uncompressed = new WeakMap(), _Address_tweakedUncompressed = new WeakMap(), _Address_p2wda = new WeakMap(), _Address_mldsaPublicKey = new WeakMap(), _Address_cachedBigInt = new WeakMap(), _Address_cachedUint64Array = new WeakMap(), _Address_originalMDLSAPublicKey = new WeakMap(), _Address_mldsaLevel = new WeakMap(), _Address_pendingLegacyKey = new WeakMap(), _Address_legacyProcessed = new WeakMap(), _Address_legacyPublicKey = new WeakMap();
package/build/opnet.d.ts CHANGED
@@ -4,6 +4,7 @@ export * from './bytecode/Compressor.js';
4
4
  export * from './generators/builders/CalldataGenerator.js';
5
5
  export * from './generators/builders/CustomGenerator.js';
6
6
  export * from './generators/builders/DeploymentGenerator.js';
7
+ export * from './generators/builders/HashCommitmentGenerator.js';
7
8
  export * from './generators/builders/LegacyCalldataGenerator.js';
8
9
  export * from './generators/builders/MultiSignGenerator.js';
9
10
  export * from './generators/builders/P2WDAGenerator.js';
@@ -27,10 +28,14 @@ export * from './generators/MLDSAData.js';
27
28
  export * from './metadata/ContractBaseMetadata.js';
28
29
  export * from './network/ChainId.js';
29
30
  export * from './signer/TweakedSigner.js';
31
+ export * from './signer/AddressRotation.js';
30
32
  export * from './transaction/enums/TransactionType.js';
31
33
  export * from './transaction/interfaces/ITransactionParameters.js';
34
+ export * from './transaction/interfaces/IConsolidatedTransactionParameters.js';
32
35
  export * from './transaction/interfaces/Tap.js';
33
36
  export * from './transaction/TransactionFactory.js';
37
+ export * from './transaction/builders/CancelTransaction.js';
38
+ export * from './transaction/builders/ConsolidatedInteractionTransaction.js';
34
39
  export * from './transaction/builders/CustomScriptTransaction.js';
35
40
  export * from './transaction/builders/DeploymentTransaction.js';
36
41
  export * from './transaction/builders/FundingTransaction.js';
@@ -39,7 +44,7 @@ export * from './transaction/builders/InteractionTransactionP2WDA.js';
39
44
  export * from './transaction/builders/MultiSignTransaction.js';
40
45
  export * from './transaction/builders/SharedInteractionTransaction.js';
41
46
  export * from './transaction/builders/TransactionBuilder.js';
42
- export * from './transaction/builders/CancelTransaction.js';
47
+ export * from './transaction/offline/index.js';
43
48
  export * from './epoch/interfaces/IChallengeSolution.js';
44
49
  export * from './epoch/validator/EpochValidator.js';
45
50
  export * from './epoch/ChallengeSolution.js';
package/build/opnet.js CHANGED
@@ -3,6 +3,7 @@ export * from './bytecode/Compressor.js';
3
3
  export * from './generators/builders/CalldataGenerator.js';
4
4
  export * from './generators/builders/CustomGenerator.js';
5
5
  export * from './generators/builders/DeploymentGenerator.js';
6
+ export * from './generators/builders/HashCommitmentGenerator.js';
6
7
  export * from './generators/builders/LegacyCalldataGenerator.js';
7
8
  export * from './generators/builders/MultiSignGenerator.js';
8
9
  export * from './generators/builders/P2WDAGenerator.js';
@@ -26,10 +27,14 @@ export * from './generators/MLDSAData.js';
26
27
  export * from './metadata/ContractBaseMetadata.js';
27
28
  export * from './network/ChainId.js';
28
29
  export * from './signer/TweakedSigner.js';
30
+ export * from './signer/AddressRotation.js';
29
31
  export * from './transaction/enums/TransactionType.js';
30
32
  export * from './transaction/interfaces/ITransactionParameters.js';
33
+ export * from './transaction/interfaces/IConsolidatedTransactionParameters.js';
31
34
  export * from './transaction/interfaces/Tap.js';
32
35
  export * from './transaction/TransactionFactory.js';
36
+ export * from './transaction/builders/CancelTransaction.js';
37
+ export * from './transaction/builders/ConsolidatedInteractionTransaction.js';
33
38
  export * from './transaction/builders/CustomScriptTransaction.js';
34
39
  export * from './transaction/builders/DeploymentTransaction.js';
35
40
  export * from './transaction/builders/FundingTransaction.js';
@@ -38,7 +43,7 @@ export * from './transaction/builders/InteractionTransactionP2WDA.js';
38
43
  export * from './transaction/builders/MultiSignTransaction.js';
39
44
  export * from './transaction/builders/SharedInteractionTransaction.js';
40
45
  export * from './transaction/builders/TransactionBuilder.js';
41
- export * from './transaction/builders/CancelTransaction.js';
46
+ export * from './transaction/offline/index.js';
42
47
  export * from './epoch/interfaces/IChallengeSolution.js';
43
48
  export * from './epoch/validator/EpochValidator.js';
44
49
  export * from './epoch/ChallengeSolution.js';
@@ -0,0 +1,12 @@
1
+ import { Signer } from '@btc-vision/bitcoin';
2
+ import { ECPairInterface } from 'ecpair';
3
+ import { UnisatSigner } from '../transaction/browser/extensions/UnisatSigner.js';
4
+ export type RotationSigner = Signer | ECPairInterface | UnisatSigner;
5
+ export type SignerMap = Map<string, RotationSigner>;
6
+ export interface AddressRotationConfig {
7
+ readonly enabled: boolean;
8
+ readonly signerMap: SignerMap;
9
+ }
10
+ export declare function createSignerMap(pairs: ReadonlyArray<readonly [string, RotationSigner]>): SignerMap;
11
+ export declare function createAddressRotation(signers: SignerMap | ReadonlyArray<readonly [string, RotationSigner]>): AddressRotationConfig;
12
+ export declare function disabledAddressRotation(): AddressRotationConfig;