@btc-vision/transaction 1.7.19 → 1.7.23

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 (139) hide show
  1. package/.babelrc +10 -1
  2. package/LICENSE +190 -21
  3. package/README.md +1 -1
  4. package/browser/_version.d.ts +1 -1
  5. package/browser/bip39.js +204 -0
  6. package/browser/bitcoin-utils.js +3172 -0
  7. package/browser/btc-vision-bip32.js +805 -0
  8. package/browser/btc-vision-bitcoin.js +4179 -0
  9. package/browser/btc-vision-logger.js +273 -0
  10. package/browser/btc-vision-post-quantum.js +542 -0
  11. package/browser/chain/ChainData.d.ts +1 -1
  12. package/browser/crypto/crypto.d.ts +1 -1
  13. package/browser/generators/AddressGenerator.d.ts +1 -1
  14. package/browser/generators/Generator.d.ts +1 -1
  15. package/browser/generators/MLDSAData.d.ts +1 -1
  16. package/browser/generators/builders/CalldataGenerator.d.ts +1 -1
  17. package/browser/generators/builders/CustomGenerator.d.ts +1 -1
  18. package/browser/generators/builders/DeploymentGenerator.d.ts +1 -1
  19. package/browser/generators/builders/HashCommitmentGenerator.d.ts +49 -0
  20. package/browser/generators/builders/LegacyCalldataGenerator.d.ts +1 -1
  21. package/browser/generators/builders/P2WDAGenerator.d.ts +1 -1
  22. package/browser/index.js +10775 -2
  23. package/browser/keypair/Address.d.ts +5 -3
  24. package/browser/keypair/AddressVerificator.d.ts +2 -2
  25. package/browser/keypair/EcKeyPair.d.ts +2 -2
  26. package/browser/keypair/MessageSigner.d.ts +2 -2
  27. package/browser/keypair/Wallet.d.ts +2 -2
  28. package/browser/metadata/ContractBaseMetadata.d.ts +1 -1
  29. package/browser/mnemonic/Mnemonic.d.ts +2 -2
  30. package/browser/noble-curves.js +3316 -0
  31. package/browser/noble-hashes.js +1608 -0
  32. package/browser/opnet.d.ts +15 -2
  33. package/browser/p2wda/P2WDADetector.d.ts +2 -2
  34. package/browser/polyfills.js +4590 -0
  35. package/browser/scure-base.js +410 -0
  36. package/browser/signer/AddressRotation.d.ts +12 -0
  37. package/browser/signer/SignerUtils.d.ts +1 -1
  38. package/browser/signer/TweakedSigner.d.ts +1 -1
  39. package/browser/transaction/TransactionFactory.d.ts +15 -1
  40. package/browser/transaction/browser/BrowserSignerBase.d.ts +1 -1
  41. package/browser/transaction/browser/Web3Provider.d.ts +1 -1
  42. package/browser/transaction/browser/extensions/UnisatSigner.d.ts +1 -1
  43. package/browser/transaction/browser/extensions/XverseSigner.d.ts +1 -1
  44. package/browser/transaction/builders/CancelTransaction.d.ts +1 -1
  45. package/browser/transaction/builders/ConsolidatedInteractionTransaction.d.ts +44 -0
  46. package/browser/transaction/builders/CustomScriptTransaction.d.ts +1 -1
  47. package/browser/transaction/builders/DeploymentTransaction.d.ts +1 -1
  48. package/browser/transaction/builders/FundingTransaction.d.ts +1 -1
  49. package/browser/transaction/builders/InteractionTransaction.d.ts +1 -1
  50. package/browser/transaction/builders/InteractionTransactionP2WDA.d.ts +2 -2
  51. package/browser/transaction/builders/MultiSignTransaction.d.ts +1 -1
  52. package/browser/transaction/builders/SharedInteractionTransaction.d.ts +1 -1
  53. package/browser/transaction/builders/TransactionBuilder.d.ts +1 -1
  54. package/browser/transaction/enums/TransactionType.d.ts +3 -1
  55. package/browser/transaction/interfaces/IConsolidatedTransactionParameters.d.ts +31 -0
  56. package/browser/transaction/interfaces/ITransactionParameters.d.ts +3 -1
  57. package/browser/transaction/interfaces/Tap.d.ts +1 -1
  58. package/browser/transaction/mineable/TimelockGenerator.d.ts +1 -1
  59. package/browser/transaction/offline/OfflineTransactionManager.d.ts +69 -0
  60. package/browser/transaction/offline/TransactionReconstructor.d.ts +28 -0
  61. package/browser/transaction/offline/TransactionSerializer.d.ts +50 -0
  62. package/browser/transaction/offline/TransactionStateCapture.d.ts +52 -0
  63. package/browser/transaction/offline/interfaces/ISerializableState.d.ts +62 -0
  64. package/browser/transaction/offline/interfaces/ITypeSpecificData.d.ts +62 -0
  65. package/browser/transaction/processor/PsbtTransaction.d.ts +1 -1
  66. package/browser/transaction/shared/P2TR_MS.d.ts +1 -1
  67. package/browser/transaction/shared/TweakedTransaction.d.ts +15 -4
  68. package/browser/utxo/OPNetLimitedProvider.d.ts +1 -1
  69. package/browser/utxo/interfaces/IUTXO.d.ts +2 -0
  70. package/browser/valibot.js +4948 -0
  71. package/browser/vendors.js +12913 -0
  72. package/browser/verification/TapscriptVerificator.d.ts +1 -1
  73. package/build/_version.d.ts +1 -1
  74. package/build/_version.js +1 -1
  75. package/build/generators/builders/HashCommitmentGenerator.d.ts +49 -0
  76. package/build/generators/builders/HashCommitmentGenerator.js +229 -0
  77. package/build/keypair/Address.d.ts +3 -1
  78. package/build/keypair/Address.js +87 -54
  79. package/build/opnet.d.ts +14 -1
  80. package/build/opnet.js +11 -1
  81. package/build/signer/AddressRotation.d.ts +12 -0
  82. package/build/signer/AddressRotation.js +16 -0
  83. package/build/transaction/TransactionFactory.d.ts +14 -0
  84. package/build/transaction/TransactionFactory.js +36 -0
  85. package/build/transaction/builders/ConsolidatedInteractionTransaction.d.ts +44 -0
  86. package/build/transaction/builders/ConsolidatedInteractionTransaction.js +259 -0
  87. package/build/transaction/builders/TransactionBuilder.js +2 -0
  88. package/build/transaction/enums/TransactionType.d.ts +3 -1
  89. package/build/transaction/enums/TransactionType.js +2 -0
  90. package/build/transaction/interfaces/IConsolidatedTransactionParameters.d.ts +31 -0
  91. package/build/transaction/interfaces/IConsolidatedTransactionParameters.js +1 -0
  92. package/build/transaction/interfaces/ITransactionParameters.d.ts +2 -0
  93. package/build/transaction/offline/OfflineTransactionManager.d.ts +69 -0
  94. package/build/transaction/offline/OfflineTransactionManager.js +255 -0
  95. package/build/transaction/offline/TransactionReconstructor.d.ts +28 -0
  96. package/build/transaction/offline/TransactionReconstructor.js +243 -0
  97. package/build/transaction/offline/TransactionSerializer.d.ts +50 -0
  98. package/build/transaction/offline/TransactionSerializer.js +700 -0
  99. package/build/transaction/offline/TransactionStateCapture.d.ts +52 -0
  100. package/build/transaction/offline/TransactionStateCapture.js +275 -0
  101. package/build/transaction/offline/interfaces/ISerializableState.d.ts +62 -0
  102. package/build/transaction/offline/interfaces/ISerializableState.js +2 -0
  103. package/build/transaction/offline/interfaces/ITypeSpecificData.d.ts +62 -0
  104. package/build/transaction/offline/interfaces/ITypeSpecificData.js +19 -0
  105. package/build/transaction/shared/TweakedTransaction.d.ts +12 -1
  106. package/build/transaction/shared/TweakedTransaction.js +75 -8
  107. package/build/utxo/interfaces/IUTXO.d.ts +2 -0
  108. package/documentation/README.md +5 -0
  109. package/documentation/offline-transaction-signing.md +650 -0
  110. package/documentation/transaction-building.md +603 -0
  111. package/package.json +62 -4
  112. package/src/_version.ts +1 -1
  113. package/src/generators/builders/HashCommitmentGenerator.ts +495 -0
  114. package/src/keypair/Address.ts +123 -70
  115. package/src/opnet.ts +16 -1
  116. package/src/signer/AddressRotation.ts +72 -0
  117. package/src/transaction/TransactionFactory.ts +87 -0
  118. package/src/transaction/builders/CancelTransaction.ts +4 -2
  119. package/src/transaction/builders/ConsolidatedInteractionTransaction.ts +561 -0
  120. package/src/transaction/builders/CustomScriptTransaction.ts +4 -2
  121. package/src/transaction/builders/MultiSignTransaction.ts +4 -2
  122. package/src/transaction/builders/TransactionBuilder.ts +8 -2
  123. package/src/transaction/enums/TransactionType.ts +2 -0
  124. package/src/transaction/interfaces/IConsolidatedTransactionParameters.ts +78 -0
  125. package/src/transaction/interfaces/ITransactionParameters.ts +8 -0
  126. package/src/transaction/offline/OfflineTransactionManager.ts +630 -0
  127. package/src/transaction/offline/TransactionReconstructor.ts +402 -0
  128. package/src/transaction/offline/TransactionSerializer.ts +920 -0
  129. package/src/transaction/offline/TransactionStateCapture.ts +469 -0
  130. package/src/transaction/offline/interfaces/ISerializableState.ts +141 -0
  131. package/src/transaction/offline/interfaces/ITypeSpecificData.ts +172 -0
  132. package/src/transaction/shared/TweakedTransaction.ts +156 -9
  133. package/src/utxo/interfaces/IUTXO.ts +8 -0
  134. package/test/address-rotation.test.ts +553 -0
  135. package/test/offline-transaction.test.ts +2065 -0
  136. package/vite.config.browser.ts +92 -0
  137. package/webpack.config.js +143 -2
  138. package/browser/crypto/crypto-browser.d.ts +0 -11
  139. package/browser/index.js.LICENSE.txt +0 -29
@@ -0,0 +1,52 @@
1
+ import { TransactionType } from '../enums/TransactionType.js';
2
+ import { ISerializableTransactionState, PrecomputedData } from './interfaces/ISerializableState.js';
3
+ import { IDeploymentParameters, IFundingTransactionParameters, IInteractionParameters, ITransactionParameters } from '../interfaces/ITransactionParameters.js';
4
+ export interface CaptureParams {
5
+ params: ITransactionParameters;
6
+ type: TransactionType;
7
+ precomputed?: Partial<PrecomputedData>;
8
+ }
9
+ export declare class TransactionStateCapture {
10
+ static fromFunding(params: IFundingTransactionParameters, precomputed?: Partial<PrecomputedData>): ISerializableTransactionState;
11
+ static fromDeployment(params: IDeploymentParameters, precomputed: Partial<PrecomputedData> & {
12
+ compiledTargetScript: string;
13
+ randomBytes: string;
14
+ }): ISerializableTransactionState;
15
+ static fromInteraction(params: IInteractionParameters, precomputed: Partial<PrecomputedData> & {
16
+ compiledTargetScript: string;
17
+ randomBytes: string;
18
+ }): ISerializableTransactionState;
19
+ static fromMultiSig(params: ITransactionParameters & {
20
+ pubkeys: Buffer[];
21
+ minimumSignatures: number;
22
+ receiver: string;
23
+ requestedAmount: bigint;
24
+ refundVault: string;
25
+ originalInputCount?: number;
26
+ existingPsbtBase64?: string;
27
+ }, precomputed?: Partial<PrecomputedData>): ISerializableTransactionState;
28
+ static fromCustomScript(params: ITransactionParameters & {
29
+ scriptElements: (Buffer | number)[];
30
+ witnesses: Buffer[];
31
+ annex?: Buffer;
32
+ }, precomputed?: Partial<PrecomputedData>): ISerializableTransactionState;
33
+ static fromCancel(params: ITransactionParameters & {
34
+ compiledTargetScript: Buffer | string;
35
+ }, precomputed?: Partial<PrecomputedData>): ISerializableTransactionState;
36
+ private static captureState;
37
+ private static createHeader;
38
+ private static extractBaseParams;
39
+ private static extractSignerMappings;
40
+ private static extractTypeSpecificData;
41
+ private static extractFundingData;
42
+ private static extractDeploymentData;
43
+ private static extractInteractionData;
44
+ private static extractMultiSigData;
45
+ private static extractCustomScriptData;
46
+ private static extractCancelData;
47
+ private static buildPrecomputedData;
48
+ private static serializeUTXOs;
49
+ private static serializeOutputs;
50
+ private static networkToName;
51
+ private static networkToChainId;
52
+ }
@@ -0,0 +1,275 @@
1
+ import { ChainId } from '../../network/ChainId.js';
2
+ import { currentConsensus } from '../../consensus/ConsensusConfig.js';
3
+ import { TransactionType } from '../enums/TransactionType.js';
4
+ import { SERIALIZATION_FORMAT_VERSION, } from './interfaces/ISerializableState.js';
5
+ export class TransactionStateCapture {
6
+ static fromFunding(params, precomputed) {
7
+ return this.captureState({
8
+ params,
9
+ type: TransactionType.FUNDING,
10
+ precomputed,
11
+ });
12
+ }
13
+ static fromDeployment(params, precomputed) {
14
+ return this.captureState({
15
+ params: params,
16
+ type: TransactionType.DEPLOYMENT,
17
+ precomputed,
18
+ });
19
+ }
20
+ static fromInteraction(params, precomputed) {
21
+ return this.captureState({
22
+ params,
23
+ type: TransactionType.INTERACTION,
24
+ precomputed,
25
+ });
26
+ }
27
+ static fromMultiSig(params, precomputed) {
28
+ return this.captureState({
29
+ params,
30
+ type: TransactionType.MULTI_SIG,
31
+ precomputed,
32
+ });
33
+ }
34
+ static fromCustomScript(params, precomputed) {
35
+ return this.captureState({
36
+ params,
37
+ type: TransactionType.CUSTOM_CODE,
38
+ precomputed,
39
+ });
40
+ }
41
+ static fromCancel(params, precomputed) {
42
+ return this.captureState({
43
+ params,
44
+ type: TransactionType.CANCEL,
45
+ precomputed,
46
+ });
47
+ }
48
+ static captureState(capture) {
49
+ const { params, type, precomputed } = capture;
50
+ return {
51
+ header: this.createHeader(type, params.network, params.chainId),
52
+ baseParams: this.extractBaseParams(params),
53
+ utxos: this.serializeUTXOs(params.utxos),
54
+ optionalInputs: this.serializeUTXOs(params.optionalInputs || []),
55
+ optionalOutputs: this.serializeOutputs(params.optionalOutputs || []),
56
+ addressRotationEnabled: params.addressRotation?.enabled ?? false,
57
+ signerMappings: this.extractSignerMappings(params),
58
+ typeSpecificData: this.extractTypeSpecificData(type, params),
59
+ precomputedData: this.buildPrecomputedData(precomputed),
60
+ };
61
+ }
62
+ static createHeader(type, network, chainId) {
63
+ return {
64
+ formatVersion: SERIALIZATION_FORMAT_VERSION,
65
+ consensusVersion: currentConsensus,
66
+ transactionType: type,
67
+ chainId: chainId ?? this.networkToChainId(network),
68
+ timestamp: Date.now(),
69
+ };
70
+ }
71
+ static extractBaseParams(params) {
72
+ const note = params.note
73
+ ? Buffer.isBuffer(params.note)
74
+ ? params.note.toString('hex')
75
+ : Buffer.from(params.note).toString('hex')
76
+ : undefined;
77
+ const priorityFee = params.priorityFee ?? 0n;
78
+ const gasSatFee = params.gasSatFee ?? 0n;
79
+ return {
80
+ from: params.from || '',
81
+ to: params.to,
82
+ feeRate: params.feeRate,
83
+ priorityFee: priorityFee.toString(),
84
+ gasSatFee: gasSatFee.toString(),
85
+ networkName: this.networkToName(params.network),
86
+ txVersion: params.txVersion ?? 2,
87
+ note,
88
+ anchor: params.anchor ?? false,
89
+ debugFees: params.debugFees,
90
+ };
91
+ }
92
+ static extractSignerMappings(params) {
93
+ if (!params.addressRotation?.enabled) {
94
+ return [];
95
+ }
96
+ const mappings = [];
97
+ const addressToIndices = new Map();
98
+ params.utxos.forEach((utxo, index) => {
99
+ const address = utxo.scriptPubKey?.address;
100
+ if (address) {
101
+ const existing = addressToIndices.get(address);
102
+ if (existing) {
103
+ existing.push(index);
104
+ }
105
+ else {
106
+ addressToIndices.set(address, [index]);
107
+ }
108
+ }
109
+ });
110
+ const utxoCount = params.utxos.length;
111
+ (params.optionalInputs || []).forEach((utxo, index) => {
112
+ const address = utxo.scriptPubKey?.address;
113
+ if (address) {
114
+ const existing = addressToIndices.get(address);
115
+ if (existing) {
116
+ existing.push(utxoCount + index);
117
+ }
118
+ else {
119
+ addressToIndices.set(address, [utxoCount + index]);
120
+ }
121
+ }
122
+ });
123
+ addressToIndices.forEach((indices, address) => {
124
+ mappings.push({ address, inputIndices: indices });
125
+ });
126
+ return mappings;
127
+ }
128
+ static extractTypeSpecificData(type, params) {
129
+ switch (type) {
130
+ case TransactionType.FUNDING:
131
+ return this.extractFundingData(params);
132
+ case TransactionType.DEPLOYMENT:
133
+ return this.extractDeploymentData(params);
134
+ case TransactionType.INTERACTION:
135
+ return this.extractInteractionData(params);
136
+ case TransactionType.MULTI_SIG:
137
+ return this.extractMultiSigData(params);
138
+ case TransactionType.CUSTOM_CODE:
139
+ return this.extractCustomScriptData(params);
140
+ case TransactionType.CANCEL:
141
+ return this.extractCancelData(params);
142
+ default:
143
+ throw new Error(`Unsupported transaction type: ${type}`);
144
+ }
145
+ }
146
+ static extractFundingData(params) {
147
+ return {
148
+ type: TransactionType.FUNDING,
149
+ amount: params.amount.toString(),
150
+ splitInputsInto: params.splitInputsInto ?? 1,
151
+ };
152
+ }
153
+ static extractDeploymentData(params) {
154
+ return {
155
+ type: TransactionType.DEPLOYMENT,
156
+ bytecode: params.bytecode.toString('hex'),
157
+ calldata: params.calldata?.toString('hex'),
158
+ challenge: params.challenge.toRaw(),
159
+ revealMLDSAPublicKey: params.revealMLDSAPublicKey,
160
+ linkMLDSAPublicKeyToAddress: params.linkMLDSAPublicKeyToAddress,
161
+ };
162
+ }
163
+ static extractInteractionData(params) {
164
+ return {
165
+ type: TransactionType.INTERACTION,
166
+ calldata: params.calldata.toString('hex'),
167
+ contract: params.contract,
168
+ challenge: params.challenge.toRaw(),
169
+ loadedStorage: params.loadedStorage,
170
+ isCancellation: params.isCancellation,
171
+ disableAutoRefund: params.disableAutoRefund,
172
+ revealMLDSAPublicKey: params.revealMLDSAPublicKey,
173
+ linkMLDSAPublicKeyToAddress: params.linkMLDSAPublicKeyToAddress,
174
+ };
175
+ }
176
+ static extractMultiSigData(params) {
177
+ return {
178
+ type: TransactionType.MULTI_SIG,
179
+ pubkeys: (params.pubkeys || []).map((pk) => pk.toString('hex')),
180
+ minimumSignatures: params.minimumSignatures || 0,
181
+ receiver: params.receiver || '',
182
+ requestedAmount: (params.requestedAmount || 0n).toString(),
183
+ refundVault: params.refundVault || '',
184
+ originalInputCount: params.originalInputCount || params.utxos.length,
185
+ existingPsbtBase64: params.existingPsbtBase64,
186
+ };
187
+ }
188
+ static extractCustomScriptData(params) {
189
+ const scriptElements = (params.scriptElements || []).map((element) => {
190
+ if (Buffer.isBuffer(element)) {
191
+ return {
192
+ elementType: 'buffer',
193
+ value: element.toString('hex'),
194
+ };
195
+ }
196
+ else {
197
+ return {
198
+ elementType: 'opcode',
199
+ value: element,
200
+ };
201
+ }
202
+ });
203
+ return {
204
+ type: TransactionType.CUSTOM_CODE,
205
+ scriptElements,
206
+ witnesses: (params.witnesses || []).map((w) => w.toString('hex')),
207
+ annex: params.annex?.toString('hex'),
208
+ };
209
+ }
210
+ static extractCancelData(params) {
211
+ const script = params.compiledTargetScript;
212
+ const scriptHex = script ? (Buffer.isBuffer(script) ? script.toString('hex') : script) : '';
213
+ return {
214
+ type: TransactionType.CANCEL,
215
+ compiledTargetScript: scriptHex,
216
+ };
217
+ }
218
+ static buildPrecomputedData(precomputed) {
219
+ return {
220
+ compiledTargetScript: precomputed?.compiledTargetScript,
221
+ randomBytes: precomputed?.randomBytes,
222
+ estimatedFees: precomputed?.estimatedFees,
223
+ contractSeed: precomputed?.contractSeed,
224
+ contractAddress: precomputed?.contractAddress,
225
+ };
226
+ }
227
+ static serializeUTXOs(utxos) {
228
+ return utxos.map((utxo) => ({
229
+ transactionId: utxo.transactionId,
230
+ outputIndex: utxo.outputIndex,
231
+ value: utxo.value.toString(),
232
+ scriptPubKeyHex: utxo.scriptPubKey.hex,
233
+ scriptPubKeyAddress: utxo.scriptPubKey.address,
234
+ redeemScript: utxo.redeemScript
235
+ ? Buffer.isBuffer(utxo.redeemScript)
236
+ ? utxo.redeemScript.toString('hex')
237
+ : utxo.redeemScript
238
+ : undefined,
239
+ witnessScript: utxo.witnessScript
240
+ ? Buffer.isBuffer(utxo.witnessScript)
241
+ ? utxo.witnessScript.toString('hex')
242
+ : utxo.witnessScript
243
+ : undefined,
244
+ nonWitnessUtxo: utxo.nonWitnessUtxo
245
+ ? Buffer.isBuffer(utxo.nonWitnessUtxo)
246
+ ? utxo.nonWitnessUtxo.toString('hex')
247
+ : utxo.nonWitnessUtxo
248
+ : undefined,
249
+ }));
250
+ }
251
+ static serializeOutputs(outputs) {
252
+ return outputs.map((output) => {
253
+ const address = 'address' in output ? output.address : undefined;
254
+ const script = 'script' in output ? output.script : undefined;
255
+ return {
256
+ value: output.value,
257
+ address,
258
+ script: script ? script.toString('hex') : undefined,
259
+ tapInternalKey: output.tapInternalKey
260
+ ? output.tapInternalKey.toString('hex')
261
+ : undefined,
262
+ };
263
+ });
264
+ }
265
+ static networkToName(network) {
266
+ if (network.bech32 === 'bc')
267
+ return 'mainnet';
268
+ if (network.bech32 === 'tb')
269
+ return 'testnet';
270
+ return 'regtest';
271
+ }
272
+ static networkToChainId(network) {
273
+ return ChainId.Bitcoin;
274
+ }
275
+ }
@@ -0,0 +1,62 @@
1
+ import { TransactionType } from '../../enums/TransactionType.js';
2
+ import { ChainId } from '../../../network/ChainId.js';
3
+ import { TypeSpecificData } from './ITypeSpecificData.js';
4
+ export declare const SERIALIZATION_FORMAT_VERSION = 1;
5
+ export declare const SERIALIZATION_MAGIC_BYTE = 66;
6
+ export interface SerializationHeader {
7
+ readonly formatVersion: number;
8
+ readonly consensusVersion: number;
9
+ readonly transactionType: TransactionType;
10
+ readonly chainId: ChainId;
11
+ readonly timestamp: number;
12
+ }
13
+ export interface SerializedUTXO {
14
+ readonly transactionId: string;
15
+ readonly outputIndex: number;
16
+ readonly value: string;
17
+ readonly scriptPubKeyHex: string;
18
+ readonly scriptPubKeyAddress?: string;
19
+ readonly redeemScript?: string;
20
+ readonly witnessScript?: string;
21
+ readonly nonWitnessUtxo?: string;
22
+ }
23
+ export interface SerializedOutput {
24
+ readonly value: number;
25
+ readonly address?: string;
26
+ readonly script?: string;
27
+ readonly tapInternalKey?: string;
28
+ }
29
+ export interface SerializedSignerMapping {
30
+ readonly address: string;
31
+ readonly inputIndices: number[];
32
+ }
33
+ export interface SerializedBaseParams {
34
+ readonly from: string;
35
+ readonly to?: string;
36
+ readonly feeRate: number;
37
+ readonly priorityFee: string;
38
+ readonly gasSatFee: string;
39
+ readonly networkName: 'mainnet' | 'testnet' | 'regtest';
40
+ readonly txVersion: number;
41
+ readonly note?: string;
42
+ readonly anchor: boolean;
43
+ readonly debugFees?: boolean;
44
+ }
45
+ export interface PrecomputedData {
46
+ readonly compiledTargetScript?: string;
47
+ readonly randomBytes?: string;
48
+ readonly estimatedFees?: string;
49
+ readonly contractSeed?: string;
50
+ readonly contractAddress?: string;
51
+ }
52
+ export interface ISerializableTransactionState {
53
+ readonly header: SerializationHeader;
54
+ readonly baseParams: SerializedBaseParams;
55
+ readonly utxos: SerializedUTXO[];
56
+ readonly optionalInputs: SerializedUTXO[];
57
+ readonly optionalOutputs: SerializedOutput[];
58
+ readonly addressRotationEnabled: boolean;
59
+ readonly signerMappings: SerializedSignerMapping[];
60
+ readonly typeSpecificData: TypeSpecificData;
61
+ readonly precomputedData: PrecomputedData;
62
+ }
@@ -0,0 +1,2 @@
1
+ export const SERIALIZATION_FORMAT_VERSION = 1;
2
+ export const SERIALIZATION_MAGIC_BYTE = 0x42;
@@ -0,0 +1,62 @@
1
+ import { TransactionType } from '../../enums/TransactionType.js';
2
+ import { RawChallenge } from '../../../epoch/interfaces/IChallengeSolution.js';
3
+ export type TypeSpecificData = FundingSpecificData | DeploymentSpecificData | InteractionSpecificData | MultiSigSpecificData | CustomScriptSpecificData | CancelSpecificData;
4
+ export interface FundingSpecificData {
5
+ readonly type: TransactionType.FUNDING;
6
+ readonly amount: string;
7
+ readonly splitInputsInto: number;
8
+ }
9
+ export interface DeploymentSpecificData {
10
+ readonly type: TransactionType.DEPLOYMENT;
11
+ readonly bytecode: string;
12
+ readonly calldata?: string;
13
+ readonly challenge: RawChallenge;
14
+ readonly revealMLDSAPublicKey?: boolean;
15
+ readonly linkMLDSAPublicKeyToAddress?: boolean;
16
+ readonly hashedPublicKey?: string;
17
+ }
18
+ export interface InteractionSpecificData {
19
+ readonly type: TransactionType.INTERACTION;
20
+ readonly calldata: string;
21
+ readonly contract?: string;
22
+ readonly challenge: RawChallenge;
23
+ readonly loadedStorage?: SerializedLoadedStorage;
24
+ readonly isCancellation?: boolean;
25
+ readonly disableAutoRefund?: boolean;
26
+ readonly revealMLDSAPublicKey?: boolean;
27
+ readonly linkMLDSAPublicKeyToAddress?: boolean;
28
+ readonly hashedPublicKey?: string;
29
+ }
30
+ export interface SerializedLoadedStorage {
31
+ [key: string]: string[];
32
+ }
33
+ export interface MultiSigSpecificData {
34
+ readonly type: TransactionType.MULTI_SIG;
35
+ readonly pubkeys: string[];
36
+ readonly minimumSignatures: number;
37
+ readonly receiver: string;
38
+ readonly requestedAmount: string;
39
+ readonly refundVault: string;
40
+ readonly originalInputCount: number;
41
+ readonly existingPsbtBase64?: string;
42
+ }
43
+ export interface SerializedScriptElement {
44
+ readonly elementType: 'buffer' | 'opcode';
45
+ readonly value: string | number;
46
+ }
47
+ export interface CustomScriptSpecificData {
48
+ readonly type: TransactionType.CUSTOM_CODE;
49
+ readonly scriptElements: SerializedScriptElement[];
50
+ readonly witnesses: string[];
51
+ readonly annex?: string;
52
+ }
53
+ export interface CancelSpecificData {
54
+ readonly type: TransactionType.CANCEL;
55
+ readonly compiledTargetScript: string;
56
+ }
57
+ export declare function isFundingSpecificData(data: TypeSpecificData): data is FundingSpecificData;
58
+ export declare function isDeploymentSpecificData(data: TypeSpecificData): data is DeploymentSpecificData;
59
+ export declare function isInteractionSpecificData(data: TypeSpecificData): data is InteractionSpecificData;
60
+ export declare function isMultiSigSpecificData(data: TypeSpecificData): data is MultiSigSpecificData;
61
+ export declare function isCustomScriptSpecificData(data: TypeSpecificData): data is CustomScriptSpecificData;
62
+ export declare function isCancelSpecificData(data: TypeSpecificData): data is CancelSpecificData;
@@ -0,0 +1,19 @@
1
+ import { TransactionType } from '../../enums/TransactionType.js';
2
+ export function isFundingSpecificData(data) {
3
+ return data.type === TransactionType.FUNDING;
4
+ }
5
+ export function isDeploymentSpecificData(data) {
6
+ return data.type === TransactionType.DEPLOYMENT;
7
+ }
8
+ export function isInteractionSpecificData(data) {
9
+ return data.type === TransactionType.INTERACTION;
10
+ }
11
+ export function isMultiSigSpecificData(data) {
12
+ return data.type === TransactionType.MULTI_SIG;
13
+ }
14
+ export function isCustomScriptSpecificData(data) {
15
+ return data.type === TransactionType.CUSTOM_CODE;
16
+ }
17
+ export function isCancelSpecificData(data) {
18
+ return data.type === TransactionType.CANCEL;
19
+ }
@@ -7,6 +7,7 @@ import { ChainId } from '../../network/ChainId.js';
7
7
  import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
8
8
  import { Buffer } from 'buffer';
9
9
  import { QuantumBIP32Interface } from '@btc-vision/bip32';
10
+ import { AddressRotationConfig, RotationSigner, SignerMap } from '../../signer/AddressRotation.js';
10
11
  export type SupportedTransactionVersion = 1 | 2 | 3;
11
12
  export interface ITweakedTransactionData {
12
13
  readonly mldsaSigner: QuantumBIP32Interface | null;
@@ -17,6 +18,7 @@ export interface ITweakedTransactionData {
17
18
  readonly noSignatures?: boolean;
18
19
  readonly unlockScript?: Buffer[];
19
20
  readonly txVersion?: SupportedTransactionVersion;
21
+ readonly addressRotation?: AddressRotationConfig;
20
22
  }
21
23
  export declare enum TransactionSequence {
22
24
  REPLACE_BY_FEE = 4294967293,
@@ -51,6 +53,10 @@ export declare abstract class TweakedTransaction extends Logger {
51
53
  protected txVersion: SupportedTransactionVersion;
52
54
  protected readonly _mldsaSigner: QuantumBIP32Interface | null;
53
55
  protected readonly _hashedPublicKey: Buffer | null;
56
+ protected readonly addressRotationEnabled: boolean;
57
+ protected readonly signerMap: SignerMap;
58
+ protected readonly inputSignerMap: Map<number, RotationSigner>;
59
+ protected readonly tweakedSignerCache: Map<number, ECPairInterface | undefined>;
54
60
  protected constructor(data: ITweakedTransactionData);
55
61
  protected get mldsaSigner(): QuantumBIP32Interface;
56
62
  protected get hashedPublicKey(): Buffer;
@@ -58,6 +64,7 @@ export declare abstract class TweakedTransaction extends Logger {
58
64
  static preEstimateTaprootTransactionFees(feeRate: bigint, numInputs: bigint, numOutputs: bigint, numWitnessElements: bigint, witnessElementSize: bigint, emptyWitness: bigint, taprootControlWitnessSize?: bigint, taprootScriptSize?: bigint): bigint;
59
65
  protected static signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer | ECPairInterface, sighashTypes: number[]): void;
60
66
  protected static calculateSignHash(sighashTypes: number[]): number;
67
+ isAddressRotationEnabled(): boolean;
61
68
  ignoreSignatureError(): void;
62
69
  getScriptAddress(): string;
63
70
  getTransaction(): Transaction;
@@ -65,6 +72,10 @@ export declare abstract class TweakedTransaction extends Logger {
65
72
  disableRBF(): void;
66
73
  getTweakerHash(): Buffer | undefined;
67
74
  preEstimateTransactionFees(feeRate: bigint, numInputs: bigint, numOutputs: bigint, numSignatures: bigint, numPubkeys: bigint): bigint;
75
+ protected getSignerForInput(inputIndex: number): RotationSigner;
76
+ protected registerInputSigner(inputIndex: number, utxo: UTXO): void;
77
+ protected internalPubKeyToXOnlyForInput(inputIndex: number): Buffer;
78
+ protected getTweakedSignerForInput(inputIndex: number, useTweakedHash?: boolean): ECPairInterface | undefined;
68
79
  protected generateTapData(): P2TRPayment;
69
80
  protected generateScriptAddress(): P2TRPayment;
70
81
  protected getSignerKey(): Signer | ECPairInterface;
@@ -81,7 +92,7 @@ export declare abstract class TweakedTransaction extends Logger {
81
92
  redeemScript: Buffer;
82
93
  outputScript: Buffer;
83
94
  } | undefined;
84
- protected generateP2SHP2PKHRedeemScript(inputAddr: string): {
95
+ protected generateP2SHP2PKHRedeemScript(inputAddr: string, inputIndex?: number): {
85
96
  redeemScript: Buffer;
86
97
  outputScript: Buffer;
87
98
  } | undefined;
@@ -36,6 +36,10 @@ export class TweakedTransaction extends Logger {
36
36
  this.txVersion = 2;
37
37
  this._mldsaSigner = null;
38
38
  this._hashedPublicKey = null;
39
+ this.addressRotationEnabled = false;
40
+ this.signerMap = new Map();
41
+ this.inputSignerMap = new Map();
42
+ this.tweakedSignerCache = new Map();
39
43
  this.customFinalizerP2SH = (inputIndex, input, scriptA, isSegwit, isP2SH, isP2WSH) => {
40
44
  const inputDecoded = this.inputs[inputIndex];
41
45
  if (isP2SH && input.partialSig && inputDecoded && inputDecoded.redeemScript) {
@@ -84,6 +88,10 @@ export class TweakedTransaction extends Logger {
84
88
  this._mldsaSigner = data.mldsaSigner;
85
89
  this._hashedPublicKey = MessageSigner.sha256(this._mldsaSigner.publicKey);
86
90
  }
91
+ if (data.addressRotation?.enabled) {
92
+ this.addressRotationEnabled = true;
93
+ this.signerMap = data.addressRotation.signerMap;
94
+ }
87
95
  }
88
96
  get mldsaSigner() {
89
97
  if (!this._mldsaSigner) {
@@ -153,6 +161,9 @@ export class TweakedTransaction extends Logger {
153
161
  }
154
162
  return signHash || 0;
155
163
  }
164
+ isAddressRotationEnabled() {
165
+ return this.addressRotationEnabled;
166
+ }
156
167
  ignoreSignatureError() {
157
168
  this.ignoreSignatureErrors = true;
158
169
  }
@@ -198,6 +209,53 @@ export class TweakedTransaction extends Logger {
198
209
  const vSize = weight / 4n;
199
210
  return vSize * feeRate;
200
211
  }
212
+ getSignerForInput(inputIndex) {
213
+ if (this.addressRotationEnabled) {
214
+ const inputSigner = this.inputSignerMap.get(inputIndex);
215
+ if (inputSigner) {
216
+ return inputSigner;
217
+ }
218
+ }
219
+ return this.signer;
220
+ }
221
+ registerInputSigner(inputIndex, utxo) {
222
+ if (!this.addressRotationEnabled) {
223
+ return;
224
+ }
225
+ if (utxo.signer) {
226
+ this.inputSignerMap.set(inputIndex, utxo.signer);
227
+ return;
228
+ }
229
+ const address = utxo.scriptPubKey?.address;
230
+ if (address && this.signerMap.has(address)) {
231
+ const signer = this.signerMap.get(address);
232
+ if (signer) {
233
+ this.inputSignerMap.set(inputIndex, signer);
234
+ return;
235
+ }
236
+ }
237
+ }
238
+ internalPubKeyToXOnlyForInput(inputIndex) {
239
+ const signer = this.getSignerForInput(inputIndex);
240
+ return toXOnly(Buffer.from(signer.publicKey));
241
+ }
242
+ getTweakedSignerForInput(inputIndex, useTweakedHash = false) {
243
+ if (!this.addressRotationEnabled) {
244
+ if (useTweakedHash) {
245
+ this.tweakSigner();
246
+ return this.tweakedSigner;
247
+ }
248
+ return this.getTweakedSigner(useTweakedHash);
249
+ }
250
+ const cacheKey = inputIndex * 2 + (useTweakedHash ? 1 : 0);
251
+ if (this.tweakedSignerCache.has(cacheKey)) {
252
+ return this.tweakedSignerCache.get(cacheKey);
253
+ }
254
+ const signer = this.getSignerForInput(inputIndex);
255
+ const tweaked = this.getTweakedSigner(useTweakedHash, signer);
256
+ this.tweakedSignerCache.set(cacheKey, tweaked);
257
+ return tweaked;
258
+ }
201
259
  generateTapData() {
202
260
  return {
203
261
  internalPubkey: this.internalPubKeyToXOnly(),
@@ -289,7 +347,8 @@ export class TweakedTransaction extends Logger {
289
347
  const index = offset + j;
290
348
  const input = batch[j];
291
349
  try {
292
- promises.push(this.signInput(transaction, input, index, this.signer));
350
+ const inputSigner = this.getSignerForInput(index);
351
+ promises.push(this.signInput(transaction, input, index, inputSigner));
293
352
  }
294
353
  catch (e) {
295
354
  this.log(`Failed to sign input ${index}: ${e.stack}`);
@@ -370,10 +429,13 @@ export class TweakedTransaction extends Logger {
370
429
  }
371
430
  return;
372
431
  }
373
- generateP2SHP2PKHRedeemScript(inputAddr) {
374
- const pubkey = Buffer.isBuffer(this.signer.publicKey)
375
- ? this.signer.publicKey
376
- : Buffer.from(this.signer.publicKey, 'hex');
432
+ generateP2SHP2PKHRedeemScript(inputAddr, inputIndex) {
433
+ const signer = this.addressRotationEnabled && inputIndex !== undefined
434
+ ? this.getSignerForInput(inputIndex)
435
+ : this.signer;
436
+ const pubkey = Buffer.isBuffer(signer.publicKey)
437
+ ? signer.publicKey
438
+ : Buffer.from(signer.publicKey, 'hex');
377
439
  const w = payments.p2wpkh({
378
440
  pubkey: pubkey,
379
441
  network: this.network,
@@ -432,7 +494,7 @@ export class TweakedTransaction extends Logger {
432
494
  if (!utxo.scriptPubKey.address) {
433
495
  throw new Error('Missing redeemScript and no address to regenerate it for P2SH UTXO');
434
496
  }
435
- const legacyScripts = this.generateP2SHP2PKHRedeemScript(utxo.scriptPubKey.address);
497
+ const legacyScripts = this.generateP2SHP2PKHRedeemScript(utxo.scriptPubKey.address, i);
436
498
  if (!legacyScripts) {
437
499
  throw new Error('Missing redeemScript for P2SH UTXO and unable to regenerate');
438
500
  }
@@ -469,8 +531,13 @@ export class TweakedTransaction extends Logger {
469
531
  if (inputSign)
470
532
  input.sighashType = inputSign;
471
533
  }
472
- this.tweakSigner();
473
- input.tapInternalKey = this.internalPubKeyToXOnly();
534
+ if (this.addressRotationEnabled) {
535
+ input.tapInternalKey = this.internalPubKeyToXOnlyForInput(i);
536
+ }
537
+ else {
538
+ this.tweakSigner();
539
+ input.tapInternalKey = this.internalPubKeyToXOnly();
540
+ }
474
541
  }
475
542
  else if (isP2A(scriptPub)) {
476
543
  this.anchorInputIndices.add(i);
@@ -1,4 +1,5 @@
1
1
  import { ScriptPubKey } from '@btc-vision/bitcoin-rpc';
2
+ import { RotationSigner } from '../../signer/AddressRotation.js';
2
3
  export interface UTXO {
3
4
  readonly transactionId: string;
4
5
  readonly outputIndex: number;
@@ -7,6 +8,7 @@ export interface UTXO {
7
8
  redeemScript?: string | Buffer;
8
9
  witnessScript?: string | Buffer;
9
10
  nonWitnessUtxo?: string | Buffer;
11
+ signer?: RotationSigner;
10
12
  }
11
13
  export interface FetchUTXOParams {
12
14
  readonly address: string;