@btc-vision/transaction 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser/_version.d.ts +1 -1
- package/browser/index.js +1 -1
- package/browser/transaction/browser/extensions/UnisatSigner.d.ts +4 -4
- package/browser/transaction/browser/types/Unisat.d.ts +1 -1
- package/browser/transaction/builders/DeploymentTransaction.d.ts +1 -0
- package/browser/transaction/builders/SharedInteractionTransaction.d.ts +2 -0
- package/browser/transaction/builders/TransactionBuilder.d.ts +2 -1
- package/browser/transaction/shared/TweakedTransaction.d.ts +6 -3
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/buffer/BinaryWriter.js +0 -1
- package/build/transaction/browser/extensions/UnisatSigner.d.ts +4 -4
- package/build/transaction/browser/extensions/UnisatSigner.js +103 -20
- package/build/transaction/browser/types/Unisat.d.ts +1 -1
- package/build/transaction/builders/DeploymentTransaction.d.ts +1 -0
- package/build/transaction/builders/DeploymentTransaction.js +17 -0
- package/build/transaction/builders/SharedInteractionTransaction.d.ts +2 -0
- package/build/transaction/builders/SharedInteractionTransaction.js +31 -10
- package/build/transaction/builders/TransactionBuilder.d.ts +2 -1
- package/build/transaction/shared/TweakedTransaction.d.ts +6 -3
- package/build/transaction/shared/TweakedTransaction.js +61 -43
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/buffer/BinaryWriter.ts +0 -2
- package/src/transaction/browser/extensions/UnisatSigner.ts +139 -28
- package/src/transaction/browser/types/Unisat.ts +1 -1
- package/src/transaction/builders/DeploymentTransaction.ts +25 -0
- package/src/transaction/builders/SharedInteractionTransaction.ts +51 -21
- package/src/transaction/builders/TransactionBuilder.ts +2 -1
- package/src/transaction/shared/TweakedTransaction.ts +90 -49
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
crypto as bitCrypto,
|
|
3
|
+
Network,
|
|
4
|
+
networks,
|
|
5
|
+
opcodes,
|
|
6
|
+
Psbt,
|
|
7
|
+
PsbtInput,
|
|
8
|
+
script as bitScript,
|
|
9
|
+
TapScriptSig,
|
|
10
|
+
} from '@btc-vision/bitcoin';
|
|
2
11
|
import { ECPairInterface } from 'ecpair';
|
|
3
12
|
import { EcKeyPair } from '../../../keypair/EcKeyPair.js';
|
|
4
13
|
import { CustomKeypair } from '../BrowserSignerBase.js';
|
|
5
14
|
import { PsbtSignatureOptions, Unisat, UnisatNetwork } from '../types/Unisat.js';
|
|
6
15
|
import { PartialSig } from 'bip174/src/lib/interfaces.js';
|
|
16
|
+
import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
|
|
7
17
|
|
|
8
18
|
declare global {
|
|
9
19
|
interface Window {
|
|
@@ -122,15 +132,15 @@ export class UnisatSigner extends CustomKeypair {
|
|
|
122
132
|
return this.publicKey;
|
|
123
133
|
}
|
|
124
134
|
|
|
125
|
-
public sign(
|
|
135
|
+
public sign(_hash: Buffer, _lowR?: boolean): Buffer {
|
|
126
136
|
throw new Error('Not implemented: sign');
|
|
127
137
|
}
|
|
128
138
|
|
|
129
|
-
public signSchnorr(
|
|
139
|
+
public signSchnorr(_hash: Buffer): Buffer {
|
|
130
140
|
throw new Error('Not implemented: signSchnorr');
|
|
131
141
|
}
|
|
132
142
|
|
|
133
|
-
public verify(
|
|
143
|
+
public verify(_hash: Buffer, _signature: Buffer): boolean {
|
|
134
144
|
throw new Error('Not implemented: verify');
|
|
135
145
|
}
|
|
136
146
|
|
|
@@ -176,6 +186,80 @@ export class UnisatSigner extends CustomKeypair {
|
|
|
176
186
|
this.combine(transaction, firstSignature, i);
|
|
177
187
|
}
|
|
178
188
|
|
|
189
|
+
public async multiSignPsbt(transactions: Psbt[]): Promise<void> {
|
|
190
|
+
const toSignPsbts: string[] = [];
|
|
191
|
+
const options: PsbtSignatureOptions[] = [];
|
|
192
|
+
|
|
193
|
+
for (const psbt of transactions) {
|
|
194
|
+
const hex = psbt.toHex();
|
|
195
|
+
toSignPsbts.push(hex);
|
|
196
|
+
|
|
197
|
+
const toSignInputs = psbt.data.inputs
|
|
198
|
+
.map((input, i) => {
|
|
199
|
+
let needsToSign = false;
|
|
200
|
+
let viaTaproot = false;
|
|
201
|
+
|
|
202
|
+
if (isTaprootInput(input)) {
|
|
203
|
+
if (input.tapLeafScript && input.tapLeafScript.length > 0) {
|
|
204
|
+
for (const tapLeafScript of input.tapLeafScript) {
|
|
205
|
+
if (pubkeyInScript(this.publicKey, tapLeafScript.script)) {
|
|
206
|
+
needsToSign = true;
|
|
207
|
+
viaTaproot = false; // for opnet, we use original keys.
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (!needsToSign && input.tapInternalKey) {
|
|
214
|
+
const tapInternalKey = input.tapInternalKey;
|
|
215
|
+
const xOnlyPubKey = toXOnly(this.publicKey);
|
|
216
|
+
|
|
217
|
+
if (tapInternalKey.equals(xOnlyPubKey)) {
|
|
218
|
+
needsToSign = true;
|
|
219
|
+
viaTaproot = true;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
// Non-Taproot input
|
|
224
|
+
const script = getInputRelevantScript(input);
|
|
225
|
+
|
|
226
|
+
if (script && pubkeyInScript(this.publicKey, script)) {
|
|
227
|
+
needsToSign = true;
|
|
228
|
+
viaTaproot = false;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (needsToSign) {
|
|
233
|
+
return {
|
|
234
|
+
index: i,
|
|
235
|
+
publicKey: this.publicKey.toString('hex'),
|
|
236
|
+
disableTweakSigner: !viaTaproot,
|
|
237
|
+
};
|
|
238
|
+
} else {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
})
|
|
242
|
+
.filter((v) => v !== null);
|
|
243
|
+
|
|
244
|
+
options.push({
|
|
245
|
+
autoFinalized: false,
|
|
246
|
+
toSignInputs: toSignInputs,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const signed = await this.unisat.signPsbt(toSignPsbts[0], options[0]);
|
|
251
|
+
const signedPsbts = Psbt.fromHex(signed); //signed.map((hex) => Psbt.fromHex(hex));
|
|
252
|
+
|
|
253
|
+
/*for (let i = 0; i < signedPsbts.length; i++) {
|
|
254
|
+
const psbtOriginal = transactions[i];
|
|
255
|
+
const psbtSigned = signedPsbts[i];
|
|
256
|
+
|
|
257
|
+
psbtOriginal.combine(psbtSigned);
|
|
258
|
+
}*/
|
|
259
|
+
|
|
260
|
+
transactions[0].combine(signedPsbts);
|
|
261
|
+
}
|
|
262
|
+
|
|
179
263
|
private hasAlreadySignedTapScriptSig(input: TapScriptSig[]): boolean {
|
|
180
264
|
for (let i = 0; i < input.length; i++) {
|
|
181
265
|
const item = input[i];
|
|
@@ -257,30 +341,6 @@ export class UnisatSigner extends CustomKeypair {
|
|
|
257
341
|
return Psbt.fromHex(signed);
|
|
258
342
|
}
|
|
259
343
|
|
|
260
|
-
private async signTweaked(
|
|
261
|
-
transaction: Psbt,
|
|
262
|
-
i: number,
|
|
263
|
-
sighashTypes: number[],
|
|
264
|
-
disableTweakSigner: boolean = false,
|
|
265
|
-
): Promise<Psbt> {
|
|
266
|
-
const opts: PsbtSignatureOptions = {
|
|
267
|
-
autoFinalized: false,
|
|
268
|
-
toSignInputs: [
|
|
269
|
-
{
|
|
270
|
-
index: i,
|
|
271
|
-
publicKey: this.publicKey.toString('hex'),
|
|
272
|
-
sighashTypes,
|
|
273
|
-
disableTweakSigner: disableTweakSigner,
|
|
274
|
-
},
|
|
275
|
-
],
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
const psbt = transaction.toHex();
|
|
279
|
-
const signed = await this.unisat.signPsbt(psbt, opts);
|
|
280
|
-
|
|
281
|
-
return Psbt.fromHex(signed);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
344
|
private getNonDuplicateScriptSig(
|
|
285
345
|
scriptSig1: TapScriptSig[],
|
|
286
346
|
scriptSig2: TapScriptSig[],
|
|
@@ -296,3 +356,54 @@ export class UnisatSigner extends CustomKeypair {
|
|
|
296
356
|
return nonDuplicate;
|
|
297
357
|
}
|
|
298
358
|
}
|
|
359
|
+
|
|
360
|
+
// Helper functions
|
|
361
|
+
function isTaprootInput(input: PsbtInput): boolean {
|
|
362
|
+
if (input.tapInternalKey || input.tapKeySig || input.tapScriptSig || input.tapLeafScript) {
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (input.witnessUtxo) {
|
|
367
|
+
const script = input.witnessUtxo.script;
|
|
368
|
+
return script.length === 34 && script[0] === opcodes.OP_1 && script[1] === 0x20;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function getInputRelevantScript(input: PsbtInput): Buffer | null {
|
|
375
|
+
if (input.redeemScript) {
|
|
376
|
+
return input.redeemScript;
|
|
377
|
+
}
|
|
378
|
+
if (input.witnessScript) {
|
|
379
|
+
return input.witnessScript;
|
|
380
|
+
}
|
|
381
|
+
if (input.witnessUtxo) {
|
|
382
|
+
return input.witnessUtxo.script;
|
|
383
|
+
}
|
|
384
|
+
if (input.nonWitnessUtxo) {
|
|
385
|
+
// Additional logic can be added here if needed
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean {
|
|
392
|
+
return pubkeyPositionInScript(pubkey, script) !== -1;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function pubkeyPositionInScript(pubkey: Buffer, script: Buffer): number {
|
|
396
|
+
const pubkeyHash = bitCrypto.hash160(pubkey);
|
|
397
|
+
const pubkeyXOnly = toXOnly(pubkey);
|
|
398
|
+
|
|
399
|
+
const decompiled = bitScript.decompile(script);
|
|
400
|
+
if (decompiled === null) throw new Error('Unknown script error');
|
|
401
|
+
|
|
402
|
+
return decompiled.findIndex((element) => {
|
|
403
|
+
if (typeof element === 'number') return false;
|
|
404
|
+
return (
|
|
405
|
+
Buffer.isBuffer(element) &&
|
|
406
|
+
(element.equals(pubkey) || element.equals(pubkeyHash) || element.equals(pubkeyXOnly))
|
|
407
|
+
);
|
|
408
|
+
});
|
|
409
|
+
}
|
|
@@ -83,7 +83,7 @@ export interface Unisat {
|
|
|
83
83
|
|
|
84
84
|
signPsbt(psbtHex: string, psbtOptions: PsbtSignatureOptions): Promise<string>;
|
|
85
85
|
|
|
86
|
-
signPsbts(psbtHex: string[], psbtOptions: PsbtSignatureOptions): Promise<string[]>;
|
|
86
|
+
signPsbts(psbtHex: string[], psbtOptions: PsbtSignatureOptions[]): Promise<string[]>;
|
|
87
87
|
|
|
88
88
|
pushPsbt(psbtHex: string): Promise<string>;
|
|
89
89
|
|
|
@@ -12,6 +12,7 @@ import { Compressor } from '../../bytecode/Compressor.js';
|
|
|
12
12
|
import { SharedInteractionTransaction } from './SharedInteractionTransaction.js';
|
|
13
13
|
import { ECPairInterface } from 'ecpair';
|
|
14
14
|
import { Address } from '../../keypair/Address.js';
|
|
15
|
+
import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
|
|
15
16
|
|
|
16
17
|
export class DeploymentTransaction extends TransactionBuilder<TransactionType.DEPLOYMENT> {
|
|
17
18
|
public static readonly MAXIMUM_CONTRACT_SIZE = 128 * 1024;
|
|
@@ -203,6 +204,25 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
|
|
|
203
204
|
await this.addRefundOutput(amountSpent);
|
|
204
205
|
}
|
|
205
206
|
|
|
207
|
+
protected override async signInputsWalletBased(transaction: Psbt): Promise<void> {
|
|
208
|
+
const signer: UnisatSigner = this.signer as UnisatSigner;
|
|
209
|
+
|
|
210
|
+
// first, we sign the first input with the script signer.
|
|
211
|
+
await this.signInput(transaction, transaction.data.inputs[0], 0, this.contractSigner);
|
|
212
|
+
|
|
213
|
+
// then, we sign all the remaining inputs with the wallet signer.
|
|
214
|
+
await signer.multiSignPsbt([transaction]);
|
|
215
|
+
|
|
216
|
+
// Then, we finalize every input.
|
|
217
|
+
for (let i = 0; i < transaction.data.inputs.length; i++) {
|
|
218
|
+
if (i === 0) {
|
|
219
|
+
transaction.finalizeInput(i, this.customFinalizer);
|
|
220
|
+
} else {
|
|
221
|
+
transaction.finalizeInput(i);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
206
226
|
/**
|
|
207
227
|
* Sign the inputs
|
|
208
228
|
* @param {Psbt} transaction The transaction to sign
|
|
@@ -215,6 +235,11 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
|
|
|
215
235
|
return;
|
|
216
236
|
}
|
|
217
237
|
|
|
238
|
+
if ('multiSignPsbt' in this.signer) {
|
|
239
|
+
await this.signInputsWalletBased(transaction);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
218
243
|
for (let i = 0; i < transaction.data.inputs.length; i++) {
|
|
219
244
|
if (i === 0) {
|
|
220
245
|
// multi sig input
|
|
@@ -9,6 +9,7 @@ import { Compressor } from '../../bytecode/Compressor.js';
|
|
|
9
9
|
import { EcKeyPair } from '../../keypair/EcKeyPair.js';
|
|
10
10
|
import { BitcoinUtils } from '../../utils/BitcoinUtils.js';
|
|
11
11
|
import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
|
|
12
|
+
import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Shared interaction transaction
|
|
@@ -186,27 +187,10 @@ export abstract class SharedInteractionTransaction<
|
|
|
186
187
|
return;
|
|
187
188
|
}
|
|
188
189
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
transaction,
|
|
194
|
-
transaction.data.inputs[i],
|
|
195
|
-
i,
|
|
196
|
-
this.getSignerKey(),
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
transaction.finalizeInput(i, this.customFinalizer);
|
|
200
|
-
} else {
|
|
201
|
-
await this.signInput(
|
|
202
|
-
transaction,
|
|
203
|
-
transaction.data.inputs[i],
|
|
204
|
-
i,
|
|
205
|
-
this.getSignerKey(),
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
transaction.finalizeInput(i);
|
|
209
|
-
}
|
|
190
|
+
if ('multiSignPsbt' in this.signer) {
|
|
191
|
+
await this.signInputsWalletBased(transaction);
|
|
192
|
+
} else {
|
|
193
|
+
await this.signInputsNonWalletBased(transaction);
|
|
210
194
|
}
|
|
211
195
|
}
|
|
212
196
|
|
|
@@ -308,6 +292,52 @@ export abstract class SharedInteractionTransaction<
|
|
|
308
292
|
};
|
|
309
293
|
};
|
|
310
294
|
|
|
295
|
+
// custom for interactions
|
|
296
|
+
protected override async signInputsWalletBased(transaction: Psbt): Promise<void> {
|
|
297
|
+
const signer: UnisatSigner = this.signer as UnisatSigner;
|
|
298
|
+
|
|
299
|
+
// first, we sign the first input with the script signer.
|
|
300
|
+
await this.signInput(transaction, transaction.data.inputs[0], 0, this.scriptSigner);
|
|
301
|
+
|
|
302
|
+
// then, we sign all the remaining inputs with the wallet signer.
|
|
303
|
+
await signer.multiSignPsbt([transaction]);
|
|
304
|
+
|
|
305
|
+
// Then, we finalize every input.
|
|
306
|
+
for (let i = 0; i < transaction.data.inputs.length; i++) {
|
|
307
|
+
if (i === 0) {
|
|
308
|
+
transaction.finalizeInput(i, this.customFinalizer);
|
|
309
|
+
} else {
|
|
310
|
+
transaction.finalizeInput(i);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private async signInputsNonWalletBased(transaction: Psbt): Promise<void> {
|
|
316
|
+
for (let i = 0; i < transaction.data.inputs.length; i++) {
|
|
317
|
+
if (i === 0) {
|
|
318
|
+
await this.signInput(transaction, transaction.data.inputs[i], i, this.scriptSigner);
|
|
319
|
+
|
|
320
|
+
await this.signInput(
|
|
321
|
+
transaction,
|
|
322
|
+
transaction.data.inputs[i],
|
|
323
|
+
i,
|
|
324
|
+
this.getSignerKey(),
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
transaction.finalizeInput(i, this.customFinalizer);
|
|
328
|
+
} else {
|
|
329
|
+
await this.signInput(
|
|
330
|
+
transaction,
|
|
331
|
+
transaction.data.inputs[i],
|
|
332
|
+
i,
|
|
333
|
+
this.getSignerKey(),
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
transaction.finalizeInput(i);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
311
341
|
/**
|
|
312
342
|
* Get the public keys
|
|
313
343
|
* @private
|
|
@@ -22,6 +22,7 @@ import { UTXO } from '../../utxo/interfaces/IUTXO.js';
|
|
|
22
22
|
import { ECPairInterface } from 'ecpair';
|
|
23
23
|
import { AddressVerificator } from '../../keypair/AddressVerificator.js';
|
|
24
24
|
import { TweakedTransaction } from '../shared/TweakedTransaction.js';
|
|
25
|
+
import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
|
|
25
26
|
|
|
26
27
|
initEccLib(ecc);
|
|
27
28
|
|
|
@@ -91,7 +92,7 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
|
|
|
91
92
|
/**
|
|
92
93
|
* @description The signer of the transaction
|
|
93
94
|
*/
|
|
94
|
-
protected readonly signer: Signer | ECPairInterface;
|
|
95
|
+
protected readonly signer: Signer | ECPairInterface | UnisatSigner;
|
|
95
96
|
|
|
96
97
|
/**
|
|
97
98
|
* @description The network where the transaction will be broadcasted
|
|
@@ -24,9 +24,10 @@ import { AddressTypes, AddressVerificator } from '../../keypair/AddressVerificat
|
|
|
24
24
|
import { ChainId } from '../../network/ChainId.js';
|
|
25
25
|
import { varuint } from '@btc-vision/bitcoin/src/bufferutils.js';
|
|
26
26
|
import * as bscript from '@btc-vision/bitcoin/src/script.js';
|
|
27
|
+
import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
|
|
27
28
|
|
|
28
29
|
export interface ITweakedTransactionData {
|
|
29
|
-
readonly signer: Signer | ECPairInterface;
|
|
30
|
+
readonly signer: Signer | ECPairInterface | UnisatSigner;
|
|
30
31
|
readonly network: Network;
|
|
31
32
|
readonly chainId?: ChainId;
|
|
32
33
|
readonly nonWitnessUtxo?: Buffer;
|
|
@@ -46,22 +47,27 @@ export enum TransactionSequence {
|
|
|
46
47
|
export abstract class TweakedTransaction extends Logger {
|
|
47
48
|
public readonly logColor: string = '#00ffe1';
|
|
48
49
|
public finalized: boolean = false;
|
|
50
|
+
|
|
49
51
|
/**
|
|
50
52
|
* @description Was the transaction signed?
|
|
51
53
|
*/
|
|
52
|
-
protected signer: Signer | ECPairInterface;
|
|
54
|
+
protected signer: Signer | ECPairInterface | UnisatSigner;
|
|
55
|
+
|
|
53
56
|
/**
|
|
54
57
|
* @description Tweaked signer
|
|
55
58
|
*/
|
|
56
59
|
protected tweakedSigner?: ECPairInterface;
|
|
60
|
+
|
|
57
61
|
/**
|
|
58
62
|
* @description The network of the transaction
|
|
59
63
|
*/
|
|
60
64
|
protected network: Network;
|
|
65
|
+
|
|
61
66
|
/**
|
|
62
67
|
* @description Was the transaction signed?
|
|
63
68
|
*/
|
|
64
69
|
protected signed: boolean = false;
|
|
70
|
+
|
|
65
71
|
/**
|
|
66
72
|
* @description The transaction
|
|
67
73
|
* @protected
|
|
@@ -363,6 +369,7 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
363
369
|
* @param {PsbtInput} input - The input to sign
|
|
364
370
|
* @param {number} i - The index of the input
|
|
365
371
|
* @param {Signer} signer - The signer to use
|
|
372
|
+
* @param {boolean} [reverse=false] - Should the input be signed in reverse
|
|
366
373
|
* @protected
|
|
367
374
|
*/
|
|
368
375
|
protected async signInput(
|
|
@@ -370,45 +377,27 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
370
377
|
input: PsbtInput,
|
|
371
378
|
i: number,
|
|
372
379
|
signer: Signer | ECPairInterface,
|
|
380
|
+
reverse: boolean = false,
|
|
373
381
|
): Promise<void> {
|
|
374
382
|
const publicKey = signer.publicKey;
|
|
375
|
-
|
|
383
|
+
let isTaproot = this.isTaprootInput(input);
|
|
376
384
|
|
|
377
|
-
|
|
385
|
+
if (reverse) {
|
|
386
|
+
isTaproot = !isTaproot;
|
|
387
|
+
}
|
|
378
388
|
|
|
379
|
-
|
|
380
|
-
const isScriptSpend = this.isTaprootScriptSpend(input, publicKey);
|
|
389
|
+
let signed: boolean = false;
|
|
381
390
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
}
|
|
389
|
-
} else {
|
|
390
|
-
let tweakedSigner: ECPairInterface | undefined;
|
|
391
|
-
if (signer !== this.signer) {
|
|
392
|
-
tweakedSigner = this.getTweakedSigner(true, signer);
|
|
393
|
-
} else {
|
|
394
|
-
if (!this.tweakedSigner) this.tweakSigner();
|
|
395
|
-
tweakedSigner = this.tweakedSigner;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
if (tweakedSigner) {
|
|
399
|
-
try {
|
|
400
|
-
await this.signTaprootInput(tweakedSigner, transaction, i);
|
|
401
|
-
signed = true;
|
|
402
|
-
} catch (e) {
|
|
403
|
-
this.error(`Failed to sign Taproot key path input ${i}: ${e}`);
|
|
404
|
-
}
|
|
405
|
-
} else {
|
|
406
|
-
this.error(`Failed to obtain tweaked signer for input ${i}.`);
|
|
407
|
-
}
|
|
391
|
+
if (isTaproot) {
|
|
392
|
+
try {
|
|
393
|
+
await this.attemptSignTaproot(transaction, input, i, signer, publicKey);
|
|
394
|
+
signed = true;
|
|
395
|
+
} catch (e) {
|
|
396
|
+
this.error(`Failed to sign Taproot script path input ${i}: ${e}`);
|
|
408
397
|
}
|
|
409
398
|
} else {
|
|
410
399
|
// Non-Taproot input
|
|
411
|
-
if (this.canSignNonTaprootInput(input, publicKey)) {
|
|
400
|
+
if (!reverse ? this.canSignNonTaprootInput(input, publicKey) : true) {
|
|
412
401
|
try {
|
|
413
402
|
await this.signNonTaprootInput(signer, transaction, i);
|
|
414
403
|
signed = true;
|
|
@@ -419,7 +408,11 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
419
408
|
}
|
|
420
409
|
|
|
421
410
|
if (!signed) {
|
|
422
|
-
|
|
411
|
+
try {
|
|
412
|
+
await this.signInput(transaction, input, i, signer, true);
|
|
413
|
+
} catch {
|
|
414
|
+
throw new Error(`Cannot sign input ${i} with the provided signer.`);
|
|
415
|
+
}
|
|
423
416
|
}
|
|
424
417
|
}
|
|
425
418
|
|
|
@@ -443,6 +436,12 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
443
436
|
* @returns {Promise<void>}
|
|
444
437
|
*/
|
|
445
438
|
protected async signInputs(transaction: Psbt): Promise<void> {
|
|
439
|
+
if ('multiSignPsbt' in this.signer) {
|
|
440
|
+
await this.signInputsWalletBased(transaction);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// non web based signing.
|
|
446
445
|
const txs: PsbtInput[] = transaction.data.inputs;
|
|
447
446
|
|
|
448
447
|
const batchSize: number = 20;
|
|
@@ -467,15 +466,11 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
467
466
|
await Promise.all(promises);
|
|
468
467
|
}
|
|
469
468
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
try {
|
|
473
|
-
transaction.finalizeAllInputs();
|
|
474
|
-
|
|
475
|
-
this.finalized = true;
|
|
476
|
-
} catch (e) {
|
|
477
|
-
this.finalized = false;
|
|
469
|
+
for (let i = 0; i < transaction.data.inputs.length; i++) {
|
|
470
|
+
transaction.finalizeInput(i, this.customFinalizerP2SH);
|
|
478
471
|
}
|
|
472
|
+
|
|
473
|
+
this.finalized = true;
|
|
479
474
|
}
|
|
480
475
|
|
|
481
476
|
/**
|
|
@@ -706,6 +701,48 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
706
701
|
return getFinalScripts(inputIndex, input, scriptA, isSegwit, isP2SH, isP2WSH);
|
|
707
702
|
};
|
|
708
703
|
|
|
704
|
+
protected async signInputsWalletBased(transaction: Psbt): Promise<void> {
|
|
705
|
+
const signer: UnisatSigner = this.signer as UnisatSigner;
|
|
706
|
+
|
|
707
|
+
// then, we sign all the remaining inputs with the wallet signer.
|
|
708
|
+
await signer.multiSignPsbt([transaction]);
|
|
709
|
+
|
|
710
|
+
// Then, we finalize every input.
|
|
711
|
+
for (let i = 0; i < transaction.data.inputs.length; i++) {
|
|
712
|
+
transaction.finalizeInput(i, this.customFinalizerP2SH);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
this.finalized = true;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
private async attemptSignTaproot(
|
|
719
|
+
transaction: Psbt,
|
|
720
|
+
input: PsbtInput,
|
|
721
|
+
i: number,
|
|
722
|
+
signer: Signer | ECPairInterface,
|
|
723
|
+
publicKey: Buffer,
|
|
724
|
+
): Promise<void> {
|
|
725
|
+
const isScriptSpend = this.isTaprootScriptSpend(input, publicKey);
|
|
726
|
+
|
|
727
|
+
if (isScriptSpend) {
|
|
728
|
+
await this.signTaprootInput(signer, transaction, i);
|
|
729
|
+
} else {
|
|
730
|
+
let tweakedSigner: ECPairInterface | undefined;
|
|
731
|
+
if (signer !== this.signer) {
|
|
732
|
+
tweakedSigner = this.getTweakedSigner(true, signer);
|
|
733
|
+
} else {
|
|
734
|
+
if (!this.tweakedSigner) this.tweakSigner();
|
|
735
|
+
tweakedSigner = this.tweakedSigner;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
if (tweakedSigner) {
|
|
739
|
+
await this.signTaprootInput(tweakedSigner, transaction, i);
|
|
740
|
+
} else {
|
|
741
|
+
this.error(`Failed to obtain tweaked signer for input ${i}.`);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
709
746
|
private isTaprootScriptSpend(input: PsbtInput, publicKey: Buffer): boolean {
|
|
710
747
|
if (input.tapLeafScript && input.tapLeafScript.length > 0) {
|
|
711
748
|
// Check if the signer's public key is involved in any tapLeafScript
|
|
@@ -788,13 +825,17 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
788
825
|
tapLeafHash?: Buffer,
|
|
789
826
|
): Promise<void> {
|
|
790
827
|
if ('signTaprootInput' in signer) {
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
828
|
+
try {
|
|
829
|
+
await (
|
|
830
|
+
signer.signTaprootInput as (
|
|
831
|
+
tx: Psbt,
|
|
832
|
+
i: number,
|
|
833
|
+
tapLeafHash?: Buffer,
|
|
834
|
+
) => Promise<void>
|
|
835
|
+
)(transaction, i, tapLeafHash);
|
|
836
|
+
} catch {
|
|
837
|
+
throw new Error('Failed to sign Taproot input with provided signer.');
|
|
838
|
+
}
|
|
798
839
|
} else {
|
|
799
840
|
transaction.signTaprootInput(i, signer); //tapLeafHash
|
|
800
841
|
}
|