@btc-vision/transaction 1.6.0 → 1.6.4

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 (49) hide show
  1. package/browser/_version.d.ts +1 -1
  2. package/browser/epoch/ChallengeSolution.d.ts +3 -3
  3. package/browser/epoch/validator/EpochValidator.d.ts +5 -6
  4. package/browser/generators/builders/DeploymentGenerator.d.ts +1 -1
  5. package/browser/generators/builders/LegacyCalldataGenerator.d.ts +1 -1
  6. package/browser/index.js +1 -1
  7. package/browser/keypair/AddressVerificator.d.ts +2 -1
  8. package/browser/transaction/TransactionFactory.d.ts +2 -2
  9. package/browser/transaction/builders/DeploymentTransaction.d.ts +1 -1
  10. package/browser/transaction/builders/TransactionBuilder.d.ts +3 -0
  11. package/browser/transaction/interfaces/ITransactionParameters.d.ts +1 -0
  12. package/browser/transaction/shared/TweakedTransaction.d.ts +18 -0
  13. package/browser/utxo/OPNetLimitedProvider.d.ts +0 -4
  14. package/build/_version.d.ts +1 -1
  15. package/build/_version.js +1 -1
  16. package/build/epoch/ChallengeSolution.d.ts +3 -3
  17. package/build/epoch/ChallengeSolution.js +4 -4
  18. package/build/epoch/validator/EpochValidator.d.ts +5 -6
  19. package/build/epoch/validator/EpochValidator.js +16 -17
  20. package/build/generators/builders/DeploymentGenerator.d.ts +1 -1
  21. package/build/generators/builders/DeploymentGenerator.js +5 -5
  22. package/build/generators/builders/LegacyCalldataGenerator.d.ts +1 -1
  23. package/build/generators/builders/LegacyCalldataGenerator.js +2 -2
  24. package/build/keypair/AddressVerificator.d.ts +2 -1
  25. package/build/keypair/AddressVerificator.js +4 -0
  26. package/build/transaction/TransactionFactory.d.ts +2 -2
  27. package/build/transaction/TransactionFactory.js +2 -2
  28. package/build/transaction/builders/DeploymentTransaction.d.ts +1 -1
  29. package/build/transaction/builders/DeploymentTransaction.js +4 -4
  30. package/build/transaction/builders/TransactionBuilder.d.ts +3 -0
  31. package/build/transaction/builders/TransactionBuilder.js +18 -3
  32. package/build/transaction/interfaces/ITransactionParameters.d.ts +1 -0
  33. package/build/transaction/shared/TweakedTransaction.d.ts +18 -0
  34. package/build/transaction/shared/TweakedTransaction.js +135 -18
  35. package/build/utxo/OPNetLimitedProvider.d.ts +0 -4
  36. package/build/utxo/OPNetLimitedProvider.js +0 -7
  37. package/package.json +2 -2
  38. package/src/_version.ts +1 -1
  39. package/src/epoch/ChallengeSolution.ts +10 -10
  40. package/src/epoch/validator/EpochValidator.ts +18 -22
  41. package/src/generators/builders/DeploymentGenerator.ts +6 -6
  42. package/src/generators/builders/LegacyCalldataGenerator.ts +3 -3
  43. package/src/keypair/AddressVerificator.ts +7 -1
  44. package/src/transaction/TransactionFactory.ts +4 -4
  45. package/src/transaction/builders/DeploymentTransaction.ts +5 -5
  46. package/src/transaction/builders/TransactionBuilder.ts +30 -3
  47. package/src/transaction/interfaces/ITransactionParameters.ts +1 -0
  48. package/src/transaction/shared/TweakedTransaction.ts +210 -23
  49. package/src/utxo/OPNetLimitedProvider.ts +0 -17
@@ -1,12 +1,19 @@
1
1
  import { Logger } from '@btc-vision/logger';
2
- import { address as bitAddress, crypto as bitCrypto, getFinalScripts, isP2MS, isP2PK, isP2PKH, isP2SHScript, isP2TR, isP2WPKH, isP2WSHScript, isUnknownSegwitVersion, opcodes, payments, PaymentType, script, toXOnly, varuint, } from '@btc-vision/bitcoin';
2
+ import { address as bitAddress, crypto as bitCrypto, getFinalScripts, isP2A, isP2MS, isP2PK, isP2PKH, isP2SHScript, isP2TR, isP2WPKH, isP2WSHScript, isUnknownSegwitVersion, opcodes, payments, PaymentType, script, toXOnly, varuint, } from '@btc-vision/bitcoin';
3
3
  import { TweakedSigner } from '../../signer/TweakedSigner.js';
4
4
  import { canSignNonTaprootInput, isTaprootInput, pubkeyInScript, } from '../../signer/SignerUtils.js';
5
+ import { TransactionBuilder } from '../builders/TransactionBuilder.js';
5
6
  export var TransactionSequence;
6
7
  (function (TransactionSequence) {
7
8
  TransactionSequence[TransactionSequence["REPLACE_BY_FEE"] = 4294967293] = "REPLACE_BY_FEE";
8
9
  TransactionSequence[TransactionSequence["FINAL"] = 4294967295] = "FINAL";
9
10
  })(TransactionSequence || (TransactionSequence = {}));
11
+ export var CSVModes;
12
+ (function (CSVModes) {
13
+ CSVModes[CSVModes["BLOCKS"] = 0] = "BLOCKS";
14
+ CSVModes[CSVModes["TIMESTAMPS"] = 1] = "TIMESTAMPS";
15
+ })(CSVModes || (CSVModes = {}));
16
+ const CSV_ENABLED_BLOCKS_MASK = 0x3fffffff;
10
17
  export class TweakedTransaction extends Logger {
11
18
  constructor(data) {
12
19
  super();
@@ -19,9 +26,12 @@ export class TweakedTransaction extends Logger {
19
26
  this.sequence = TransactionSequence.REPLACE_BY_FEE;
20
27
  this.tapLeafScript = null;
21
28
  this.isBrowser = false;
29
+ this.csvInputIndices = new Set();
30
+ this.anchorInputIndices = new Set();
22
31
  this.regenerated = false;
23
32
  this.ignoreSignatureErrors = false;
24
33
  this.noSignatures = false;
34
+ this.txVersion = 2;
25
35
  this.customFinalizerP2SH = (inputIndex, input, scriptA, isSegwit, isP2SH, isP2WSH) => {
26
36
  const inputDecoded = this.inputs[inputIndex];
27
37
  if (isP2SH && input.partialSig && inputDecoded && inputDecoded.redeemScript) {
@@ -32,6 +42,25 @@ export class TweakedTransaction extends Logger {
32
42
  finalScriptWitness: undefined,
33
43
  };
34
44
  }
45
+ if (this.anchorInputIndices.has(inputIndex)) {
46
+ return {
47
+ finalScriptSig: undefined,
48
+ finalScriptWitness: Buffer.from([0]),
49
+ };
50
+ }
51
+ if (isP2WSH && isSegwit && input.witnessScript) {
52
+ if (!input.partialSig || input.partialSig.length === 0) {
53
+ throw new Error(`No signatures for P2WSH input #${inputIndex}`);
54
+ }
55
+ const isCSVInput = this.csvInputIndices.has(inputIndex);
56
+ if (isCSVInput) {
57
+ const witnessStack = [input.partialSig[0].signature, input.witnessScript];
58
+ return {
59
+ finalScriptSig: undefined,
60
+ finalScriptWitness: TransactionBuilder.witnessStackToScriptWitness(witnessStack),
61
+ };
62
+ }
63
+ }
35
64
  return getFinalScripts(inputIndex, input, scriptA, isSegwit, isP2SH, isP2WSH, true, this.unlockScript);
36
65
  };
37
66
  this.signer = data.signer;
@@ -40,6 +69,9 @@ export class TweakedTransaction extends Logger {
40
69
  this.nonWitnessUtxo = data.nonWitnessUtxo;
41
70
  this.unlockScript = data.unlockScript;
42
71
  this.isBrowser = typeof window !== 'undefined';
72
+ if (data.txVersion) {
73
+ this.txVersion = data.txVersion;
74
+ }
43
75
  }
44
76
  static readScriptWitnessToWitnessStack(Buffer) {
45
77
  let offset = 0;
@@ -120,6 +152,9 @@ export class TweakedTransaction extends Logger {
120
152
  throw new Error('Transaction is already signed');
121
153
  this.sequence = TransactionSequence.FINAL;
122
154
  for (const input of this.inputs) {
155
+ if (this.csvInputIndices.has(this.inputs.indexOf(input))) {
156
+ continue;
157
+ }
123
158
  input.sequence = TransactionSequence.FINAL;
124
159
  }
125
160
  }
@@ -157,6 +192,8 @@ export class TweakedTransaction extends Logger {
157
192
  return this.signer;
158
193
  }
159
194
  async signInput(transaction, input, i, signer, reverse = false, errored = false) {
195
+ if (this.anchorInputIndices.has(i))
196
+ return;
160
197
  const publicKey = signer.publicKey;
161
198
  let isTaproot = isTaprootInput(input);
162
199
  if (reverse) {
@@ -335,17 +372,17 @@ export class TweakedTransaction extends Logger {
335
372
  return;
336
373
  }
337
374
  generatePsbtInputExtended(utxo, i, _extra = false) {
338
- const script = Buffer.from(utxo.scriptPubKey.hex, 'hex');
375
+ const scriptPub = Buffer.from(utxo.scriptPubKey.hex, 'hex');
339
376
  const input = {
340
377
  hash: utxo.transactionId,
341
378
  index: utxo.outputIndex,
342
379
  sequence: this.sequence,
343
380
  witnessUtxo: {
344
381
  value: Number(utxo.value),
345
- script,
382
+ script: scriptPub,
346
383
  },
347
384
  };
348
- if (isP2PKH(script)) {
385
+ if (isP2PKH(scriptPub)) {
349
386
  if (utxo.nonWitnessUtxo) {
350
387
  input.nonWitnessUtxo = Buffer.isBuffer(utxo.nonWitnessUtxo)
351
388
  ? utxo.nonWitnessUtxo
@@ -355,17 +392,12 @@ export class TweakedTransaction extends Logger {
355
392
  throw new Error('Missing nonWitnessUtxo for P2PKH UTXO');
356
393
  }
357
394
  }
358
- else if (isP2WPKH(script) || isUnknownSegwitVersion(script)) {
395
+ else if (isP2WPKH(scriptPub) || isUnknownSegwitVersion(scriptPub)) {
359
396
  }
360
- else if (isP2WSHScript(script)) {
361
- if (!utxo.witnessScript) {
362
- throw new Error('Missing witnessScript for P2WSH UTXO');
363
- }
364
- input.witnessScript = Buffer.isBuffer(utxo.witnessScript)
365
- ? utxo.witnessScript
366
- : Buffer.from(utxo.witnessScript, 'hex');
397
+ else if (isP2WSHScript(scriptPub)) {
398
+ this.processP2WSHInput(utxo, input, i);
367
399
  }
368
- else if (isP2SHScript(script)) {
400
+ else if (isP2SHScript(scriptPub)) {
369
401
  let redeemScriptBuf;
370
402
  if (utxo.redeemScript) {
371
403
  redeemScriptBuf = Buffer.isBuffer(utxo.redeemScript)
@@ -401,15 +433,13 @@ export class TweakedTransaction extends Logger {
401
433
  }
402
434
  else if (isP2WSHScript(redeemOutput)) {
403
435
  delete input.nonWitnessUtxo;
404
- if (!input.witnessScript) {
405
- throw new Error('Missing witnessScript for P2SH-P2WSH UTXO');
406
- }
436
+ this.processP2WSHInput(utxo, input, i);
407
437
  }
408
438
  else {
409
439
  delete input.witnessUtxo;
410
440
  }
411
441
  }
412
- else if (isP2TR(script)) {
442
+ else if (isP2TR(scriptPub)) {
413
443
  if (this.sighashTypes) {
414
444
  const inputSign = TweakedTransaction.calculateSignHash(this.sighashTypes);
415
445
  if (inputSign)
@@ -418,7 +448,11 @@ export class TweakedTransaction extends Logger {
418
448
  this.tweakSigner();
419
449
  input.tapInternalKey = this.internalPubKeyToXOnly();
420
450
  }
421
- else if (isP2PK(script) || isP2MS(script)) {
451
+ else if (isP2A(scriptPub)) {
452
+ this.anchorInputIndices.add(i);
453
+ input.isPayToAnchor = true;
454
+ }
455
+ else if (isP2PK(scriptPub) || isP2MS(scriptPub)) {
422
456
  if (utxo.nonWitnessUtxo) {
423
457
  input.nonWitnessUtxo = Buffer.isBuffer(utxo.nonWitnessUtxo)
424
458
  ? utxo.nonWitnessUtxo
@@ -441,6 +475,40 @@ export class TweakedTransaction extends Logger {
441
475
  }
442
476
  return input;
443
477
  }
478
+ processP2WSHInput(utxo, input, i) {
479
+ if (!utxo.witnessScript) {
480
+ throw new Error('Missing witnessScript for P2WSH UTXO');
481
+ }
482
+ input.witnessScript = Buffer.isBuffer(utxo.witnessScript)
483
+ ? utxo.witnessScript
484
+ : Buffer.from(utxo.witnessScript, 'hex');
485
+ const decompiled = script.decompile(input.witnessScript);
486
+ if (decompiled && this.isCSVScript(decompiled)) {
487
+ const decompiled = script.decompile(input.witnessScript);
488
+ if (decompiled && this.isCSVScript(decompiled)) {
489
+ this.csvInputIndices.add(i);
490
+ const csvBlocks = this.extractCSVBlocks(decompiled);
491
+ console.log('csvBlocks', csvBlocks);
492
+ input.sequence = this.setCSVSequence(csvBlocks, this.sequence);
493
+ }
494
+ }
495
+ }
496
+ secondsToCSVTimeUnits(seconds) {
497
+ return Math.floor(seconds / 512);
498
+ }
499
+ createTimeBasedCSV(seconds) {
500
+ const timeUnits = this.secondsToCSVTimeUnits(seconds);
501
+ if (timeUnits > 0xffff) {
502
+ throw new Error(`Time units ${timeUnits} exceeds maximum of 65,535`);
503
+ }
504
+ return timeUnits | (1 << 22);
505
+ }
506
+ isCSVEnabled(sequence) {
507
+ return (sequence & (1 << 31)) === 0;
508
+ }
509
+ extractCSVValue(sequence) {
510
+ return sequence & 0x0000ffff;
511
+ }
444
512
  async signInputsWalletBased(transaction) {
445
513
  const signer = this.signer;
446
514
  await signer.multiSignPsbt([transaction]);
@@ -449,6 +517,55 @@ export class TweakedTransaction extends Logger {
449
517
  }
450
518
  this.finalized = true;
451
519
  }
520
+ isCSVScript(decompiled) {
521
+ return decompiled.some((op) => op === opcodes.OP_CHECKSEQUENCEVERIFY);
522
+ }
523
+ setCSVSequence(csvBlocks, currentSequence) {
524
+ if (this.txVersion < 2) {
525
+ throw new Error('CSV requires transaction version 2 or higher');
526
+ }
527
+ if (csvBlocks > 0xffff) {
528
+ throw new Error(`CSV blocks ${csvBlocks} exceeds maximum of 65,535`);
529
+ }
530
+ const isTimeBased = (csvBlocks & (1 << 22)) !== 0;
531
+ let sequence = csvBlocks & 0x0000ffff;
532
+ if (isTimeBased) {
533
+ sequence |= 1 << 22;
534
+ }
535
+ if (currentSequence === TransactionSequence.REPLACE_BY_FEE) {
536
+ sequence |= 1 << 25;
537
+ }
538
+ sequence = sequence & 0x7fffffff;
539
+ return sequence;
540
+ }
541
+ getCSVType(csvValue) {
542
+ return csvValue & (1 << 22) ? CSVModes.TIMESTAMPS : CSVModes.BLOCKS;
543
+ }
544
+ extractCSVBlocks(decompiled) {
545
+ for (let i = 0; i < decompiled.length; i++) {
546
+ if (decompiled[i] === opcodes.OP_CHECKSEQUENCEVERIFY && i > 0) {
547
+ const csvValue = decompiled[i - 1];
548
+ if (Buffer.isBuffer(csvValue)) {
549
+ return script.number.decode(csvValue);
550
+ }
551
+ else if (typeof csvValue === 'number') {
552
+ if (csvValue === opcodes.OP_0 || csvValue === opcodes.OP_FALSE) {
553
+ return 0;
554
+ }
555
+ else if (csvValue === opcodes.OP_1NEGATE) {
556
+ return -1;
557
+ }
558
+ else if (csvValue >= opcodes.OP_1 && csvValue <= opcodes.OP_16) {
559
+ return csvValue - opcodes.OP_1 + 1;
560
+ }
561
+ else {
562
+ throw new Error(`Unexpected raw number in script: ${csvValue}`);
563
+ }
564
+ }
565
+ }
566
+ }
567
+ return 0;
568
+ }
452
569
  async attemptSignTaproot(transaction, input, i, signer, publicKey) {
453
570
  const isScriptSpend = this.isTaprootScriptSpend(input, publicKey);
454
571
  if (isScriptSpend) {
@@ -7,9 +7,6 @@ export interface WalletUTXOs {
7
7
  readonly pending: RawUTXOResponse[];
8
8
  readonly spentTransactions: RawUTXOResponse[];
9
9
  }
10
- export interface PreimageData {
11
- readonly preimage: Buffer;
12
- }
13
10
  export declare class OPNetLimitedProvider {
14
11
  private readonly opnetAPIUrl;
15
12
  private readonly utxoPath;
@@ -18,7 +15,6 @@ export declare class OPNetLimitedProvider {
18
15
  fetchUTXO(settings: FetchUTXOParams): Promise<UTXO[]>;
19
16
  fetchUTXOMultiAddr(settings: FetchUTXOParamsMultiAddress): Promise<UTXO[]>;
20
17
  broadcastTransaction(transaction: string, psbt: boolean): Promise<BroadcastResponse | undefined>;
21
- getPreimage(): Promise<PreimageData | undefined>;
22
18
  splitUTXOs(wallet: Wallet, network: Network, splitInputsInto: number, amountPerUTXO: bigint): Promise<BroadcastResponse | {
23
19
  error: string;
24
20
  }>;
@@ -103,13 +103,6 @@ export class OPNetLimitedProvider {
103
103
  }
104
104
  return result;
105
105
  }
106
- async getPreimage() {
107
- const result = await this.rpcMethod('btc_preimage', []);
108
- if (!result) {
109
- return;
110
- }
111
- return result;
112
- }
113
106
  async splitUTXOs(wallet, network, splitInputsInto, amountPerUTXO) {
114
107
  const utxoSetting = {
115
108
  addresses: [wallet.p2wpkh, wallet.p2tr],
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@btc-vision/transaction",
3
3
  "type": "module",
4
- "version": "1.6.0",
4
+ "version": "1.6.4",
5
5
  "author": "BlobMaster41",
6
6
  "description": "OPNet transaction library allows you to create and sign transactions for the OPNet network.",
7
7
  "engines": {
@@ -89,7 +89,7 @@
89
89
  "dependencies": {
90
90
  "@babel/plugin-proposal-object-rest-spread": "^7.21.4-esm",
91
91
  "@bitcoinerlab/secp256k1": "^1.2.0",
92
- "@btc-vision/bitcoin": "^6.4.6",
92
+ "@btc-vision/bitcoin": "^6.4.8",
93
93
  "@btc-vision/bitcoin-rpc": "^1.0.2",
94
94
  "@btc-vision/logger": "^1.0.6",
95
95
  "@eslint/js": "^9.32.0",
package/src/_version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '1.6.0';
1
+ export const version = '1.6.4';
@@ -90,7 +90,7 @@ export class ChallengeSolution implements IChallengeSolution {
90
90
  /**
91
91
  * Static method to validate from raw data directly
92
92
  */
93
- public static async validateRaw(data: RawChallenge): Promise<boolean> {
93
+ public static validateRaw(data: RawChallenge): boolean {
94
94
  return EpochValidator.validateEpochWinner(data);
95
95
  }
96
96
 
@@ -115,16 +115,16 @@ export class ChallengeSolution implements IChallengeSolution {
115
115
  }
116
116
 
117
117
  /**
118
- * Verify this preimage
119
- * @returns {Promise<boolean>} True if the preimage is valid
118
+ * Verify this challenge
119
+ * @returns {boolean} True if the challenge is valid
120
120
  */
121
- public async verify(): Promise<boolean> {
122
- return EpochValidator.validatePreimage(this);
121
+ public verify(): boolean {
122
+ return EpochValidator.validateChallengeSolution(this);
123
123
  }
124
124
 
125
125
  /**
126
- * Get the preimage buffer (alias for solution)
127
- * @returns {Buffer} The solution/preimage as a buffer
126
+ * Get the preimage challenge
127
+ * @returns {Buffer} The solution/challenge as a buffer
128
128
  */
129
129
  public toBuffer(): Buffer {
130
130
  return this.solution;
@@ -162,10 +162,10 @@ export class ChallengeSolution implements IChallengeSolution {
162
162
  }
163
163
 
164
164
  /**
165
- * Calculate the expected solution hash for this preimage
165
+ * Calculate the expected solution hash for this challenge
166
166
  * @returns {Promise<Buffer>} The calculated solution hash
167
167
  */
168
- public async calculateSolution(): Promise<Buffer> {
168
+ public calculateSolution(): Buffer {
169
169
  return EpochValidator.calculateSolution(
170
170
  this.verification.targetChecksum,
171
171
  this.publicKey.toBuffer(),
@@ -174,7 +174,7 @@ export class ChallengeSolution implements IChallengeSolution {
174
174
  }
175
175
 
176
176
  /**
177
- * Check if the preimage meets a specific difficulty requirement
177
+ * Check if the challenge meets a specific difficulty requirement
178
178
  * @param {number} minDifficulty The minimum difficulty required
179
179
  * @returns {Promise<{valid: boolean; difficulty: number}>} Validation result
180
180
  */
@@ -1,9 +1,9 @@
1
1
  import { IChallengeSolution, RawChallenge } from '../interfaces/IChallengeSolution.js';
2
2
  import { ChallengeSolution } from '../ChallengeSolution.js';
3
+ import { crypto } from '@btc-vision/bitcoin';
3
4
 
4
5
  export class EpochValidator {
5
6
  private static readonly BLOCKS_PER_EPOCH: bigint = 5n;
6
- private static readonly GRAFFITI_LENGTH: number = 16;
7
7
 
8
8
  /**
9
9
  * Convert Buffer to Uint8Array
@@ -22,9 +22,8 @@ export class EpochValidator {
22
22
  /**
23
23
  * Calculate SHA-1 hash
24
24
  */
25
- public static async sha1(data: Uint8Array): Promise<Uint8Array> {
26
- const hashBuffer = await crypto.subtle.digest('SHA-1', data);
27
- return new Uint8Array(hashBuffer);
25
+ public static sha1(data: Uint8Array | Buffer): Buffer {
26
+ return crypto.sha1(Buffer.isBuffer(data) ? data : Buffer.from(data));
28
27
  }
29
28
 
30
29
  /**
@@ -78,22 +77,19 @@ export class EpochValidator {
78
77
  /**
79
78
  * Verify an epoch solution using IPreimage
80
79
  */
81
- public static async verifySolution(
82
- preimage: IChallengeSolution,
83
- log: boolean = false,
84
- ): Promise<boolean> {
80
+ public static verifySolution(challenge: IChallengeSolution, log: boolean = false): boolean {
85
81
  try {
86
- const verification = preimage.verification;
82
+ const verification = challenge.verification;
87
83
  const calculatedPreimage = this.calculatePreimage(
88
84
  verification.targetChecksum,
89
- preimage.publicKey.toBuffer(),
90
- preimage.salt,
85
+ challenge.publicKey.toBuffer(),
86
+ challenge.salt,
91
87
  );
92
88
 
93
- const computedSolution = await this.sha1(this.bufferToUint8Array(calculatedPreimage));
89
+ const computedSolution = this.sha1(calculatedPreimage);
94
90
  const computedSolutionBuffer = this.uint8ArrayToBuffer(computedSolution);
95
91
 
96
- if (!computedSolutionBuffer.equals(preimage.solution)) {
92
+ if (!computedSolutionBuffer.equals(challenge.solution)) {
97
93
  return false;
98
94
  }
99
95
 
@@ -102,11 +98,11 @@ export class EpochValidator {
102
98
  verification.targetHash,
103
99
  );
104
100
 
105
- if (matchingBits !== preimage.difficulty) {
101
+ if (matchingBits !== challenge.difficulty) {
106
102
  return false;
107
103
  }
108
104
 
109
- const expectedStartBlock = preimage.epochNumber * this.BLOCKS_PER_EPOCH;
105
+ const expectedStartBlock = challenge.epochNumber * this.BLOCKS_PER_EPOCH;
110
106
  const expectedEndBlock = expectedStartBlock + this.BLOCKS_PER_EPOCH - 1n;
111
107
 
112
108
  return !(
@@ -134,16 +130,16 @@ export class EpochValidator {
134
130
  /**
135
131
  * Validate epoch winner from raw data
136
132
  */
137
- public static async validateEpochWinner(epochData: RawChallenge): Promise<boolean> {
133
+ public static validateEpochWinner(epochData: RawChallenge): boolean {
138
134
  const preimage = new ChallengeSolution(epochData);
139
- return await this.verifySolution(preimage);
135
+ return this.verifySolution(preimage);
140
136
  }
141
137
 
142
138
  /**
143
139
  * Validate epoch winner from Preimage instance
144
140
  */
145
- public static async validatePreimage(preimage: IChallengeSolution): Promise<boolean> {
146
- return await this.verifySolution(preimage);
141
+ public static validateChallengeSolution(challenge: IChallengeSolution): boolean {
142
+ return this.verifySolution(challenge);
147
143
  }
148
144
 
149
145
  /**
@@ -153,13 +149,13 @@ export class EpochValidator {
153
149
  * @param salt The salt buffer (32 bytes)
154
150
  * @returns The SHA-1 hash of the preimage
155
151
  */
156
- public static async calculateSolution(
152
+ public static calculateSolution(
157
153
  targetChecksum: Buffer,
158
154
  publicKey: Buffer,
159
155
  salt: Buffer,
160
- ): Promise<Buffer> {
156
+ ): Buffer {
161
157
  const preimage = this.calculatePreimage(targetChecksum, publicKey, salt);
162
- const hash = await this.sha1(this.bufferToUint8Array(preimage));
158
+ const hash = this.sha1(this.bufferToUint8Array(preimage));
163
159
  return this.uint8ArrayToBuffer(hash);
164
160
  }
165
161
 
@@ -19,7 +19,7 @@ export class DeploymentGenerator extends Generator {
19
19
  * Compile a bitcoin script representing a contract deployment
20
20
  * @param {Buffer} contractBytecode - The contract bytecode
21
21
  * @param {Buffer} contractSalt - The contract salt
22
- * @param {ChallengeSolution} preimage - The preimage for reward
22
+ * @param {ChallengeSolution} challenge - The challenge for reward
23
23
  * @param {bigint} maxPriority - The maximum priority for the contract
24
24
  * @param {Buffer} [calldata] - The calldata to be passed to the contract
25
25
  * @param {Feature<Features>[]} [features] - Optional features to include in the script
@@ -28,7 +28,7 @@ export class DeploymentGenerator extends Generator {
28
28
  public compile(
29
29
  contractBytecode: Buffer,
30
30
  contractSalt: Buffer,
31
- preimage: ChallengeSolution,
31
+ challenge: ChallengeSolution,
32
32
  maxPriority: bigint,
33
33
  calldata?: Buffer,
34
34
  features?: Feature<Features>[],
@@ -36,7 +36,7 @@ export class DeploymentGenerator extends Generator {
36
36
  const asm = this.getAsm(
37
37
  contractBytecode,
38
38
  contractSalt,
39
- preimage,
39
+ challenge,
40
40
  maxPriority,
41
41
  calldata,
42
42
  features,
@@ -57,7 +57,7 @@ export class DeploymentGenerator extends Generator {
57
57
  private getAsm(
58
58
  contractBytecode: Buffer,
59
59
  contractSalt: Buffer,
60
- preimage: ChallengeSolution,
60
+ challenge: ChallengeSolution,
61
61
  maxPriority: bigint,
62
62
  calldata?: Buffer,
63
63
  features?: Feature<Features>[],
@@ -85,10 +85,10 @@ export class DeploymentGenerator extends Generator {
85
85
  opcodes.OP_TOALTSTACK,
86
86
 
87
87
  // CHALLENGE PREIMAGE FOR REWARD,
88
- preimage.publicKey.originalPublicKeyBuffer(),
88
+ challenge.publicKey.originalPublicKeyBuffer(),
89
89
  opcodes.OP_TOALTSTACK,
90
90
 
91
- preimage.solution,
91
+ challenge.solution,
92
92
  opcodes.OP_TOALTSTACK,
93
93
 
94
94
  this.xSenderPubKey,
@@ -52,7 +52,7 @@ export class LegacyCalldataGenerator extends Generator {
52
52
  * Compile an interaction bitcoin script
53
53
  * @param {Buffer} calldata - The calldata to use
54
54
  * @param {Buffer} contractSecret - The contract secret
55
- * @param {Buffer} preimage - The preimage to use
55
+ * @param {Buffer} challenge - The challenge to use
56
56
  * @param {bigint} maxPriority - The maximum priority
57
57
  * @param {number[]} [features=[]] - The features to use (optional)
58
58
  * @returns {Buffer} - The compiled script
@@ -61,7 +61,7 @@ export class LegacyCalldataGenerator extends Generator {
61
61
  public compile(
62
62
  calldata: Buffer,
63
63
  contractSecret: Buffer,
64
- preimage: Buffer,
64
+ challenge: Buffer,
65
65
  maxPriority: bigint,
66
66
  features: Feature<Features>[] = [],
67
67
  ): Buffer {
@@ -83,7 +83,7 @@ export class LegacyCalldataGenerator extends Generator {
83
83
  opcodes.OP_TOALTSTACK,
84
84
 
85
85
  // CHALLENGE PREIMAGE FOR REWARD,
86
- preimage,
86
+ challenge,
87
87
  opcodes.OP_TOALTSTACK,
88
88
 
89
89
  this.senderPubKey,
@@ -12,6 +12,7 @@ export enum AddressTypes {
12
12
  P2PK = 'P2PK',
13
13
  P2TR = 'P2TR',
14
14
  P2WPKH = 'P2WPKH',
15
+ P2WSH = 'P2WSH',
15
16
  }
16
17
 
17
18
  export class AddressVerificator {
@@ -193,11 +194,16 @@ export class AddressVerificator {
193
194
  }
194
195
 
195
196
  if (decodedBech32.prefix === network.bech32) {
196
- // P2WPKH: SegWit address (starting with 'bc1q' for mainnet, 'tb1q' for testnet)
197
+ // P2WPKH: SegWit address (20 bytes)
197
198
  if (decodedBech32.version === 0 && decodedBech32.data.length === 20) {
198
199
  return AddressTypes.P2WPKH;
199
200
  }
200
201
 
202
+ // P2WSH: SegWit script hash (32 bytes)
203
+ if (decodedBech32.version === 0 && decodedBech32.data.length === 32) {
204
+ return AddressTypes.P2WSH;
205
+ }
206
+
201
207
  // P2TR: Taproot address (starting with 'bc1p' for mainnet, 'tb1p' for testnet)
202
208
  if (decodedBech32.version === 1 && decodedBech32.data.length === 32) {
203
209
  return AddressTypes.P2TR;
@@ -29,7 +29,7 @@ export interface DeploymentResult {
29
29
 
30
30
  readonly contractAddress: string;
31
31
  readonly contractPubKey: string;
32
- readonly preimage: RawChallenge;
32
+ readonly challenge: RawChallenge;
33
33
 
34
34
  readonly utxos: UTXO[];
35
35
  }
@@ -52,7 +52,7 @@ export interface InteractionResponse {
52
52
  readonly interactionTransaction: string;
53
53
  readonly estimatedFees: bigint;
54
54
  readonly nextUTXOs: UTXO[];
55
- readonly preimage: RawChallenge;
55
+ readonly challenge: RawChallenge;
56
56
  }
57
57
 
58
58
  export interface BitcoinTransferResponse extends BitcoinTransferBase {
@@ -253,7 +253,7 @@ export class TransactionFactory {
253
253
  interactionParameters.from,
254
254
  1,
255
255
  ), // always 1
256
- preimage: preTransaction.getPreimage().toRaw(),
256
+ challenge: preTransaction.getPreimage().toRaw(),
257
257
  };
258
258
  }
259
259
 
@@ -358,7 +358,7 @@ export class TransactionFactory {
358
358
  contractAddress: finalTransaction.getContractAddress(), //finalTransaction.contractAddress.p2tr(deploymentParameters.network),
359
359
  contractPubKey: finalTransaction.contractPubKey,
360
360
  utxos: [refundUTXO],
361
- preimage: preTransaction.getPreimage().toRaw(),
361
+ challenge: preTransaction.getPreimage().toRaw(),
362
362
  };
363
363
  }
364
364
 
@@ -36,7 +36,7 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
36
36
 
37
37
  public type: TransactionType.DEPLOYMENT = TransactionType.DEPLOYMENT;
38
38
 
39
- protected readonly preimage: ChallengeSolution;
39
+ protected readonly challenge: ChallengeSolution;
40
40
  protected readonly epochChallenge: ITimeLockOutput;
41
41
 
42
42
  /**
@@ -128,10 +128,10 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
128
128
  if (!parameters.challenge) throw new Error('Challenge solution is required');
129
129
 
130
130
  this.randomBytes = parameters.randomBytes || BitcoinUtils.rndBytes();
131
- this.preimage = parameters.challenge;
131
+ this.challenge = parameters.challenge;
132
132
 
133
133
  this.epochChallenge = TimeLockGenerator.generateTimeLockAddress(
134
- this.preimage.publicKey.originalPublicKeyBuffer(),
134
+ this.challenge.publicKey.originalPublicKeyBuffer(),
135
135
  this.network,
136
136
  );
137
137
 
@@ -147,7 +147,7 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
147
147
  this.compiledTargetScript = this.deploymentGenerator.compile(
148
148
  this.bytecode,
149
149
  this.randomBytes,
150
- this.preimage,
150
+ this.challenge,
151
151
  this.priorityFee,
152
152
  this.calldata,
153
153
  this.generateFeatures(parameters),
@@ -195,7 +195,7 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
195
195
  * @returns {Buffer} The contract bytecode
196
196
  */
197
197
  public getPreimage(): ChallengeSolution {
198
- return this.preimage;
198
+ return this.challenge;
199
199
  }
200
200
 
201
201
  public getContractAddress(): string {