@btc-vision/transaction 1.6.4 → 1.6.5

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 (60) hide show
  1. package/browser/_version.d.ts +1 -1
  2. package/browser/generators/builders/P2WDAGenerator.d.ts +13 -0
  3. package/browser/index.js +1 -1
  4. package/browser/keypair/Address.d.ts +3 -2
  5. package/browser/keypair/AddressVerificator.d.ts +12 -1
  6. package/browser/keypair/Wallet.d.ts +3 -0
  7. package/browser/opnet.d.ts +4 -0
  8. package/browser/p2wda/P2WDADetector.d.ts +16 -0
  9. package/browser/transaction/TransactionFactory.d.ts +3 -1
  10. package/browser/transaction/builders/DeploymentTransaction.d.ts +3 -3
  11. package/browser/transaction/builders/InteractionTransactionP2WDA.d.ts +37 -0
  12. package/browser/transaction/builders/SharedInteractionTransaction.d.ts +4 -4
  13. package/browser/transaction/mineable/IP2WSHAddress.d.ts +4 -0
  14. package/browser/transaction/mineable/TimelockGenerator.d.ts +2 -5
  15. package/browser/transaction/shared/TweakedTransaction.d.ts +5 -0
  16. package/build/_version.d.ts +1 -1
  17. package/build/_version.js +1 -1
  18. package/build/generators/builders/P2WDAGenerator.d.ts +13 -0
  19. package/build/generators/builders/P2WDAGenerator.js +62 -0
  20. package/build/keypair/Address.d.ts +3 -2
  21. package/build/keypair/Address.js +28 -2
  22. package/build/keypair/AddressVerificator.d.ts +12 -1
  23. package/build/keypair/AddressVerificator.js +78 -1
  24. package/build/keypair/Wallet.d.ts +3 -0
  25. package/build/keypair/Wallet.js +4 -0
  26. package/build/opnet.d.ts +4 -0
  27. package/build/opnet.js +4 -0
  28. package/build/p2wda/P2WDADetector.d.ts +16 -0
  29. package/build/p2wda/P2WDADetector.js +97 -0
  30. package/build/transaction/TransactionFactory.d.ts +3 -1
  31. package/build/transaction/TransactionFactory.js +35 -4
  32. package/build/transaction/builders/DeploymentTransaction.d.ts +3 -3
  33. package/build/transaction/builders/DeploymentTransaction.js +1 -1
  34. package/build/transaction/builders/InteractionTransactionP2WDA.d.ts +37 -0
  35. package/build/transaction/builders/InteractionTransactionP2WDA.js +205 -0
  36. package/build/transaction/builders/SharedInteractionTransaction.d.ts +4 -4
  37. package/build/transaction/builders/SharedInteractionTransaction.js +3 -3
  38. package/build/transaction/mineable/IP2WSHAddress.d.ts +4 -0
  39. package/build/transaction/mineable/IP2WSHAddress.js +1 -0
  40. package/build/transaction/mineable/TimelockGenerator.d.ts +2 -5
  41. package/build/transaction/shared/TweakedTransaction.d.ts +5 -0
  42. package/build/transaction/shared/TweakedTransaction.js +19 -0
  43. package/doc/README.md +0 -0
  44. package/doc/addresses/P2OP.md +1 -0
  45. package/doc/addresses/P2WDA.md +240 -0
  46. package/package.json +1 -1
  47. package/src/_version.ts +1 -1
  48. package/src/generators/builders/P2WDAGenerator.ts +174 -0
  49. package/src/keypair/Address.ts +58 -3
  50. package/src/keypair/AddressVerificator.ts +140 -1
  51. package/src/keypair/Wallet.ts +16 -0
  52. package/src/opnet.ts +4 -0
  53. package/src/p2wda/P2WDADetector.ts +218 -0
  54. package/src/transaction/TransactionFactory.ts +79 -5
  55. package/src/transaction/builders/DeploymentTransaction.ts +4 -3
  56. package/src/transaction/builders/InteractionTransactionP2WDA.ts +376 -0
  57. package/src/transaction/builders/SharedInteractionTransaction.ts +7 -6
  58. package/src/transaction/mineable/IP2WSHAddress.ts +4 -0
  59. package/src/transaction/mineable/TimelockGenerator.ts +2 -6
  60. package/src/transaction/shared/TweakedTransaction.ts +36 -0
@@ -0,0 +1,16 @@
1
+ import { Buffer } from 'buffer';
2
+ import { Network } from '@btc-vision/bitcoin';
3
+ import { UTXO } from '../utxo/interfaces/IUTXO.js';
4
+ import { IP2WSHAddress } from '../transaction/mineable/IP2WSHAddress.js';
5
+ export declare class P2WDADetector {
6
+ static isP2WDAUTXO(utxo: UTXO): boolean;
7
+ static isP2WDAWitnessScript(witnessScript: Buffer): boolean;
8
+ static generateP2WDAAddress(publicKey: Buffer, network: Network): IP2WSHAddress & {
9
+ scriptPubKey: Buffer;
10
+ };
11
+ static extractPublicKeyFromP2WDA(witnessScript: Buffer): Buffer | null;
12
+ static createSimpleP2WDAWitness(transactionSignature: Buffer, witnessScript: Buffer): Buffer[];
13
+ static validateP2WDASignature(publicKey: Buffer, dataSignature: Buffer, operationData: Buffer): boolean;
14
+ static estimateP2WDAWitnessSize(dataSize?: number): number;
15
+ static couldBeP2WDA(scriptPubKey: Buffer): boolean;
16
+ }
@@ -0,0 +1,97 @@
1
+ import { Buffer } from 'buffer';
2
+ import { opcodes, payments, script } from '@btc-vision/bitcoin';
3
+ export class P2WDADetector {
4
+ static isP2WDAUTXO(utxo) {
5
+ if (!utxo.witnessScript) {
6
+ return false;
7
+ }
8
+ const witnessScript = Buffer.isBuffer(utxo.witnessScript)
9
+ ? utxo.witnessScript
10
+ : Buffer.from(utxo.witnessScript, 'hex');
11
+ return this.isP2WDAWitnessScript(witnessScript);
12
+ }
13
+ static isP2WDAWitnessScript(witnessScript) {
14
+ try {
15
+ const decompiled = script.decompile(witnessScript);
16
+ if (!decompiled || decompiled.length !== 7) {
17
+ return false;
18
+ }
19
+ for (let i = 0; i < 5; i++) {
20
+ if (decompiled[i] !== opcodes.OP_2DROP) {
21
+ return false;
22
+ }
23
+ }
24
+ return (Buffer.isBuffer(decompiled[5]) &&
25
+ decompiled[5].length === 33 &&
26
+ decompiled[6] === opcodes.OP_CHECKSIG);
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ }
32
+ static generateP2WDAAddress(publicKey, network) {
33
+ if (publicKey.length !== 33) {
34
+ throw new Error('Public key must be 33 bytes (compressed)');
35
+ }
36
+ const witnessScript = script.compile([
37
+ opcodes.OP_2DROP,
38
+ opcodes.OP_2DROP,
39
+ opcodes.OP_2DROP,
40
+ opcodes.OP_2DROP,
41
+ opcodes.OP_2DROP,
42
+ publicKey,
43
+ opcodes.OP_CHECKSIG,
44
+ ]);
45
+ const p2wsh = payments.p2wsh({
46
+ redeem: { output: witnessScript },
47
+ network,
48
+ });
49
+ if (!p2wsh.address || !p2wsh.output) {
50
+ throw new Error('Failed to generate P2WDA address');
51
+ }
52
+ return {
53
+ address: p2wsh.address,
54
+ witnessScript,
55
+ scriptPubKey: p2wsh.output,
56
+ };
57
+ }
58
+ static extractPublicKeyFromP2WDA(witnessScript) {
59
+ try {
60
+ const decompiled = script.decompile(witnessScript);
61
+ if (!decompiled || decompiled.length !== 7) {
62
+ return null;
63
+ }
64
+ for (let i = 0; i < 5; i++) {
65
+ if (decompiled[i] !== opcodes.OP_2DROP) {
66
+ return null;
67
+ }
68
+ }
69
+ if (Buffer.isBuffer(decompiled[5]) &&
70
+ decompiled[5].length === 33 &&
71
+ decompiled[6] === opcodes.OP_CHECKSIG) {
72
+ return decompiled[5];
73
+ }
74
+ return null;
75
+ }
76
+ catch {
77
+ return null;
78
+ }
79
+ }
80
+ static createSimpleP2WDAWitness(transactionSignature, witnessScript) {
81
+ const witnessStack = [transactionSignature];
82
+ for (let i = 0; i < 10; i++) {
83
+ witnessStack.push(Buffer.alloc(0));
84
+ }
85
+ witnessStack.push(witnessScript);
86
+ return witnessStack;
87
+ }
88
+ static validateP2WDASignature(publicKey, dataSignature, operationData) {
89
+ return dataSignature.length === 64;
90
+ }
91
+ static estimateP2WDAWitnessSize(dataSize = 0) {
92
+ return 72 + dataSize + 39 + 12;
93
+ }
94
+ static couldBeP2WDA(scriptPubKey) {
95
+ return scriptPubKey.length === 34 && scriptPubKey[0] === 0x00 && scriptPubKey[1] === 0x20;
96
+ }
97
+ }
@@ -26,7 +26,7 @@ export interface BitcoinTransferBase {
26
26
  readonly nextUTXOs: UTXO[];
27
27
  }
28
28
  export interface InteractionResponse {
29
- readonly fundingTransaction: string;
29
+ readonly fundingTransaction: string | null;
30
30
  readonly interactionTransaction: string;
31
31
  readonly estimatedFees: bigint;
32
32
  readonly nextUTXOs: UTXO[];
@@ -45,7 +45,9 @@ export declare class TransactionFactory {
45
45
  private detectInteractionOPWallet;
46
46
  private detectDeploymentOPWallet;
47
47
  private createFundTransaction;
48
+ private hasP2WDAInputs;
48
49
  private writePSBTHeader;
50
+ private signP2WDAInteraction;
49
51
  private getPriorityFee;
50
52
  private getUTXOAsTransaction;
51
53
  }
@@ -4,6 +4,8 @@ import { DeploymentTransaction } from './builders/DeploymentTransaction.js';
4
4
  import { FundingTransaction } from './builders/FundingTransaction.js';
5
5
  import { InteractionTransaction } from './builders/InteractionTransaction.js';
6
6
  import { TransactionBuilder } from './builders/TransactionBuilder.js';
7
+ import { P2WDADetector } from '../p2wda/P2WDADetector.js';
8
+ import { InteractionTransactionP2WDA } from './builders/InteractionTransactionP2WDA.js';
7
9
  export class TransactionFactory {
8
10
  async createCustomScriptTransaction(interactionParameters) {
9
11
  if (!interactionParameters.to) {
@@ -84,6 +86,10 @@ export class TransactionFactory {
84
86
  if (!('signer' in interactionParameters)) {
85
87
  throw new Error('Field "signer" not provided, OP_WALLET not detected.');
86
88
  }
89
+ const useP2WDA = this.hasP2WDAInputs(interactionParameters.utxos);
90
+ if (useP2WDA) {
91
+ return this.signP2WDAInteraction(interactionParameters);
92
+ }
87
93
  const inputs = this.parseOptionalInputs(interactionParameters.optionalInputs);
88
94
  const preTransaction = new InteractionTransaction({
89
95
  ...interactionParameters,
@@ -121,7 +127,7 @@ export class TransactionFactory {
121
127
  ...this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0),
122
128
  ],
123
129
  randomBytes: preTransaction.getRndBytes(),
124
- challenge: preTransaction.getPreimage(),
130
+ challenge: preTransaction.getChallenge(),
125
131
  nonWitnessUtxo: signedTransaction.tx.toBuffer(),
126
132
  estimatedFees: preTransaction.estimatedFees,
127
133
  optionalInputs: inputs,
@@ -133,7 +139,7 @@ export class TransactionFactory {
133
139
  interactionTransaction: outTx.toHex(),
134
140
  estimatedFees: preTransaction.estimatedFees,
135
141
  nextUTXOs: this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.from, 1),
136
- challenge: preTransaction.getPreimage().toRaw(),
142
+ challenge: preTransaction.getChallenge().toRaw(),
137
143
  };
138
144
  }
139
145
  async signDeployment(deploymentParameters) {
@@ -189,7 +195,7 @@ export class TransactionFactory {
189
195
  ...deploymentParameters,
190
196
  utxos: [newUtxo],
191
197
  randomBytes: preTransaction.getRndBytes(),
192
- challenge: preTransaction.getPreimage(),
198
+ challenge: preTransaction.getChallenge(),
193
199
  nonWitnessUtxo: signedTransaction.toBuffer(),
194
200
  estimatedFees: preTransaction.estimatedFees,
195
201
  optionalInputs: inputs,
@@ -211,7 +217,7 @@ export class TransactionFactory {
211
217
  contractAddress: finalTransaction.getContractAddress(),
212
218
  contractPubKey: finalTransaction.contractPubKey,
213
219
  utxos: [refundUTXO],
214
- challenge: preTransaction.getPreimage().toRaw(),
220
+ challenge: preTransaction.getChallenge().toRaw(),
215
221
  };
216
222
  }
217
223
  async createBTCTransfer(parameters) {
@@ -307,6 +313,9 @@ export class TransactionFactory {
307
313
  nextUTXOs: this.getUTXOAsTransaction(signedTransaction, parameters.to, 0),
308
314
  };
309
315
  }
316
+ hasP2WDAInputs(utxos) {
317
+ return utxos.some((utxo) => P2WDADetector.isP2WDAUTXO(utxo));
318
+ }
310
319
  writePSBTHeader(type, psbt) {
311
320
  const buf = Buffer.from(psbt, 'base64');
312
321
  const header = Buffer.alloc(2);
@@ -314,6 +323,28 @@ export class TransactionFactory {
314
323
  header.writeUInt8(currentConsensus, 1);
315
324
  return Buffer.concat([header, buf]).toString('hex');
316
325
  }
326
+ async signP2WDAInteraction(interactionParameters) {
327
+ if (!interactionParameters.from) {
328
+ throw new Error('Field "from" not provided.');
329
+ }
330
+ if (!('signer' in interactionParameters)) {
331
+ throw new Error('P2WDA interactions require a signer. OP_WALLET is not supported for P2WDA.');
332
+ }
333
+ const inputs = this.parseOptionalInputs(interactionParameters.optionalInputs);
334
+ const p2wdaTransaction = new InteractionTransactionP2WDA({
335
+ ...interactionParameters,
336
+ optionalInputs: inputs,
337
+ });
338
+ const signedTx = await p2wdaTransaction.signTransaction();
339
+ const txHex = signedTx.toHex();
340
+ return {
341
+ fundingTransaction: null,
342
+ interactionTransaction: txHex,
343
+ estimatedFees: p2wdaTransaction.estimatedFees,
344
+ nextUTXOs: this.getUTXOAsTransaction(signedTx, interactionParameters.from, signedTx.outs.length - 1),
345
+ challenge: interactionParameters.challenge.toRaw(),
346
+ };
347
+ }
317
348
  getPriorityFee(params) {
318
349
  const totalFee = params.priorityFee + params.gasSatFee;
319
350
  if (totalFee < TransactionBuilder.MINIMUM_DUST) {
@@ -4,13 +4,13 @@ import { P2TRPayment, Psbt } from '@btc-vision/bitcoin';
4
4
  import { TransactionBuilder } from './TransactionBuilder.js';
5
5
  import { TapLeafScript } from '../interfaces/Tap.js';
6
6
  import { Address } from '../../keypair/Address.js';
7
- import { ITimeLockOutput } from '../mineable/TimelockGenerator.js';
8
7
  import { ChallengeSolution } from '../../epoch/ChallengeSolution.js';
8
+ import { IP2WSHAddress } from '../mineable/IP2WSHAddress.js';
9
9
  export declare class DeploymentTransaction extends TransactionBuilder<TransactionType.DEPLOYMENT> {
10
10
  static readonly MAXIMUM_CONTRACT_SIZE: number;
11
11
  type: TransactionType.DEPLOYMENT;
12
12
  protected readonly challenge: ChallengeSolution;
13
- protected readonly epochChallenge: ITimeLockOutput;
13
+ protected readonly epochChallenge: IP2WSHAddress;
14
14
  protected readonly _contractAddress: Address;
15
15
  protected tapLeafScript: TapLeafScript | null;
16
16
  private readonly deploymentVersion;
@@ -31,7 +31,7 @@ export declare class DeploymentTransaction extends TransactionBuilder<Transactio
31
31
  get contractAddress(): Address;
32
32
  get p2trAddress(): string;
33
33
  getRndBytes(): Buffer;
34
- getPreimage(): ChallengeSolution;
34
+ getChallenge(): ChallengeSolution;
35
35
  getContractAddress(): string;
36
36
  protected contractSignerXOnlyPubKey(): Buffer;
37
37
  protected buildTransaction(): Promise<void>;
@@ -68,7 +68,7 @@ export class DeploymentTransaction extends TransactionBuilder {
68
68
  getRndBytes() {
69
69
  return this.randomBytes;
70
70
  }
71
- getPreimage() {
71
+ getChallenge() {
72
72
  return this.challenge;
73
73
  }
74
74
  getContractAddress() {
@@ -0,0 +1,37 @@
1
+ import { Buffer } from 'buffer';
2
+ import { Psbt } from '@btc-vision/bitcoin';
3
+ import { TransactionType } from '../enums/TransactionType.js';
4
+ import { IInteractionParameters } from '../interfaces/ITransactionParameters.js';
5
+ import { TransactionBuilder } from './TransactionBuilder.js';
6
+ import { ChallengeSolution } from '../../epoch/ChallengeSolution.js';
7
+ import { IP2WSHAddress } from '../mineable/IP2WSHAddress.js';
8
+ export declare class InteractionTransactionP2WDA extends TransactionBuilder<TransactionType.INTERACTION> {
9
+ private static readonly MAX_WITNESS_FIELDS;
10
+ private static readonly MAX_BYTES_PER_WITNESS;
11
+ readonly type: TransactionType.INTERACTION;
12
+ protected readonly epochChallenge: IP2WSHAddress;
13
+ protected readonly disableAutoRefund: boolean;
14
+ private readonly contractAddress;
15
+ private readonly contractSecret;
16
+ private readonly calldata;
17
+ private readonly challenge;
18
+ private readonly randomBytes;
19
+ private p2wdaGenerator;
20
+ private scriptSigner;
21
+ private p2wdaInputIndices;
22
+ private readonly compiledOperationData;
23
+ constructor(parameters: IInteractionParameters);
24
+ getRndBytes(): Buffer;
25
+ getChallenge(): ChallengeSolution;
26
+ getContractSecret(): Buffer;
27
+ protected buildTransaction(): Promise<void>;
28
+ protected createMineableRewardOutputs(): Promise<void>;
29
+ protected signInputs(transaction: Psbt): Promise<void>;
30
+ private generateFeatures;
31
+ private generateKeyPairFromSeed;
32
+ private scriptSignerXOnlyPubKey;
33
+ private validateP2WDAInputs;
34
+ private validateOperationDataSize;
35
+ private finalizePrimaryP2WDA;
36
+ private splitIntoWitnessChunks;
37
+ }
@@ -0,0 +1,205 @@
1
+ import { Buffer } from 'buffer';
2
+ import { toXOnly } from '@btc-vision/bitcoin';
3
+ import { TransactionType } from '../enums/TransactionType.js';
4
+ import { MINIMUM_AMOUNT_CA, MINIMUM_AMOUNT_REWARD, TransactionBuilder, } from './TransactionBuilder.js';
5
+ import { MessageSigner } from '../../keypair/MessageSigner.js';
6
+ import { Compressor } from '../../bytecode/Compressor.js';
7
+ import { P2WDAGenerator } from '../../generators/builders/P2WDAGenerator.js';
8
+ import { Features } from '../../generators/Features.js';
9
+ import { BitcoinUtils } from '../../utils/BitcoinUtils.js';
10
+ import { EcKeyPair } from '../../keypair/EcKeyPair.js';
11
+ import { P2WDADetector } from '../../p2wda/P2WDADetector.js';
12
+ import { TimeLockGenerator } from '../mineable/TimelockGenerator.js';
13
+ export class InteractionTransactionP2WDA extends TransactionBuilder {
14
+ constructor(parameters) {
15
+ super(parameters);
16
+ this.type = TransactionType.INTERACTION;
17
+ this.p2wdaInputIndices = new Set();
18
+ this.compiledOperationData = null;
19
+ if (!parameters.to) {
20
+ throw new Error('Contract address (to) is required');
21
+ }
22
+ if (!parameters.contract) {
23
+ throw new Error('Contract secret is required');
24
+ }
25
+ if (!parameters.calldata) {
26
+ throw new Error('Calldata is required');
27
+ }
28
+ if (!parameters.challenge) {
29
+ throw new Error('Challenge solution is required');
30
+ }
31
+ this.disableAutoRefund = parameters.disableAutoRefund || false;
32
+ this.contractAddress = parameters.to;
33
+ this.contractSecret = Buffer.from(parameters.contract.replace('0x', ''), 'hex');
34
+ this.calldata = Compressor.compress(parameters.calldata);
35
+ this.challenge = parameters.challenge;
36
+ this.randomBytes = parameters.randomBytes || BitcoinUtils.rndBytes();
37
+ this.scriptSigner = this.generateKeyPairFromSeed();
38
+ this.p2wdaGenerator = new P2WDAGenerator(Buffer.from(this.signer.publicKey), this.scriptSignerXOnlyPubKey(), this.network);
39
+ if (this.contractSecret.length !== 32) {
40
+ throw new Error('Invalid contract secret length. Expected 32 bytes.');
41
+ }
42
+ this.epochChallenge = TimeLockGenerator.generateTimeLockAddress(this.challenge.publicKey.originalPublicKeyBuffer(), this.network);
43
+ this.validateP2WDAInputs();
44
+ this.compiledOperationData = this.p2wdaGenerator.compile(this.calldata, this.contractSecret, this.challenge, this.priorityFee, this.generateFeatures(parameters));
45
+ this.validateOperationDataSize();
46
+ this.internalInit();
47
+ }
48
+ getRndBytes() {
49
+ return this.randomBytes;
50
+ }
51
+ getChallenge() {
52
+ return this.challenge;
53
+ }
54
+ getContractSecret() {
55
+ return this.contractSecret;
56
+ }
57
+ async buildTransaction() {
58
+ if (!this.regenerated) {
59
+ this.addInputsFromUTXO();
60
+ }
61
+ await this.createMineableRewardOutputs();
62
+ }
63
+ async createMineableRewardOutputs() {
64
+ if (!this.to)
65
+ throw new Error('To address is required');
66
+ const amountSpent = this.getTransactionOPNetFee();
67
+ let amountToCA;
68
+ if (amountSpent > MINIMUM_AMOUNT_REWARD + MINIMUM_AMOUNT_CA) {
69
+ amountToCA = MINIMUM_AMOUNT_CA;
70
+ }
71
+ else {
72
+ amountToCA = amountSpent;
73
+ }
74
+ this.addOutput({
75
+ value: Number(amountToCA),
76
+ address: this.to,
77
+ });
78
+ if (amountToCA === MINIMUM_AMOUNT_CA &&
79
+ amountSpent - MINIMUM_AMOUNT_CA > MINIMUM_AMOUNT_REWARD) {
80
+ this.addOutput({
81
+ value: Number(amountSpent - amountToCA),
82
+ address: this.epochChallenge.address,
83
+ });
84
+ }
85
+ const amount = this.addOptionalOutputsAndGetAmount();
86
+ if (!this.disableAutoRefund) {
87
+ await this.addRefundOutput(amountSpent + amount);
88
+ }
89
+ }
90
+ async signInputs(transaction) {
91
+ for (let i = 0; i < transaction.data.inputs.length; i++) {
92
+ await this.signInput(transaction, transaction.data.inputs[i], i, this.signer);
93
+ }
94
+ for (let i = 0; i < transaction.data.inputs.length; i++) {
95
+ if (this.p2wdaInputIndices.has(i)) {
96
+ if (i === 0) {
97
+ transaction.finalizeInput(i, this.finalizePrimaryP2WDA.bind(this));
98
+ }
99
+ else {
100
+ transaction.finalizeInput(i, this.finalizeSecondaryP2WDA.bind(this));
101
+ }
102
+ }
103
+ else {
104
+ transaction.finalizeInput(i, this.customFinalizerP2SH.bind(this));
105
+ }
106
+ }
107
+ this.finalized = true;
108
+ }
109
+ generateFeatures(parameters) {
110
+ const features = [];
111
+ if (parameters.loadedStorage) {
112
+ features.push({
113
+ opcode: Features.ACCESS_LIST,
114
+ data: parameters.loadedStorage,
115
+ });
116
+ }
117
+ const submission = parameters.challenge.getSubmission();
118
+ if (submission) {
119
+ features.push({
120
+ opcode: Features.EPOCH_SUBMISSION,
121
+ data: submission,
122
+ });
123
+ }
124
+ return features;
125
+ }
126
+ generateKeyPairFromSeed() {
127
+ return EcKeyPair.fromSeedKeyPair(this.randomBytes, this.network);
128
+ }
129
+ scriptSignerXOnlyPubKey() {
130
+ return toXOnly(Buffer.from(this.scriptSigner.publicKey));
131
+ }
132
+ validateP2WDAInputs() {
133
+ if (this.utxos.length === 0 || !P2WDADetector.isP2WDAUTXO(this.utxos[0])) {
134
+ throw new Error('Input 0 must be a P2WDA UTXO');
135
+ }
136
+ for (let i = 0; i < this.utxos.length; i++) {
137
+ if (P2WDADetector.isP2WDAUTXO(this.utxos[i])) {
138
+ this.p2wdaInputIndices.add(i);
139
+ }
140
+ }
141
+ for (let i = 0; i < this.optionalInputs.length; i++) {
142
+ const actualIndex = this.utxos.length + i;
143
+ if (P2WDADetector.isP2WDAUTXO(this.optionalInputs[i])) {
144
+ this.p2wdaInputIndices.add(actualIndex);
145
+ }
146
+ }
147
+ }
148
+ validateOperationDataSize() {
149
+ if (!this.compiledOperationData) {
150
+ throw new Error('Operation data not compiled');
151
+ }
152
+ const estimatedSize = this.compiledOperationData.length;
153
+ if (!P2WDAGenerator.validateWitnessSize(estimatedSize)) {
154
+ const signatureSize = 64;
155
+ const totalSize = estimatedSize + signatureSize;
156
+ const compressedEstimate = Math.ceil(totalSize * 0.7);
157
+ const requiredFields = Math.ceil(compressedEstimate / InteractionTransactionP2WDA.MAX_BYTES_PER_WITNESS);
158
+ throw new Error(`Please dont use P2WDA for this operation. Data too large. Raw size: ${estimatedSize} bytes, ` +
159
+ `estimated compressed: ${compressedEstimate} bytes, ` +
160
+ `needs ${requiredFields} witness fields, max is ${InteractionTransactionP2WDA.MAX_WITNESS_FIELDS}`);
161
+ }
162
+ }
163
+ finalizePrimaryP2WDA(inputIndex, input) {
164
+ if (!input.partialSig || input.partialSig.length === 0) {
165
+ throw new Error(`No signature for P2WDA input #${inputIndex}`);
166
+ }
167
+ if (!input.witnessScript) {
168
+ throw new Error(`No witness script for P2WDA input #${inputIndex}`);
169
+ }
170
+ if (!this.compiledOperationData) {
171
+ throw new Error('Operation data not compiled');
172
+ }
173
+ const txSignature = input.partialSig[0].signature;
174
+ const messageToSign = Buffer.concat([txSignature, this.compiledOperationData]);
175
+ const signedMessage = MessageSigner.signMessage(this.signer, messageToSign);
176
+ const schnorrSignature = Buffer.from(signedMessage.signature);
177
+ const fullData = Buffer.concat([schnorrSignature, this.compiledOperationData]);
178
+ const compressedData = Compressor.compress(fullData);
179
+ const chunks = this.splitIntoWitnessChunks(compressedData);
180
+ if (chunks.length > InteractionTransactionP2WDA.MAX_WITNESS_FIELDS) {
181
+ throw new Error(`Compressed data needs ${chunks.length} witness fields, max is ${InteractionTransactionP2WDA.MAX_WITNESS_FIELDS}`);
182
+ }
183
+ const witnessStack = [txSignature];
184
+ for (let i = 0; i < InteractionTransactionP2WDA.MAX_WITNESS_FIELDS; i++) {
185
+ witnessStack.push(i < chunks.length ? chunks[i] : Buffer.alloc(0));
186
+ }
187
+ witnessStack.push(input.witnessScript);
188
+ return {
189
+ finalScriptSig: undefined,
190
+ finalScriptWitness: TransactionBuilder.witnessStackToScriptWitness(witnessStack),
191
+ };
192
+ }
193
+ splitIntoWitnessChunks(data) {
194
+ const chunks = [];
195
+ let offset = 0;
196
+ while (offset < data.length) {
197
+ const size = Math.min(InteractionTransactionP2WDA.MAX_BYTES_PER_WITNESS, data.length - offset);
198
+ chunks.push(data.subarray(offset, offset + size));
199
+ offset += size;
200
+ }
201
+ return chunks;
202
+ }
203
+ }
204
+ InteractionTransactionP2WDA.MAX_WITNESS_FIELDS = 10;
205
+ InteractionTransactionP2WDA.MAX_BYTES_PER_WITNESS = 80;
@@ -4,8 +4,8 @@ import { TransactionBuilder } from './TransactionBuilder.js';
4
4
  import { TransactionType } from '../enums/TransactionType.js';
5
5
  import { CalldataGenerator } from '../../generators/builders/CalldataGenerator.js';
6
6
  import { SharedInteractionParameters } from '../interfaces/ITransactionParameters.js';
7
- import { ITimeLockOutput } from '../mineable/TimelockGenerator.js';
8
7
  import { ChallengeSolution } from '../../epoch/ChallengeSolution.js';
8
+ import { IP2WSHAddress } from '../mineable/IP2WSHAddress.js';
9
9
  export declare abstract class SharedInteractionTransaction<T extends TransactionType> extends TransactionBuilder<T> {
10
10
  static readonly MAXIMUM_CALLDATA_SIZE: number;
11
11
  readonly randomBytes: Buffer;
@@ -14,7 +14,7 @@ export declare abstract class SharedInteractionTransaction<T extends Transaction
14
14
  protected abstract readonly compiledTargetScript: Buffer;
15
15
  protected abstract readonly scriptTree: Taptree;
16
16
  protected readonly challenge: ChallengeSolution;
17
- protected readonly epochChallenge: ITimeLockOutput;
17
+ protected readonly epochChallenge: IP2WSHAddress;
18
18
  protected calldataGenerator: CalldataGenerator;
19
19
  protected readonly calldata: Buffer;
20
20
  protected abstract readonly contractSecret: Buffer;
@@ -23,7 +23,7 @@ export declare abstract class SharedInteractionTransaction<T extends Transaction
23
23
  protected constructor(parameters: SharedInteractionParameters);
24
24
  getContractSecret(): Buffer;
25
25
  getRndBytes(): Buffer;
26
- getPreimage(): ChallengeSolution;
26
+ getChallenge(): ChallengeSolution;
27
27
  protected scriptSignerXOnlyPubKey(): Buffer;
28
28
  protected generateKeyPairFromSeed(): ECPairInterface;
29
29
  protected buildTransaction(): Promise<void>;
@@ -37,7 +37,7 @@ export declare abstract class SharedInteractionTransaction<T extends Transaction
37
37
  };
38
38
  protected signInputsWalletBased(transaction: Psbt): Promise<void>;
39
39
  protected signInputsNonWalletBased(transaction: Psbt): Promise<void>;
40
- private createMineableRewardOutputs;
40
+ protected createMineableRewardOutputs(): Promise<void>;
41
41
  private getPubKeys;
42
42
  private generateRedeemScripts;
43
43
  }
@@ -1,5 +1,5 @@
1
- import { PaymentType, toXOnly } from '@btc-vision/bitcoin';
2
- import { MINIMUM_AMOUNT_CA, MINIMUM_AMOUNT_REWARD, TransactionBuilder } from './TransactionBuilder.js';
1
+ import { PaymentType, toXOnly, } from '@btc-vision/bitcoin';
2
+ import { MINIMUM_AMOUNT_CA, MINIMUM_AMOUNT_REWARD, TransactionBuilder, } from './TransactionBuilder.js';
3
3
  import { CalldataGenerator } from '../../generators/builders/CalldataGenerator.js';
4
4
  import { Compressor } from '../../bytecode/Compressor.js';
5
5
  import { EcKeyPair } from '../../keypair/EcKeyPair.js';
@@ -45,7 +45,7 @@ export class SharedInteractionTransaction extends TransactionBuilder {
45
45
  getRndBytes() {
46
46
  return this.randomBytes;
47
47
  }
48
- getPreimage() {
48
+ getChallenge() {
49
49
  return this.challenge;
50
50
  }
51
51
  scriptSignerXOnlyPubKey() {
@@ -0,0 +1,4 @@
1
+ export interface IP2WSHAddress {
2
+ address: string;
3
+ witnessScript: Buffer;
4
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,9 +1,6 @@
1
1
  import { Network } from '@btc-vision/bitcoin';
2
- export interface ITimeLockOutput {
3
- address: string;
4
- witnessScript: Buffer;
5
- }
2
+ import { IP2WSHAddress } from './IP2WSHAddress.js';
6
3
  export declare class TimeLockGenerator {
7
4
  private static readonly CSV_BLOCKS;
8
- static generateTimeLockAddress(publicKey: Buffer, network?: Network, csvBlocks?: number): ITimeLockOutput;
5
+ static generateTimeLockAddress(publicKey: Buffer, network?: Network, csvBlocks?: number): IP2WSHAddress;
9
6
  }
@@ -5,6 +5,7 @@ import { UTXO } from '../../utxo/interfaces/IUTXO.js';
5
5
  import { TapLeafScript } from '../interfaces/Tap.js';
6
6
  import { ChainId } from '../../network/ChainId.js';
7
7
  import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
8
+ import { Buffer } from 'buffer';
8
9
  export type SupportedTransactionVersion = 1 | 2 | 3;
9
10
  export interface ITweakedTransactionData {
10
11
  readonly signer: Signer | ECPairInterface | UnisatSigner;
@@ -88,6 +89,10 @@ export declare abstract class TweakedTransaction extends Logger {
88
89
  finalScriptSig: Buffer | undefined;
89
90
  finalScriptWitness: Buffer | undefined;
90
91
  };
92
+ protected finalizeSecondaryP2WDA(inputIndex: number, input: PsbtInput): {
93
+ finalScriptWitness: Buffer | undefined;
94
+ finalScriptSig: Buffer | undefined;
95
+ };
91
96
  protected signInputsWalletBased(transaction: Psbt): Promise<void>;
92
97
  protected isCSVScript(decompiled: (number | Buffer)[]): boolean;
93
98
  protected setCSVSequence(csvBlocks: number, currentSequence: number): number;
@@ -3,6 +3,8 @@ import { address as bitAddress, crypto as bitCrypto, getFinalScripts, isP2A, isP
3
3
  import { TweakedSigner } from '../../signer/TweakedSigner.js';
4
4
  import { canSignNonTaprootInput, isTaprootInput, pubkeyInScript, } from '../../signer/SignerUtils.js';
5
5
  import { TransactionBuilder } from '../builders/TransactionBuilder.js';
6
+ import { Buffer } from 'buffer';
7
+ import { P2WDADetector } from '../../p2wda/P2WDADetector.js';
6
8
  export var TransactionSequence;
7
9
  (function (TransactionSequence) {
8
10
  TransactionSequence[TransactionSequence["REPLACE_BY_FEE"] = 4294967293] = "REPLACE_BY_FEE";
@@ -52,6 +54,10 @@ export class TweakedTransaction extends Logger {
52
54
  if (!input.partialSig || input.partialSig.length === 0) {
53
55
  throw new Error(`No signatures for P2WSH input #${inputIndex}`);
54
56
  }
57
+ const isP2WDA = P2WDADetector.isP2WDAWitnessScript(input.witnessScript);
58
+ if (isP2WDA) {
59
+ return this.finalizeSecondaryP2WDA(inputIndex, input);
60
+ }
55
61
  const isCSVInput = this.csvInputIndices.has(inputIndex);
56
62
  if (isCSVInput) {
57
63
  const witnessStack = [input.partialSig[0].signature, input.witnessScript];
@@ -509,6 +515,19 @@ export class TweakedTransaction extends Logger {
509
515
  extractCSVValue(sequence) {
510
516
  return sequence & 0x0000ffff;
511
517
  }
518
+ finalizeSecondaryP2WDA(inputIndex, input) {
519
+ if (!input.partialSig || input.partialSig.length === 0) {
520
+ throw new Error(`No signature for P2WDA input #${inputIndex}`);
521
+ }
522
+ if (!input.witnessScript) {
523
+ throw new Error(`No witness script for P2WDA input #${inputIndex}`);
524
+ }
525
+ const witnessStack = P2WDADetector.createSimpleP2WDAWitness(input.partialSig[0].signature, input.witnessScript);
526
+ return {
527
+ finalScriptSig: undefined,
528
+ finalScriptWitness: TransactionBuilder.witnessStackToScriptWitness(witnessStack),
529
+ };
530
+ }
512
531
  async signInputsWalletBased(transaction) {
513
532
  const signer = this.signer;
514
533
  await signer.multiSignPsbt([transaction]);
package/doc/README.md ADDED
File without changes
@@ -0,0 +1 @@
1
+