@btc-vision/transaction 1.5.3 → 1.6.0

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 (106) hide show
  1. package/browser/_version.d.ts +1 -1
  2. package/browser/abi/ABICoder.d.ts +1 -0
  3. package/browser/buffer/BinaryWriter.d.ts +1 -1
  4. package/browser/epoch/ChallengeSolution.d.ts +45 -0
  5. package/browser/epoch/interfaces/IChallengeSolution.d.ts +50 -0
  6. package/browser/epoch/validator/EpochValidator.d.ts +19 -0
  7. package/browser/generators/Features.d.ts +6 -1
  8. package/browser/generators/Generator.d.ts +1 -0
  9. package/browser/generators/builders/CalldataGenerator.d.ts +2 -1
  10. package/browser/generators/builders/DeploymentGenerator.d.ts +3 -1
  11. package/browser/index.js +1 -1
  12. package/browser/keypair/Address.d.ts +2 -0
  13. package/browser/opnet.d.ts +4 -3
  14. package/browser/transaction/TransactionFactory.d.ts +4 -15
  15. package/browser/transaction/browser/Web3Provider.d.ts +3 -3
  16. package/browser/transaction/browser/types/Unisat.d.ts +1 -1
  17. package/browser/transaction/builders/ChallengeSolutionTransaction.d.ts +1 -18
  18. package/browser/transaction/builders/CustomScriptTransaction.d.ts +1 -1
  19. package/browser/transaction/builders/DeploymentTransaction.d.ts +6 -4
  20. package/browser/transaction/builders/SharedInteractionTransaction.d.ts +5 -4
  21. package/browser/transaction/builders/TransactionBuilder.d.ts +2 -0
  22. package/browser/transaction/interfaces/ITransactionParameters.d.ts +4 -6
  23. package/browser/transaction/mineable/TimelockGenerator.d.ts +9 -0
  24. package/browser/utils/StringToBuffer.d.ts +1 -0
  25. package/browser/verification/TapscriptVerificator.d.ts +4 -1
  26. package/build/_version.d.ts +1 -1
  27. package/build/_version.js +1 -1
  28. package/build/abi/ABICoder.d.ts +1 -0
  29. package/build/abi/ABICoder.js +4 -0
  30. package/build/buffer/BinaryWriter.d.ts +1 -1
  31. package/build/buffer/BinaryWriter.js +11 -9
  32. package/build/epoch/ChallengeSolution.d.ts +45 -0
  33. package/build/epoch/ChallengeSolution.js +105 -0
  34. package/build/epoch/interfaces/IChallengeSolution.d.ts +50 -0
  35. package/build/epoch/interfaces/IChallengeSolution.js +1 -0
  36. package/build/epoch/validator/EpochValidator.d.ts +19 -0
  37. package/build/epoch/validator/EpochValidator.js +99 -0
  38. package/build/generators/Features.d.ts +6 -1
  39. package/build/generators/Features.js +1 -0
  40. package/build/generators/Generator.d.ts +1 -0
  41. package/build/generators/Generator.js +17 -1
  42. package/build/generators/builders/CalldataGenerator.d.ts +2 -1
  43. package/build/generators/builders/CalldataGenerator.js +4 -2
  44. package/build/generators/builders/DeploymentGenerator.d.ts +3 -1
  45. package/build/generators/builders/DeploymentGenerator.js +5 -3
  46. package/build/keypair/Address.d.ts +2 -0
  47. package/build/keypair/Address.js +12 -0
  48. package/build/keypair/EcKeyPair.js +10 -7
  49. package/build/opnet.d.ts +4 -3
  50. package/build/opnet.js +4 -3
  51. package/build/transaction/TransactionFactory.d.ts +4 -15
  52. package/build/transaction/TransactionFactory.js +4 -32
  53. package/build/transaction/browser/Web3Provider.d.ts +3 -3
  54. package/build/transaction/browser/types/Unisat.d.ts +1 -1
  55. package/build/transaction/builders/ChallengeSolutionTransaction.d.ts +0 -18
  56. package/build/transaction/builders/ChallengeSolutionTransaction.js +1 -51
  57. package/build/transaction/builders/CustomScriptTransaction.d.ts +1 -1
  58. package/build/transaction/builders/DeploymentTransaction.d.ts +6 -4
  59. package/build/transaction/builders/DeploymentTransaction.js +19 -7
  60. package/build/transaction/builders/InteractionTransaction.js +8 -1
  61. package/build/transaction/builders/SharedInteractionTransaction.d.ts +5 -4
  62. package/build/transaction/builders/SharedInteractionTransaction.js +7 -7
  63. package/build/transaction/builders/TransactionBuilder.d.ts +2 -0
  64. package/build/transaction/builders/TransactionBuilder.js +31 -3
  65. package/build/transaction/interfaces/ITransactionParameters.d.ts +4 -6
  66. package/build/transaction/mineable/TimelockGenerator.d.ts +9 -0
  67. package/build/transaction/mineable/TimelockGenerator.js +24 -0
  68. package/build/utils/StringToBuffer.d.ts +1 -0
  69. package/build/utils/StringToBuffer.js +3 -0
  70. package/build/verification/TapscriptVerificator.d.ts +4 -1
  71. package/build/verification/TapscriptVerificator.js +2 -2
  72. package/package.json +16 -16
  73. package/src/_version.ts +1 -1
  74. package/src/abi/ABICoder.ts +4 -0
  75. package/src/buffer/BinaryWriter.ts +13 -11
  76. package/src/epoch/ChallengeSolution.ts +196 -0
  77. package/src/epoch/interfaces/IChallengeSolution.ts +56 -0
  78. package/src/epoch/validator/EpochValidator.ts +180 -0
  79. package/src/generators/Features.ts +6 -0
  80. package/src/generators/Generator.ts +24 -2
  81. package/src/generators/builders/CalldataGenerator.ts +7 -3
  82. package/src/generators/builders/DeploymentGenerator.ts +18 -6
  83. package/src/keypair/Address.ts +34 -0
  84. package/src/keypair/EcKeyPair.ts +13 -8
  85. package/src/opnet.ts +6 -3
  86. package/src/transaction/TransactionFactory.ts +7 -62
  87. package/src/transaction/browser/Web3Provider.ts +3 -3
  88. package/src/transaction/browser/types/Unisat.ts +1 -1
  89. package/src/transaction/builders/ChallengeSolutionTransaction.ts +3 -4
  90. package/src/transaction/builders/CustomScriptTransaction.ts +2 -1
  91. package/src/transaction/builders/DeploymentTransaction.ts +27 -9
  92. package/src/transaction/builders/InteractionTransaction.ts +10 -2
  93. package/src/transaction/builders/SharedInteractionTransaction.ts +12 -28
  94. package/src/transaction/builders/TransactionBuilder.ts +40 -2
  95. package/src/transaction/interfaces/ITransactionParameters.ts +5 -8
  96. package/src/transaction/mineable/TimelockGenerator.ts +42 -0
  97. package/src/utils/StringToBuffer.ts +3 -0
  98. package/src/verification/TapscriptVerificator.ts +8 -4
  99. package/browser/generators/builders/MineableReward.d.ts +0 -7
  100. package/browser/transaction/mineable/ChallengeGenerator.d.ts +0 -9
  101. package/build/generators/builders/MineableReward.d.ts +0 -7
  102. package/build/generators/builders/MineableReward.js +0 -48
  103. package/build/transaction/mineable/ChallengeGenerator.d.ts +0 -9
  104. package/build/transaction/mineable/ChallengeGenerator.js +0 -28
  105. package/src/generators/builders/MineableReward.ts +0 -66
  106. package/src/transaction/mineable/ChallengeGenerator.ts +0 -39
@@ -26,9 +26,8 @@ if (!BIP32factory) {
26
26
  throw new Error('Failed to load BIP32 library');
27
27
  }
28
28
 
29
- secp256k1.utils.precompute(8);
30
-
31
- const { ProjectivePoint: Point, CURVE } = secp256k1;
29
+ const Point = secp256k1.Point;
30
+ const CURVE_N = Point.Fn.ORDER;
32
31
 
33
32
  const TAP_TAG = utf8ToBytes('TapTweak');
34
33
  const TAP_TAG_HASH = sha256(TAP_TAG);
@@ -48,6 +47,12 @@ export class EcKeyPair {
48
47
  public static BIP32: BIP32API = BIP32factory(ecc);
49
48
  public static ECPair: ECPairAPI = ECPairFactory(ecc);
50
49
 
50
+ // Initialize precomputation for better performance
51
+ static {
52
+ // Precompute tables for the base point for better performance
53
+ Point.BASE.precompute(8);
54
+ }
55
+
51
56
  /**
52
57
  * Generate a keypair from a WIF
53
58
  * @param {string} wif - The WIF to use
@@ -272,12 +277,12 @@ export class EcKeyPair {
272
277
  const P = Point.fromHex(pub);
273
278
  const Peven = (P.y & 1n) === 0n ? P : P.negate();
274
279
 
275
- const xBytes = Peven.toRawBytes(true).subarray(1);
280
+ const xBytes = Peven.toBytes(true).subarray(1);
276
281
  const tBytes = tapTweakHash(xBytes);
277
- const t = mod(bytesToNumberBE(tBytes), CURVE.n);
282
+ const t = mod(bytesToNumberBE(tBytes), CURVE_N);
278
283
 
279
284
  const Q = Peven.add(Point.BASE.multiply(t));
280
- return Buffer.from(Q.toRawBytes(true));
285
+ return Buffer.from(Q.toBytes(true));
281
286
  }
282
287
 
283
288
  /**
@@ -294,9 +299,9 @@ export class EcKeyPair {
294
299
 
295
300
  return pubkeys.map((bytes) => {
296
301
  const P = Point.fromHex(bytes);
297
- const P_even = P.hasEvenY() ? P : P.negate();
302
+ const P_even = P.y % 2n === 0n ? P : P.negate();
298
303
  const Q = P_even.add(T);
299
- return Q.toRawBytes(true);
304
+ return Q.toBytes(true);
300
305
  });
301
306
  }
302
307
 
package/src/opnet.ts CHANGED
@@ -10,12 +10,11 @@ export * from './generators/builders/CalldataGenerator.js';
10
10
  export * from './generators/builders/CustomGenerator.js';
11
11
  export * from './generators/builders/DeploymentGenerator.js';
12
12
  export * from './generators/builders/LegacyCalldataGenerator.js';
13
- export * from './generators/builders/MineableReward.js';
14
13
  export * from './generators/builders/MultiSignGenerator.js';
15
14
  export * from './generators/Features.js';
16
15
  export * from './generators/Generator.js';
17
16
 
18
- export * from './transaction/mineable/ChallengeGenerator.js';
17
+ export * from './transaction/mineable/TimelockGenerator.js';
19
18
 
20
19
  /** Address */
21
20
  export * from './generators/AddressGenerator.js';
@@ -42,7 +41,6 @@ export * from './transaction/interfaces/Tap.js';
42
41
  export * from './transaction/TransactionFactory.js';
43
42
 
44
43
  /** Builders */
45
- export * from './transaction/builders/ChallengeSolutionTransaction.js';
46
44
  export * from './transaction/builders/CustomScriptTransaction.js';
47
45
  export * from './transaction/builders/DeploymentTransaction.js';
48
46
  export * from './transaction/builders/FundingTransaction.js';
@@ -51,6 +49,11 @@ export * from './transaction/builders/MultiSignTransaction.js';
51
49
  export * from './transaction/builders/SharedInteractionTransaction.js';
52
50
  export * from './transaction/builders/TransactionBuilder.js';
53
51
 
52
+ /** Epoch */
53
+ export * from './epoch/interfaces/IChallengeSolution.js';
54
+ export * from './epoch/validator/EpochValidator.js';
55
+ export * from './epoch/ChallengeSolution.js';
56
+
54
57
  /** Utils */
55
58
  export * from './utils/BitcoinUtils.js';
56
59
  export * from './utils/lengths.js';
@@ -11,26 +11,25 @@ import { InteractionTransaction } from './builders/InteractionTransaction.js';
11
11
  import { TransactionBuilder } from './builders/TransactionBuilder.js';
12
12
  import { TransactionType } from './enums/TransactionType.js';
13
13
  import {
14
- IChallengeSolutionTransactionParameters,
15
14
  IDeploymentParameters,
16
15
  IFundingTransactionParameters,
17
16
  IInteractionParameters,
18
17
  ITransactionParameters,
19
18
  } from './interfaces/ITransactionParameters.js';
20
19
  import { PSBTTypes } from './psbt/PSBTTypes.js';
21
- import { ChallengeSolutionTransaction } from './builders/ChallengeSolutionTransaction.js';
22
20
  import {
23
21
  IDeploymentParametersWithoutSigner,
24
22
  InteractionParametersWithoutSigner,
25
23
  } from './browser/Web3Provider.js';
26
24
  import { WindowWithWallets } from './browser/extensions/UnisatSigner.js';
25
+ import { RawChallenge } from '../epoch/interfaces/IChallengeSolution.js';
27
26
 
28
27
  export interface DeploymentResult {
29
28
  readonly transaction: [string, string];
30
29
 
31
30
  readonly contractAddress: string;
32
31
  readonly contractPubKey: string;
33
- readonly preimage: string;
32
+ readonly preimage: RawChallenge;
34
33
 
35
34
  readonly utxos: UTXO[];
36
35
  }
@@ -42,13 +41,6 @@ export interface FundingTransactionResponse {
42
41
  readonly nextUTXOs: UTXO[];
43
42
  }
44
43
 
45
- export interface ChallengeSolutionResponse {
46
- readonly tx: Transaction;
47
- readonly original: ChallengeSolutionTransaction;
48
- readonly estimatedFees: bigint;
49
- readonly nextUTXOs: UTXO[];
50
- }
51
-
52
44
  export interface BitcoinTransferBase {
53
45
  readonly tx: string;
54
46
  readonly estimatedFees: bigint;
@@ -60,11 +52,7 @@ export interface InteractionResponse {
60
52
  readonly interactionTransaction: string;
61
53
  readonly estimatedFees: bigint;
62
54
  readonly nextUTXOs: UTXO[];
63
- readonly preimage: string;
64
- }
65
-
66
- export interface ChallengeSolution extends BitcoinTransferBase {
67
- readonly original: ChallengeSolutionTransaction;
55
+ readonly preimage: RawChallenge;
68
56
  }
69
57
 
70
58
  export interface BitcoinTransferResponse extends BitcoinTransferBase {
@@ -246,7 +234,7 @@ export class TransactionFactory {
246
234
  ...this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0),
247
235
  ], // always 0
248
236
  randomBytes: preTransaction.getRndBytes(),
249
- preimage: preTransaction.getPreimage(),
237
+ challenge: preTransaction.getPreimage(),
250
238
  nonWitnessUtxo: signedTransaction.tx.toBuffer(),
251
239
  estimatedFees: preTransaction.estimatedFees,
252
240
  optionalInputs: inputs,
@@ -265,7 +253,7 @@ export class TransactionFactory {
265
253
  interactionParameters.from,
266
254
  1,
267
255
  ), // always 1
268
- preimage: preTransaction.getPreimage().toString('hex'),
256
+ preimage: preTransaction.getPreimage().toRaw(),
269
257
  };
270
258
  }
271
259
 
@@ -343,7 +331,7 @@ export class TransactionFactory {
343
331
  ...deploymentParameters,
344
332
  utxos: [newUtxo], // always 0
345
333
  randomBytes: preTransaction.getRndBytes(),
346
- preimage: preTransaction.getPreimage(),
334
+ challenge: preTransaction.getPreimage(),
347
335
  nonWitnessUtxo: signedTransaction.toBuffer(),
348
336
  estimatedFees: preTransaction.estimatedFees,
349
337
  optionalInputs: inputs,
@@ -370,7 +358,7 @@ export class TransactionFactory {
370
358
  contractAddress: finalTransaction.getContractAddress(), //finalTransaction.contractAddress.p2tr(deploymentParameters.network),
371
359
  contractPubKey: finalTransaction.contractPubKey,
372
360
  utxos: [refundUTXO],
373
- preimage: preTransaction.getPreimage().toString('hex'),
361
+ preimage: preTransaction.getPreimage().toRaw(),
374
362
  };
375
363
  }
376
364
 
@@ -395,27 +383,6 @@ export class TransactionFactory {
395
383
  };
396
384
  }
397
385
 
398
- /**
399
- * @description Creates a challenge solution transaction.
400
- * @param {IChallengeSolutionTransactionParameters} parameters - The challenge solution transaction parameters
401
- * @returns {Promise<ChallengeSolution>} - The signed transaction
402
- */
403
- public async createChallengeSolution(
404
- parameters: IChallengeSolutionTransactionParameters,
405
- ): Promise<ChallengeSolution> {
406
- if (!parameters.from) {
407
- throw new Error('Field "from" not provided.');
408
- }
409
-
410
- const resp = await this._createChallengeSolution(parameters);
411
- return {
412
- estimatedFees: resp.estimatedFees,
413
- original: resp.original,
414
- tx: resp.tx.toHex(),
415
- nextUTXOs: this.getAllNewUTXOs(resp.original, resp.tx, parameters.from),
416
- };
417
- }
418
-
419
386
  /**
420
387
  * Get all new UTXOs of a generated transaction.
421
388
  * @param {TransactionBuilder<TransactionType>} original - The original transaction
@@ -520,28 +487,6 @@ export class TransactionFactory {
520
487
  return deployment;
521
488
  }
522
489
 
523
- private async _createChallengeSolution(
524
- parameters: IChallengeSolutionTransactionParameters,
525
- ): Promise<ChallengeSolutionResponse> {
526
- if (!parameters.to) throw new Error('Field "to" not provided.');
527
-
528
- const challengeTransaction: ChallengeSolutionTransaction = new ChallengeSolutionTransaction(
529
- parameters,
530
- );
531
-
532
- const signedTransaction: Transaction = await challengeTransaction.signTransaction();
533
- if (!signedTransaction) {
534
- throw new Error('Could not sign funding transaction.');
535
- }
536
-
537
- return {
538
- tx: signedTransaction,
539
- original: challengeTransaction,
540
- estimatedFees: challengeTransaction.estimatedFees,
541
- nextUTXOs: this.getUTXOAsTransaction(signedTransaction, parameters.to, 0),
542
- };
543
- }
544
-
545
490
  private async createFundTransaction(
546
491
  parameters: IFundingTransactionParameters,
547
492
  ): Promise<FundingTransactionResponse> {
@@ -8,17 +8,17 @@ import { ICustomTransactionParameters } from '../builders/CustomScriptTransactio
8
8
 
9
9
  export type InteractionParametersWithoutSigner = Omit<
10
10
  IInteractionParameters,
11
- 'signer' | 'preimage'
11
+ 'signer' | 'challenge'
12
12
  >;
13
13
 
14
14
  export type IDeploymentParametersWithoutSigner = Omit<
15
15
  IDeploymentParameters,
16
- 'signer' | 'network' | 'preimage'
16
+ 'signer' | 'network' | 'challenge'
17
17
  >;
18
18
 
19
19
  export type CustomTransactionWithoutSigner = Omit<
20
20
  ICustomTransactionParameters,
21
- 'signer' | 'preimage'
21
+ 'signer' | 'challenge'
22
22
  >;
23
23
 
24
24
  export interface BroadcastTransactionOptions {
@@ -85,7 +85,7 @@ export interface Unisat {
85
85
 
86
86
  getBalance(): Promise<Balance>;
87
87
 
88
- signMessage(message: string, type?: MessageType): Promise<string>;
88
+ signMessage(message: string | Buffer, type?: MessageType): Promise<string>;
89
89
 
90
90
  signData(hex: string, type?: SignatureType): Promise<string>;
91
91
 
@@ -1,5 +1,4 @@
1
- import { TransactionType } from '../enums/TransactionType.js';
2
- import { IChallengeSolutionTransactionParameters } from '../interfaces/ITransactionParameters.js';
1
+ /*import { TransactionType } from '../enums/TransactionType.js';
3
2
  import { getFinalScripts, opcodes, Psbt, PsbtInput, script, Signer } from '@btc-vision/bitcoin';
4
3
  import { TransactionBuilder } from './TransactionBuilder.js';
5
4
  import { ECPairInterface } from 'ecpair';
@@ -69,7 +68,7 @@ export class ChallengeSolutionTransaction extends TransactionBuilder<Transaction
69
68
  finalScriptWitness: Buffer | undefined;
70
69
  } => {
71
70
  const inputDecoded = this.inputs[inputIndex];
72
-
71
+
73
72
  if (isP2SH && inputDecoded && inputDecoded.redeemScript) {
74
73
  const scriptSig = script.compile([this.challengeSolution, inputDecoded.redeemScript]);
75
74
 
@@ -85,4 +84,4 @@ export class ChallengeSolutionTransaction extends TransactionBuilder<Transaction
85
84
  protected override getSignerKey(): Signer | ECPairInterface {
86
85
  return this.signer;
87
86
  }
88
- }
87
+ }*/
@@ -20,7 +20,8 @@ import { EcKeyPair } from '../../keypair/EcKeyPair.js';
20
20
  import { AddressGenerator } from '../../generators/AddressGenerator.js';
21
21
  import { ECPairInterface } from 'ecpair';
22
22
 
23
- export interface ICustomTransactionParameters extends SharedInteractionParameters {
23
+ export interface ICustomTransactionParameters
24
+ extends Omit<SharedInteractionParameters, 'challenge'> {
24
25
  script: (Buffer | Stack)[];
25
26
  witnesses: Buffer[];
26
27
 
@@ -27,15 +27,18 @@ import { SharedInteractionTransaction } from './SharedInteractionTransaction.js'
27
27
  import { ECPairInterface } from 'ecpair';
28
28
  import { Address } from '../../keypair/Address.js';
29
29
  import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
30
- import { ChallengeGenerator, IMineableReward } from '../mineable/ChallengeGenerator.js';
30
+ import { ITimeLockOutput, TimeLockGenerator } from '../mineable/TimelockGenerator.js';
31
+ import { ChallengeSolution } from '../../epoch/ChallengeSolution.js';
32
+ import { Feature, Features } from '../../generators/Features.js';
31
33
 
32
34
  export class DeploymentTransaction extends TransactionBuilder<TransactionType.DEPLOYMENT> {
33
35
  public static readonly MAXIMUM_CONTRACT_SIZE = 128 * 1024;
34
36
 
35
37
  public type: TransactionType.DEPLOYMENT = TransactionType.DEPLOYMENT;
36
38
 
37
- protected readonly preimage: Buffer; // ALWAYS 128 bytes for the preimage
38
- protected readonly rewardChallenge: IMineableReward;
39
+ protected readonly preimage: ChallengeSolution;
40
+ protected readonly epochChallenge: ITimeLockOutput;
41
+
39
42
  /**
40
43
  * The contract address
41
44
  * @protected
@@ -122,13 +125,13 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
122
125
  this.verifyCalldata();
123
126
  }
124
127
 
125
- if (!parameters.preimage) throw new Error('Preimage is required');
128
+ if (!parameters.challenge) throw new Error('Challenge solution is required');
126
129
 
127
130
  this.randomBytes = parameters.randomBytes || BitcoinUtils.rndBytes();
128
- this.preimage = parameters.preimage;
131
+ this.preimage = parameters.challenge;
129
132
 
130
- this.rewardChallenge = ChallengeGenerator.generateMineableReward(
131
- this.preimage,
133
+ this.epochChallenge = TimeLockGenerator.generateTimeLockAddress(
134
+ this.preimage.publicKey.originalPublicKeyBuffer(),
132
135
  this.network,
133
136
  );
134
137
 
@@ -147,6 +150,7 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
147
150
  this.preimage,
148
151
  this.priorityFee,
149
152
  this.calldata,
153
+ this.generateFeatures(parameters),
150
154
  );
151
155
 
152
156
  this.scriptTree = this.getScriptTree();
@@ -190,7 +194,7 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
190
194
  * Get the contract bytecode
191
195
  * @returns {Buffer} The contract bytecode
192
196
  */
193
- public getPreimage(): Buffer {
197
+ public getPreimage(): ChallengeSolution {
194
198
  return this.preimage;
195
199
  }
196
200
 
@@ -271,7 +275,7 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
271
275
  ) {
272
276
  this.addOutput({
273
277
  value: Number(amountSpent - amountToCA),
274
- address: this.rewardChallenge.address,
278
+ address: this.epochChallenge.address,
275
279
  });
276
280
  }
277
281
 
@@ -376,6 +380,20 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
376
380
  };
377
381
  }
378
382
 
383
+ private generateFeatures(parameters: IDeploymentParameters): Feature<Features>[] {
384
+ const features: Feature<Features>[] = [];
385
+
386
+ const submission = parameters.challenge.getSubmission();
387
+ if (submission) {
388
+ features.push({
389
+ opcode: Features.EPOCH_SUBMISSION,
390
+ data: submission,
391
+ });
392
+ }
393
+
394
+ return features;
395
+ }
396
+
379
397
  private verifyCalldata(): void {
380
398
  if (
381
399
  this.calldata &&
@@ -31,7 +31,7 @@ export class InteractionTransaction extends SharedInteractionTransaction<Transac
31
31
  }
32
32
 
33
33
  this.contractSecret = Buffer.from(parameters.contract.replace('0x', ''), 'hex');
34
-
34
+
35
35
  if (this.contractSecret.length !== 32) {
36
36
  throw new Error('Invalid contract secret length. Expected 32 bytes.');
37
37
  }
@@ -39,7 +39,7 @@ export class InteractionTransaction extends SharedInteractionTransaction<Transac
39
39
  this.compiledTargetScript = this.calldataGenerator.compile(
40
40
  this.calldata,
41
41
  this.contractSecret,
42
- this.preimage,
42
+ this.challenge,
43
43
  this.priorityFee,
44
44
  this.generateFeatures(parameters),
45
45
  );
@@ -58,6 +58,14 @@ export class InteractionTransaction extends SharedInteractionTransaction<Transac
58
58
  });
59
59
  }
60
60
 
61
+ const submission = parameters.challenge.getSubmission();
62
+ if (submission) {
63
+ features.push({
64
+ opcode: Features.EPOCH_SUBMISSION,
65
+ data: submission,
66
+ });
67
+ }
68
+
61
69
  return features;
62
70
  }
63
71
  }
@@ -8,7 +8,8 @@ import { Compressor } from '../../bytecode/Compressor.js';
8
8
  import { EcKeyPair } from '../../keypair/EcKeyPair.js';
9
9
  import { BitcoinUtils } from '../../utils/BitcoinUtils.js';
10
10
  import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
11
- import { ChallengeGenerator, IMineableReward } from '../mineable/ChallengeGenerator.js';
11
+ import { ITimeLockOutput, TimeLockGenerator } from '../mineable/TimelockGenerator.js';
12
+ import { ChallengeSolution } from '../../epoch/ChallengeSolution.js';
12
13
 
13
14
  /**
14
15
  * Shared interaction transaction
@@ -31,8 +32,8 @@ export abstract class SharedInteractionTransaction<
31
32
  protected abstract readonly compiledTargetScript: Buffer;
32
33
  protected abstract readonly scriptTree: Taptree;
33
34
 
34
- protected readonly preimage: Buffer; // ALWAYS 128 bytes for the preimage
35
- protected readonly rewardChallenge: IMineableReward;
35
+ protected readonly challenge: ChallengeSolution;
36
+ protected readonly epochChallenge: ITimeLockOutput;
36
37
 
37
38
  protected calldataGenerator: CalldataGenerator;
38
39
 
@@ -67,15 +68,15 @@ export abstract class SharedInteractionTransaction<
67
68
  throw new Error('Calldata is required');
68
69
  }
69
70
 
70
- if (!parameters.preimage) {
71
- throw new Error('Preimage is required');
71
+ if (!parameters.challenge) {
72
+ throw new Error('Challenge solution is required');
72
73
  }
73
74
 
74
- this.preimage = parameters.preimage;
75
+ this.challenge = parameters.challenge;
75
76
 
76
77
  this.disableAutoRefund = parameters.disableAutoRefund || false;
77
- this.rewardChallenge = ChallengeGenerator.generateMineableReward(
78
- this.preimage,
78
+ this.epochChallenge = TimeLockGenerator.generateTimeLockAddress(
79
+ this.challenge.publicKey.originalPublicKeyBuffer(),
79
80
  this.network,
80
81
  );
81
82
 
@@ -110,27 +111,10 @@ export abstract class SharedInteractionTransaction<
110
111
  /**
111
112
  * Get the preimage
112
113
  */
113
- public getPreimage(): Buffer {
114
- return this.preimage;
114
+ public getPreimage(): ChallengeSolution {
115
+ return this.challenge;
115
116
  }
116
117
 
117
- /**
118
- * Generate the secret for the interaction
119
- * @protected
120
- * @returns {Buffer} The secret
121
- * @throws {Error} If the to address is invalid
122
- */
123
-
124
- /*protected generateSecret(): Buffer {
125
- if (!this.to) throw new Error('To address is required');
126
-
127
- if (this.to.startsWith('0x')) {
128
- throw new Error(`Legacy not support at this time. Reserved for future use.`);
129
- }
130
-
131
- return address.fromBech32(this.to).data;
132
- }*/
133
-
134
118
  /**
135
119
  * Get the internal pubkey as an x-only key
136
120
  * @protected
@@ -381,7 +365,7 @@ export abstract class SharedInteractionTransaction<
381
365
  ) {
382
366
  this.addOutput({
383
367
  value: Number(amountSpent - amountToCA),
384
- address: this.rewardChallenge.address,
368
+ address: this.epochChallenge.address,
385
369
  });
386
370
  }
387
371
 
@@ -148,6 +148,8 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
148
148
  */
149
149
  protected isPubKeyDestination: boolean;
150
150
 
151
+ protected note?: Buffer;
152
+
151
153
  protected constructor(parameters: ITransactionParameters) {
152
154
  super(parameters);
153
155
 
@@ -164,6 +166,14 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
164
166
  this.optionalInputs = parameters.optionalInputs || [];
165
167
  this.to = parameters.to || undefined;
166
168
 
169
+ if (parameters.note) {
170
+ if (typeof parameters.note === 'string') {
171
+ this.note = Buffer.from(parameters.note, 'utf8');
172
+ } else {
173
+ this.note = parameters.note;
174
+ }
175
+ }
176
+
167
177
  this.isPubKeyDestination = this.to
168
178
  ? AddressVerificator.isValidPublicKey(this.to, this.network)
169
179
  : false;
@@ -228,6 +238,15 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
228
238
  return buffer;
229
239
  }
230
240
 
241
+ public addOPReturn(buffer: Buffer): void {
242
+ const compileScript = script.compile([opcodes.OP_RETURN, buffer]);
243
+
244
+ this.addOutput({
245
+ value: 0,
246
+ script: compileScript,
247
+ });
248
+ }
249
+
231
250
  public async getFundingTransactionParameters(): Promise<IFundingTransactionParameters> {
232
251
  if (!this.estimatedFees) {
233
252
  this.estimatedFees = await this.estimateTransactionFees();
@@ -368,8 +387,23 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
368
387
  * @returns {void}
369
388
  */
370
389
  public addOutput(output: PsbtOutputExtended): void {
371
- if (output.value === 0) return;
372
- if (output.value < TransactionBuilder.MINIMUM_DUST) {
390
+ if (output.value === 0) {
391
+ const script = output as {
392
+ script: Buffer;
393
+ };
394
+
395
+ if (!script.script || script.script.length === 0) {
396
+ throw new Error('Output value is 0 and no script provided');
397
+ }
398
+
399
+ if (script.script.length < 2) {
400
+ throw new Error('Output script is too short');
401
+ }
402
+
403
+ if (script.script[0] !== opcodes.OP_RETURN) {
404
+ throw new Error('Output script must start with OP_RETURN when value is 0');
405
+ }
406
+ } else if (output.value < TransactionBuilder.MINIMUM_DUST) {
373
407
  throw new Error(
374
408
  `Output value is less than the minimum dust ${output.value} < ${TransactionBuilder.MINIMUM_DUST}`,
375
409
  );
@@ -479,6 +513,10 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
479
513
  * @returns {Promise<void>}
480
514
  */
481
515
  protected async addRefundOutput(amountSpent: bigint): Promise<void> {
516
+ if (this.note) {
517
+ this.addOPReturn(this.note);
518
+ }
519
+
482
520
  /** Add the refund output */
483
521
  const sendBackAmount: bigint = this.totalInputAmount - amountSpent;
484
522
  if (sendBackAmount >= TransactionBuilder.MINIMUM_DUST) {
@@ -2,6 +2,7 @@ import { UTXO } from '../../utxo/interfaces/IUTXO.js';
2
2
  import { ITweakedTransactionData } from '../shared/TweakedTransaction.js';
3
3
  import { ChainId } from '../../network/ChainId.js';
4
4
  import { PsbtOutputExtended } from '@btc-vision/bitcoin';
5
+ import { ChallengeSolution } from '../../epoch/ChallengeSolution.js';
5
6
 
6
7
  export interface LoadedStorage {
7
8
  [key: string]: string[];
@@ -22,6 +23,8 @@ export interface ITransactionParameters extends ITweakedTransactionData {
22
23
  chainId?: ChainId;
23
24
  noSignatures?: boolean;
24
25
 
26
+ readonly note?: string | Buffer;
27
+
25
28
  readonly feeRate: number;
26
29
  readonly priorityFee: bigint;
27
30
  readonly gasSatFee: bigint;
@@ -33,17 +36,11 @@ export interface IFundingTransactionParameters extends ITransactionParameters {
33
36
  splitInputsInto?: number;
34
37
  }
35
38
 
36
- export interface IChallengeSolutionTransactionParameters extends ITransactionParameters {
37
- amount: bigint;
38
-
39
- readonly challengeSolution: Buffer;
40
- }
41
-
42
39
  export interface SharedInteractionParameters extends ITransactionParameters {
43
40
  calldata?: Buffer;
44
41
  disableAutoRefund?: boolean;
45
42
 
46
- readonly preimage: Buffer;
43
+ readonly challenge: ChallengeSolution;
47
44
  readonly randomBytes?: Buffer;
48
45
 
49
46
  readonly loadedStorage?: LoadedStorage;
@@ -61,5 +58,5 @@ export interface IDeploymentParameters extends Omit<ITransactionParameters, 'to'
61
58
  readonly calldata?: Buffer;
62
59
 
63
60
  readonly randomBytes?: Buffer;
64
- readonly preimage: Buffer;
61
+ readonly challenge: ChallengeSolution;
65
62
  }
@@ -0,0 +1,42 @@
1
+ import bitcoin, { Network, networks, opcodes, script } from '@btc-vision/bitcoin';
2
+
3
+ export interface ITimeLockOutput {
4
+ address: string;
5
+ witnessScript: Buffer;
6
+ }
7
+
8
+ export class TimeLockGenerator {
9
+ private static readonly CSV_BLOCKS = 75;
10
+
11
+ /**
12
+ * Generate a P2WSH address with CSV timelock
13
+ * Note: This uses ECDSA, not Schnorr (Schnorr only available in Taproot)
14
+ */
15
+ public static generateTimeLockAddress(
16
+ publicKey: Buffer,
17
+ network: Network = networks.bitcoin,
18
+ csvBlocks: number = TimeLockGenerator.CSV_BLOCKS,
19
+ ): ITimeLockOutput {
20
+ const witnessScript = script.compile([
21
+ script.number.encode(csvBlocks),
22
+ opcodes.OP_CHECKSEQUENCEVERIFY,
23
+ opcodes.OP_DROP,
24
+ publicKey,
25
+ opcodes.OP_CHECKSIG,
26
+ ]);
27
+
28
+ const p2wsh = bitcoin.payments.p2wsh({
29
+ redeem: { output: witnessScript },
30
+ network,
31
+ });
32
+
33
+ if (!p2wsh.address) {
34
+ throw new Error('Failed to generate P2WSH address');
35
+ }
36
+
37
+ return {
38
+ address: p2wsh.address,
39
+ witnessScript: witnessScript,
40
+ };
41
+ }
42
+ }
@@ -0,0 +1,3 @@
1
+ export function stringToBuffer(str: string): Buffer {
2
+ return Buffer.from(str.replace('0x', ''), 'hex');
3
+ }