@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.
- package/LICENSE +190 -21
- package/README.md +1 -1
- package/browser/_version.d.ts +1 -1
- package/browser/generators/builders/HashCommitmentGenerator.d.ts +49 -0
- package/browser/index.js +1 -1
- package/browser/keypair/Address.d.ts +3 -1
- package/browser/opnet.d.ts +6 -1
- package/browser/signer/AddressRotation.d.ts +12 -0
- package/browser/transaction/TransactionFactory.d.ts +14 -0
- package/browser/transaction/builders/ConsolidatedInteractionTransaction.d.ts +44 -0
- package/browser/transaction/enums/TransactionType.d.ts +3 -1
- package/browser/transaction/interfaces/IConsolidatedTransactionParameters.d.ts +31 -0
- package/browser/transaction/interfaces/ITransactionParameters.d.ts +2 -0
- package/browser/transaction/offline/OfflineTransactionManager.d.ts +69 -0
- package/browser/transaction/offline/TransactionReconstructor.d.ts +28 -0
- package/browser/transaction/offline/TransactionSerializer.d.ts +50 -0
- package/browser/transaction/offline/TransactionStateCapture.d.ts +52 -0
- package/browser/transaction/offline/index.d.ts +5 -0
- package/browser/transaction/offline/interfaces/ISerializableState.d.ts +62 -0
- package/browser/transaction/offline/interfaces/ITypeSpecificData.d.ts +62 -0
- package/browser/transaction/offline/interfaces/index.d.ts +2 -0
- package/browser/transaction/shared/TweakedTransaction.d.ts +12 -1
- package/browser/utxo/interfaces/IUTXO.d.ts +2 -0
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/generators/builders/HashCommitmentGenerator.d.ts +49 -0
- package/build/generators/builders/HashCommitmentGenerator.js +229 -0
- package/build/keypair/Address.d.ts +3 -1
- package/build/keypair/Address.js +87 -54
- package/build/opnet.d.ts +6 -1
- package/build/opnet.js +6 -1
- package/build/signer/AddressRotation.d.ts +12 -0
- package/build/signer/AddressRotation.js +16 -0
- package/build/transaction/TransactionFactory.d.ts +14 -0
- package/build/transaction/TransactionFactory.js +36 -0
- package/build/transaction/builders/ConsolidatedInteractionTransaction.d.ts +44 -0
- package/build/transaction/builders/ConsolidatedInteractionTransaction.js +259 -0
- package/build/transaction/builders/TransactionBuilder.js +2 -0
- package/build/transaction/enums/TransactionType.d.ts +3 -1
- package/build/transaction/enums/TransactionType.js +2 -0
- package/build/transaction/interfaces/IConsolidatedTransactionParameters.d.ts +31 -0
- package/build/transaction/interfaces/IConsolidatedTransactionParameters.js +1 -0
- package/build/transaction/interfaces/ITransactionParameters.d.ts +2 -0
- package/build/transaction/offline/OfflineTransactionManager.d.ts +69 -0
- package/build/transaction/offline/OfflineTransactionManager.js +255 -0
- package/build/transaction/offline/TransactionReconstructor.d.ts +28 -0
- package/build/transaction/offline/TransactionReconstructor.js +243 -0
- package/build/transaction/offline/TransactionSerializer.d.ts +50 -0
- package/build/transaction/offline/TransactionSerializer.js +700 -0
- package/build/transaction/offline/TransactionStateCapture.d.ts +52 -0
- package/build/transaction/offline/TransactionStateCapture.js +275 -0
- package/build/transaction/offline/index.d.ts +5 -0
- package/build/transaction/offline/index.js +5 -0
- package/build/transaction/offline/interfaces/ISerializableState.d.ts +62 -0
- package/build/transaction/offline/interfaces/ISerializableState.js +2 -0
- package/build/transaction/offline/interfaces/ITypeSpecificData.d.ts +62 -0
- package/build/transaction/offline/interfaces/ITypeSpecificData.js +19 -0
- package/build/transaction/offline/interfaces/index.d.ts +2 -0
- package/build/transaction/offline/interfaces/index.js +2 -0
- package/build/transaction/shared/TweakedTransaction.d.ts +12 -1
- package/build/transaction/shared/TweakedTransaction.js +75 -8
- package/build/utxo/interfaces/IUTXO.d.ts +2 -0
- package/documentation/README.md +5 -0
- package/documentation/offline-transaction-signing.md +650 -0
- package/documentation/transaction-building.md +603 -0
- package/package.json +2 -2
- package/src/_version.ts +1 -1
- package/src/generators/builders/HashCommitmentGenerator.ts +495 -0
- package/src/keypair/Address.ts +123 -70
- package/src/opnet.ts +8 -1
- package/src/signer/AddressRotation.ts +72 -0
- package/src/transaction/TransactionFactory.ts +90 -0
- package/src/transaction/builders/CancelTransaction.ts +4 -2
- package/src/transaction/builders/ConsolidatedInteractionTransaction.ts +568 -0
- package/src/transaction/builders/CustomScriptTransaction.ts +4 -2
- package/src/transaction/builders/MultiSignTransaction.ts +4 -2
- package/src/transaction/builders/TransactionBuilder.ts +8 -2
- package/src/transaction/enums/TransactionType.ts +2 -0
- package/src/transaction/interfaces/IConsolidatedTransactionParameters.ts +78 -0
- package/src/transaction/interfaces/ITransactionParameters.ts +8 -0
- package/src/transaction/offline/OfflineTransactionManager.ts +630 -0
- package/src/transaction/offline/TransactionReconstructor.ts +402 -0
- package/src/transaction/offline/TransactionSerializer.ts +920 -0
- package/src/transaction/offline/TransactionStateCapture.ts +469 -0
- package/src/transaction/offline/index.ts +8 -0
- package/src/transaction/offline/interfaces/ISerializableState.ts +141 -0
- package/src/transaction/offline/interfaces/ITypeSpecificData.ts +172 -0
- package/src/transaction/offline/interfaces/index.ts +2 -0
- package/src/transaction/shared/TweakedTransaction.ts +156 -9
- package/src/utxo/interfaces/IUTXO.ts +8 -0
- package/test/address-rotation.test.ts +553 -0
- package/test/offline-transaction.test.ts +2065 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { TransactionType } from '../../enums/TransactionType.js';
|
|
2
|
+
import { RawChallenge } from '../../../epoch/interfaces/IChallengeSolution.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Discriminated union for type-specific serialized data
|
|
6
|
+
*/
|
|
7
|
+
export type TypeSpecificData =
|
|
8
|
+
| FundingSpecificData
|
|
9
|
+
| DeploymentSpecificData
|
|
10
|
+
| InteractionSpecificData
|
|
11
|
+
| MultiSigSpecificData
|
|
12
|
+
| CustomScriptSpecificData
|
|
13
|
+
| CancelSpecificData;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Funding transaction specific data
|
|
17
|
+
*/
|
|
18
|
+
export interface FundingSpecificData {
|
|
19
|
+
readonly type: TransactionType.FUNDING;
|
|
20
|
+
/** Amount to send in satoshis */
|
|
21
|
+
readonly amount: string; // bigint as string
|
|
22
|
+
/** Number of outputs to split into */
|
|
23
|
+
readonly splitInputsInto: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Deployment transaction specific data
|
|
28
|
+
*/
|
|
29
|
+
export interface DeploymentSpecificData {
|
|
30
|
+
readonly type: TransactionType.DEPLOYMENT;
|
|
31
|
+
/** Compressed bytecode (hex) */
|
|
32
|
+
readonly bytecode: string;
|
|
33
|
+
/** Constructor calldata (hex) */
|
|
34
|
+
readonly calldata?: string;
|
|
35
|
+
/** Challenge solution for epoch */
|
|
36
|
+
readonly challenge: RawChallenge;
|
|
37
|
+
/** Reveal MLDSA public key in transaction */
|
|
38
|
+
readonly revealMLDSAPublicKey?: boolean;
|
|
39
|
+
/** Link MLDSA public key to legacy address */
|
|
40
|
+
readonly linkMLDSAPublicKeyToAddress?: boolean;
|
|
41
|
+
/** Hashed MLDSA public key (hex) */
|
|
42
|
+
readonly hashedPublicKey?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Interaction transaction specific data
|
|
47
|
+
*/
|
|
48
|
+
export interface InteractionSpecificData {
|
|
49
|
+
readonly type: TransactionType.INTERACTION;
|
|
50
|
+
/** Compressed calldata (hex) */
|
|
51
|
+
readonly calldata: string;
|
|
52
|
+
/** Contract address/identifier */
|
|
53
|
+
readonly contract?: string;
|
|
54
|
+
/** Challenge solution for epoch */
|
|
55
|
+
readonly challenge: RawChallenge;
|
|
56
|
+
/** Loaded storage for access list */
|
|
57
|
+
readonly loadedStorage?: SerializedLoadedStorage;
|
|
58
|
+
/** Whether this is a cancellation */
|
|
59
|
+
readonly isCancellation?: boolean;
|
|
60
|
+
/** Disable auto refund */
|
|
61
|
+
readonly disableAutoRefund?: boolean;
|
|
62
|
+
/** Reveal MLDSA public key in transaction */
|
|
63
|
+
readonly revealMLDSAPublicKey?: boolean;
|
|
64
|
+
/** Link MLDSA public key to legacy address */
|
|
65
|
+
readonly linkMLDSAPublicKeyToAddress?: boolean;
|
|
66
|
+
/** Hashed MLDSA public key (hex) */
|
|
67
|
+
readonly hashedPublicKey?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Loaded storage serialization format
|
|
72
|
+
*/
|
|
73
|
+
export interface SerializedLoadedStorage {
|
|
74
|
+
[key: string]: string[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* MultiSig transaction specific data
|
|
79
|
+
*/
|
|
80
|
+
export interface MultiSigSpecificData {
|
|
81
|
+
readonly type: TransactionType.MULTI_SIG;
|
|
82
|
+
/** Public keys (hex array) */
|
|
83
|
+
readonly pubkeys: string[];
|
|
84
|
+
/** M of N (minimum signatures required) */
|
|
85
|
+
readonly minimumSignatures: number;
|
|
86
|
+
/** Receiver address */
|
|
87
|
+
readonly receiver: string;
|
|
88
|
+
/** Requested amount to send */
|
|
89
|
+
readonly requestedAmount: string; // bigint as string
|
|
90
|
+
/** Refund vault address */
|
|
91
|
+
readonly refundVault: string;
|
|
92
|
+
/** Original input count (for partial signing) */
|
|
93
|
+
readonly originalInputCount: number;
|
|
94
|
+
/** Existing PSBT state (base64) if partially signed */
|
|
95
|
+
readonly existingPsbtBase64?: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Serialized script element for custom scripts
|
|
100
|
+
*/
|
|
101
|
+
export interface SerializedScriptElement {
|
|
102
|
+
/** Element type */
|
|
103
|
+
readonly elementType: 'buffer' | 'opcode';
|
|
104
|
+
/** Value as hex (buffer) or opcode number */
|
|
105
|
+
readonly value: string | number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Custom script transaction specific data
|
|
110
|
+
*/
|
|
111
|
+
export interface CustomScriptSpecificData {
|
|
112
|
+
readonly type: TransactionType.CUSTOM_CODE;
|
|
113
|
+
/** Bitcoin script elements */
|
|
114
|
+
readonly scriptElements: SerializedScriptElement[];
|
|
115
|
+
/** Witnesses (hex array) */
|
|
116
|
+
readonly witnesses: string[];
|
|
117
|
+
/** Optional annex data (hex) */
|
|
118
|
+
readonly annex?: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Cancel transaction specific data
|
|
123
|
+
*/
|
|
124
|
+
export interface CancelSpecificData {
|
|
125
|
+
readonly type: TransactionType.CANCEL;
|
|
126
|
+
/** Compiled target script to cancel (hex) */
|
|
127
|
+
readonly compiledTargetScript: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Type guard for FundingSpecificData
|
|
132
|
+
*/
|
|
133
|
+
export function isFundingSpecificData(data: TypeSpecificData): data is FundingSpecificData {
|
|
134
|
+
return data.type === TransactionType.FUNDING;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Type guard for DeploymentSpecificData
|
|
139
|
+
*/
|
|
140
|
+
export function isDeploymentSpecificData(data: TypeSpecificData): data is DeploymentSpecificData {
|
|
141
|
+
return data.type === TransactionType.DEPLOYMENT;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Type guard for InteractionSpecificData
|
|
146
|
+
*/
|
|
147
|
+
export function isInteractionSpecificData(data: TypeSpecificData): data is InteractionSpecificData {
|
|
148
|
+
return data.type === TransactionType.INTERACTION;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Type guard for MultiSigSpecificData
|
|
153
|
+
*/
|
|
154
|
+
export function isMultiSigSpecificData(data: TypeSpecificData): data is MultiSigSpecificData {
|
|
155
|
+
return data.type === TransactionType.MULTI_SIG;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Type guard for CustomScriptSpecificData
|
|
160
|
+
*/
|
|
161
|
+
export function isCustomScriptSpecificData(
|
|
162
|
+
data: TypeSpecificData,
|
|
163
|
+
): data is CustomScriptSpecificData {
|
|
164
|
+
return data.type === TransactionType.CUSTOM_CODE;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Type guard for CancelSpecificData
|
|
169
|
+
*/
|
|
170
|
+
export function isCancelSpecificData(data: TypeSpecificData): data is CancelSpecificData {
|
|
171
|
+
return data.type === TransactionType.CANCEL;
|
|
172
|
+
}
|
|
@@ -43,6 +43,7 @@ import { Buffer } from 'buffer';
|
|
|
43
43
|
import { P2WDADetector } from '../../p2wda/P2WDADetector.js';
|
|
44
44
|
import { QuantumBIP32Interface } from '@btc-vision/bip32';
|
|
45
45
|
import { MessageSigner } from '../../keypair/MessageSigner.js';
|
|
46
|
+
import { AddressRotationConfig, RotationSigner, SignerMap } from '../../signer/AddressRotation.js';
|
|
46
47
|
|
|
47
48
|
export type SupportedTransactionVersion = 1 | 2 | 3;
|
|
48
49
|
|
|
@@ -55,6 +56,12 @@ export interface ITweakedTransactionData {
|
|
|
55
56
|
readonly noSignatures?: boolean;
|
|
56
57
|
readonly unlockScript?: Buffer[];
|
|
57
58
|
readonly txVersion?: SupportedTransactionVersion;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Address rotation configuration for per-UTXO signing.
|
|
62
|
+
* When enabled, each UTXO can be signed by a different signer.
|
|
63
|
+
*/
|
|
64
|
+
readonly addressRotation?: AddressRotationConfig;
|
|
58
65
|
}
|
|
59
66
|
|
|
60
67
|
/**
|
|
@@ -165,6 +172,28 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
165
172
|
protected readonly _mldsaSigner: QuantumBIP32Interface | null = null;
|
|
166
173
|
protected readonly _hashedPublicKey: Buffer | null = null;
|
|
167
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Whether address rotation mode is enabled.
|
|
177
|
+
* When true, each UTXO can be signed by a different signer.
|
|
178
|
+
*/
|
|
179
|
+
protected readonly addressRotationEnabled: boolean = false;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Map of addresses to their respective signers for address rotation mode.
|
|
183
|
+
*/
|
|
184
|
+
protected readonly signerMap: SignerMap = new Map();
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Map of input indices to their signers (resolved from UTXOs or signerMap).
|
|
188
|
+
* Populated during input addition.
|
|
189
|
+
*/
|
|
190
|
+
protected readonly inputSignerMap: Map<number, RotationSigner> = new Map();
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Cache of tweaked signers per input for address rotation mode.
|
|
194
|
+
*/
|
|
195
|
+
protected readonly tweakedSignerCache: Map<number, ECPairInterface | undefined> = new Map();
|
|
196
|
+
|
|
168
197
|
protected constructor(data: ITweakedTransactionData) {
|
|
169
198
|
super();
|
|
170
199
|
|
|
@@ -185,6 +214,12 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
185
214
|
this._mldsaSigner = data.mldsaSigner;
|
|
186
215
|
this._hashedPublicKey = MessageSigner.sha256(this._mldsaSigner.publicKey);
|
|
187
216
|
}
|
|
217
|
+
|
|
218
|
+
// Initialize address rotation
|
|
219
|
+
if (data.addressRotation?.enabled) {
|
|
220
|
+
this.addressRotationEnabled = true;
|
|
221
|
+
this.signerMap = data.addressRotation.signerMap;
|
|
222
|
+
}
|
|
188
223
|
}
|
|
189
224
|
|
|
190
225
|
/**
|
|
@@ -323,6 +358,13 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
323
358
|
return signHash || 0;
|
|
324
359
|
}
|
|
325
360
|
|
|
361
|
+
/**
|
|
362
|
+
* Check if address rotation mode is enabled.
|
|
363
|
+
*/
|
|
364
|
+
public isAddressRotationEnabled(): boolean {
|
|
365
|
+
return this.addressRotationEnabled;
|
|
366
|
+
}
|
|
367
|
+
|
|
326
368
|
public ignoreSignatureError(): void {
|
|
327
369
|
this.ignoreSignatureErrors = true;
|
|
328
370
|
}
|
|
@@ -425,6 +467,93 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
425
467
|
return vSize * feeRate;
|
|
426
468
|
}
|
|
427
469
|
|
|
470
|
+
/**
|
|
471
|
+
* Get the signer for a specific input index.
|
|
472
|
+
* Returns the input-specific signer if in rotation mode, otherwise the default signer.
|
|
473
|
+
* @param inputIndex - The index of the input
|
|
474
|
+
*/
|
|
475
|
+
protected getSignerForInput(inputIndex: number): RotationSigner {
|
|
476
|
+
if (this.addressRotationEnabled) {
|
|
477
|
+
const inputSigner = this.inputSignerMap.get(inputIndex);
|
|
478
|
+
if (inputSigner) {
|
|
479
|
+
return inputSigner;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return this.signer;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Register a signer for a specific input index.
|
|
487
|
+
* Called during UTXO processing to map each input to its signer.
|
|
488
|
+
* @param inputIndex - The index of the input
|
|
489
|
+
* @param utxo - The UTXO being added
|
|
490
|
+
*/
|
|
491
|
+
protected registerInputSigner(inputIndex: number, utxo: UTXO): void {
|
|
492
|
+
if (!this.addressRotationEnabled) {
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Priority 1: UTXO has an explicit signer attached
|
|
497
|
+
if (utxo.signer) {
|
|
498
|
+
this.inputSignerMap.set(inputIndex, utxo.signer);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Priority 2: Look up signer from signerMap by address
|
|
503
|
+
const address = utxo.scriptPubKey?.address;
|
|
504
|
+
if (address && this.signerMap.has(address)) {
|
|
505
|
+
const signer = this.signerMap.get(address);
|
|
506
|
+
if (signer) {
|
|
507
|
+
this.inputSignerMap.set(inputIndex, signer);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Fallback: Use default signer (no entry in inputSignerMap)
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Get the x-only public key for a specific input's signer.
|
|
517
|
+
* Used for taproot inputs in address rotation mode.
|
|
518
|
+
* @param inputIndex - The index of the input
|
|
519
|
+
*/
|
|
520
|
+
protected internalPubKeyToXOnlyForInput(inputIndex: number): Buffer {
|
|
521
|
+
const signer = this.getSignerForInput(inputIndex);
|
|
522
|
+
return toXOnly(Buffer.from(signer.publicKey));
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Get the tweaked signer for a specific input.
|
|
527
|
+
* Caches the result for efficiency.
|
|
528
|
+
* @param inputIndex - The index of the input
|
|
529
|
+
* @param useTweakedHash - Whether to use the tweaked hash
|
|
530
|
+
*/
|
|
531
|
+
protected getTweakedSignerForInput(
|
|
532
|
+
inputIndex: number,
|
|
533
|
+
useTweakedHash: boolean = false,
|
|
534
|
+
): ECPairInterface | undefined {
|
|
535
|
+
if (!this.addressRotationEnabled) {
|
|
536
|
+
// Fall back to original behavior
|
|
537
|
+
if (useTweakedHash) {
|
|
538
|
+
this.tweakSigner();
|
|
539
|
+
return this.tweakedSigner;
|
|
540
|
+
}
|
|
541
|
+
return this.getTweakedSigner(useTweakedHash);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Check cache
|
|
545
|
+
const cacheKey = inputIndex * 2 + (useTweakedHash ? 1 : 0);
|
|
546
|
+
if (this.tweakedSignerCache.has(cacheKey)) {
|
|
547
|
+
return this.tweakedSignerCache.get(cacheKey);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const signer = this.getSignerForInput(inputIndex);
|
|
551
|
+
const tweaked = this.getTweakedSigner(useTweakedHash, signer);
|
|
552
|
+
this.tweakedSignerCache.set(cacheKey, tweaked);
|
|
553
|
+
|
|
554
|
+
return tweaked;
|
|
555
|
+
}
|
|
556
|
+
|
|
428
557
|
protected generateTapData(): P2TRPayment {
|
|
429
558
|
return {
|
|
430
559
|
internalPubkey: this.internalPubKeyToXOnly(),
|
|
@@ -567,7 +696,9 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
567
696
|
const input = batch[j];
|
|
568
697
|
|
|
569
698
|
try {
|
|
570
|
-
|
|
699
|
+
// Use per-input signer in address rotation mode
|
|
700
|
+
const inputSigner = this.getSignerForInput(index);
|
|
701
|
+
promises.push(this.signInput(transaction, input, index, inputSigner));
|
|
571
702
|
} catch (e) {
|
|
572
703
|
this.log(`Failed to sign input ${index}: ${(e as Error).stack}`);
|
|
573
704
|
}
|
|
@@ -696,15 +827,24 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
696
827
|
return;
|
|
697
828
|
}
|
|
698
829
|
|
|
699
|
-
protected generateP2SHP2PKHRedeemScript(
|
|
830
|
+
protected generateP2SHP2PKHRedeemScript(
|
|
831
|
+
inputAddr: string,
|
|
832
|
+
inputIndex?: number,
|
|
833
|
+
):
|
|
700
834
|
| {
|
|
701
835
|
redeemScript: Buffer;
|
|
702
836
|
outputScript: Buffer;
|
|
703
837
|
}
|
|
704
838
|
| undefined {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
839
|
+
// Use per-input signer in address rotation mode
|
|
840
|
+
const signer =
|
|
841
|
+
this.addressRotationEnabled && inputIndex !== undefined
|
|
842
|
+
? this.getSignerForInput(inputIndex)
|
|
843
|
+
: this.signer;
|
|
844
|
+
|
|
845
|
+
const pubkey = Buffer.isBuffer(signer.publicKey)
|
|
846
|
+
? signer.publicKey
|
|
847
|
+
: Buffer.from(signer.publicKey, 'hex');
|
|
708
848
|
|
|
709
849
|
const w = payments.p2wpkh({
|
|
710
850
|
pubkey: pubkey,
|
|
@@ -798,7 +938,10 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
798
938
|
);
|
|
799
939
|
}
|
|
800
940
|
|
|
801
|
-
const legacyScripts = this.generateP2SHP2PKHRedeemScript(
|
|
941
|
+
const legacyScripts = this.generateP2SHP2PKHRedeemScript(
|
|
942
|
+
utxo.scriptPubKey.address,
|
|
943
|
+
i,
|
|
944
|
+
);
|
|
802
945
|
if (!legacyScripts) {
|
|
803
946
|
throw new Error('Missing redeemScript for P2SH UTXO and unable to regenerate');
|
|
804
947
|
}
|
|
@@ -853,9 +996,13 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
853
996
|
if (inputSign) input.sighashType = inputSign;
|
|
854
997
|
}
|
|
855
998
|
|
|
856
|
-
// Taproot internal key
|
|
857
|
-
this.
|
|
858
|
-
|
|
999
|
+
// Taproot internal key - use per-input signer in address rotation mode
|
|
1000
|
+
if (this.addressRotationEnabled) {
|
|
1001
|
+
input.tapInternalKey = this.internalPubKeyToXOnlyForInput(i);
|
|
1002
|
+
} else {
|
|
1003
|
+
this.tweakSigner();
|
|
1004
|
+
input.tapInternalKey = this.internalPubKeyToXOnly();
|
|
1005
|
+
}
|
|
859
1006
|
}
|
|
860
1007
|
|
|
861
1008
|
// Handle P2A (Any SegWit version, future versions)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ScriptPubKey } from '@btc-vision/bitcoin-rpc';
|
|
2
|
+
import { RotationSigner } from '../../signer/AddressRotation.js';
|
|
2
3
|
|
|
3
4
|
export interface UTXO {
|
|
4
5
|
readonly transactionId: string;
|
|
@@ -9,6 +10,13 @@ export interface UTXO {
|
|
|
9
10
|
redeemScript?: string | Buffer;
|
|
10
11
|
witnessScript?: string | Buffer;
|
|
11
12
|
nonWitnessUtxo?: string | Buffer;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Optional signer for this specific UTXO.
|
|
16
|
+
* Used in address rotation mode where each UTXO may have its own signer.
|
|
17
|
+
* If not provided, the signer will be resolved from the signerMap or the default signer.
|
|
18
|
+
*/
|
|
19
|
+
signer?: RotationSigner;
|
|
12
20
|
}
|
|
13
21
|
|
|
14
22
|
export interface FetchUTXOParams {
|