@btc-vision/transaction 1.5.4 → 1.6.1

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 (103) hide show
  1. package/browser/_version.d.ts +1 -1
  2. package/browser/epoch/ChallengeSolution.d.ts +45 -0
  3. package/browser/epoch/interfaces/IChallengeSolution.d.ts +50 -0
  4. package/browser/epoch/validator/EpochValidator.d.ts +19 -0
  5. package/browser/generators/Features.d.ts +6 -1
  6. package/browser/generators/Generator.d.ts +1 -0
  7. package/browser/generators/builders/CalldataGenerator.d.ts +2 -1
  8. package/browser/generators/builders/DeploymentGenerator.d.ts +3 -1
  9. package/browser/generators/builders/LegacyCalldataGenerator.d.ts +1 -1
  10. package/browser/index.js +1 -1
  11. package/browser/keypair/Address.d.ts +2 -0
  12. package/browser/opnet.d.ts +4 -3
  13. package/browser/transaction/TransactionFactory.d.ts +4 -15
  14. package/browser/transaction/browser/Web3Provider.d.ts +3 -3
  15. package/browser/transaction/builders/ChallengeSolutionTransaction.d.ts +1 -18
  16. package/browser/transaction/builders/CustomScriptTransaction.d.ts +1 -1
  17. package/browser/transaction/builders/DeploymentTransaction.d.ts +6 -4
  18. package/browser/transaction/builders/SharedInteractionTransaction.d.ts +5 -4
  19. package/browser/transaction/builders/TransactionBuilder.d.ts +2 -0
  20. package/browser/transaction/interfaces/ITransactionParameters.d.ts +4 -6
  21. package/browser/transaction/mineable/TimelockGenerator.d.ts +9 -0
  22. package/browser/utils/StringToBuffer.d.ts +1 -0
  23. package/browser/utxo/OPNetLimitedProvider.d.ts +0 -4
  24. package/browser/verification/TapscriptVerificator.d.ts +4 -1
  25. package/build/_version.d.ts +1 -1
  26. package/build/_version.js +1 -1
  27. package/build/epoch/ChallengeSolution.d.ts +45 -0
  28. package/build/epoch/ChallengeSolution.js +105 -0
  29. package/build/epoch/interfaces/IChallengeSolution.d.ts +50 -0
  30. package/build/epoch/interfaces/IChallengeSolution.js +1 -0
  31. package/build/epoch/validator/EpochValidator.d.ts +19 -0
  32. package/build/epoch/validator/EpochValidator.js +99 -0
  33. package/build/generators/Features.d.ts +6 -1
  34. package/build/generators/Features.js +1 -0
  35. package/build/generators/Generator.d.ts +1 -0
  36. package/build/generators/Generator.js +17 -1
  37. package/build/generators/builders/CalldataGenerator.d.ts +2 -1
  38. package/build/generators/builders/CalldataGenerator.js +4 -2
  39. package/build/generators/builders/DeploymentGenerator.d.ts +3 -1
  40. package/build/generators/builders/DeploymentGenerator.js +6 -4
  41. package/build/generators/builders/LegacyCalldataGenerator.d.ts +1 -1
  42. package/build/generators/builders/LegacyCalldataGenerator.js +2 -2
  43. package/build/keypair/Address.d.ts +2 -0
  44. package/build/keypair/Address.js +12 -0
  45. package/build/keypair/EcKeyPair.js +6 -3
  46. package/build/opnet.d.ts +4 -3
  47. package/build/opnet.js +4 -3
  48. package/build/transaction/TransactionFactory.d.ts +4 -15
  49. package/build/transaction/TransactionFactory.js +4 -32
  50. package/build/transaction/browser/Web3Provider.d.ts +3 -3
  51. package/build/transaction/builders/ChallengeSolutionTransaction.d.ts +0 -18
  52. package/build/transaction/builders/ChallengeSolutionTransaction.js +1 -51
  53. package/build/transaction/builders/CustomScriptTransaction.d.ts +1 -1
  54. package/build/transaction/builders/DeploymentTransaction.d.ts +6 -4
  55. package/build/transaction/builders/DeploymentTransaction.js +20 -8
  56. package/build/transaction/builders/InteractionTransaction.js +8 -1
  57. package/build/transaction/builders/SharedInteractionTransaction.d.ts +5 -4
  58. package/build/transaction/builders/SharedInteractionTransaction.js +7 -7
  59. package/build/transaction/builders/TransactionBuilder.d.ts +2 -0
  60. package/build/transaction/builders/TransactionBuilder.js +31 -3
  61. package/build/transaction/interfaces/ITransactionParameters.d.ts +4 -6
  62. package/build/transaction/mineable/TimelockGenerator.d.ts +9 -0
  63. package/build/transaction/mineable/TimelockGenerator.js +24 -0
  64. package/build/utils/StringToBuffer.d.ts +1 -0
  65. package/build/utils/StringToBuffer.js +3 -0
  66. package/build/utxo/OPNetLimitedProvider.d.ts +0 -4
  67. package/build/utxo/OPNetLimitedProvider.js +0 -7
  68. package/build/verification/TapscriptVerificator.d.ts +4 -1
  69. package/build/verification/TapscriptVerificator.js +2 -2
  70. package/package.json +15 -15
  71. package/src/_version.ts +1 -1
  72. package/src/epoch/ChallengeSolution.ts +196 -0
  73. package/src/epoch/interfaces/IChallengeSolution.ts +56 -0
  74. package/src/epoch/validator/EpochValidator.ts +180 -0
  75. package/src/generators/Features.ts +6 -0
  76. package/src/generators/Generator.ts +24 -2
  77. package/src/generators/builders/CalldataGenerator.ts +7 -3
  78. package/src/generators/builders/DeploymentGenerator.ts +18 -6
  79. package/src/generators/builders/LegacyCalldataGenerator.ts +3 -3
  80. package/src/keypair/Address.ts +34 -0
  81. package/src/keypair/EcKeyPair.ts +9 -4
  82. package/src/opnet.ts +6 -3
  83. package/src/transaction/TransactionFactory.ts +7 -62
  84. package/src/transaction/browser/Web3Provider.ts +3 -3
  85. package/src/transaction/builders/ChallengeSolutionTransaction.ts +3 -4
  86. package/src/transaction/builders/CustomScriptTransaction.ts +2 -1
  87. package/src/transaction/builders/DeploymentTransaction.ts +29 -11
  88. package/src/transaction/builders/InteractionTransaction.ts +10 -2
  89. package/src/transaction/builders/SharedInteractionTransaction.ts +12 -28
  90. package/src/transaction/builders/TransactionBuilder.ts +40 -2
  91. package/src/transaction/interfaces/ITransactionParameters.ts +5 -8
  92. package/src/transaction/mineable/TimelockGenerator.ts +42 -0
  93. package/src/utils/StringToBuffer.ts +3 -0
  94. package/src/utxo/OPNetLimitedProvider.ts +0 -17
  95. package/src/verification/TapscriptVerificator.ts +8 -4
  96. package/browser/generators/builders/MineableReward.d.ts +0 -7
  97. package/browser/transaction/mineable/ChallengeGenerator.d.ts +0 -9
  98. package/build/generators/builders/MineableReward.d.ts +0 -7
  99. package/build/generators/builders/MineableReward.js +0 -48
  100. package/build/transaction/mineable/ChallengeGenerator.d.ts +0 -9
  101. package/build/transaction/mineable/ChallengeGenerator.js +0 -28
  102. package/src/generators/builders/MineableReward.ts +0 -66
  103. package/src/transaction/mineable/ChallengeGenerator.ts +0 -39
@@ -0,0 +1,180 @@
1
+ import { IChallengeSolution, RawChallenge } from '../interfaces/IChallengeSolution.js';
2
+ import { ChallengeSolution } from '../ChallengeSolution.js';
3
+
4
+ export class EpochValidator {
5
+ private static readonly BLOCKS_PER_EPOCH: bigint = 5n;
6
+ private static readonly GRAFFITI_LENGTH: number = 16;
7
+
8
+ /**
9
+ * Convert Buffer to Uint8Array
10
+ */
11
+ public static bufferToUint8Array(buffer: Buffer): Uint8Array {
12
+ return new Uint8Array(buffer);
13
+ }
14
+
15
+ /**
16
+ * Convert Uint8Array to Buffer
17
+ */
18
+ public static uint8ArrayToBuffer(array: Uint8Array): Buffer {
19
+ return Buffer.from(array);
20
+ }
21
+
22
+ /**
23
+ * Calculate SHA-1 hash
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);
28
+ }
29
+
30
+ /**
31
+ * Calculate mining preimage
32
+ */
33
+ public static calculatePreimage(checksumRoot: Buffer, publicKey: Buffer, salt: Buffer): Buffer {
34
+ // Ensure all are 32 bytes
35
+ if (checksumRoot.length !== 32 || publicKey.length !== 32 || salt.length !== 32) {
36
+ throw new Error('All inputs must be 32 bytes');
37
+ }
38
+
39
+ const preimage = Buffer.alloc(32);
40
+ for (let i = 0; i < 32; i++) {
41
+ preimage[i] = checksumRoot[i] ^ publicKey[i] ^ salt[i];
42
+ }
43
+
44
+ return preimage;
45
+ }
46
+
47
+ /**
48
+ * Count matching bits between two hashes
49
+ */
50
+ public static countMatchingBits(hash1: Buffer, hash2: Buffer): number {
51
+ let matchingBits = 0;
52
+ if (hash1.length !== hash2.length) {
53
+ throw new Error('Hashes must be of the same length');
54
+ }
55
+
56
+ const minLength = Math.min(hash1.length, hash2.length);
57
+ for (let i = 0; i < minLength; i++) {
58
+ const byte1 = hash1[i];
59
+ const byte2 = hash2[i];
60
+
61
+ if (byte1 === byte2) {
62
+ matchingBits += 8;
63
+ } else {
64
+ // Check individual bits
65
+ for (let bit = 7; bit >= 0; bit--) {
66
+ if (((byte1 >> bit) & 1) === ((byte2 >> bit) & 1)) {
67
+ matchingBits++;
68
+ } else {
69
+ return matchingBits;
70
+ }
71
+ }
72
+ }
73
+ }
74
+
75
+ return matchingBits;
76
+ }
77
+
78
+ /**
79
+ * Verify an epoch solution using IPreimage
80
+ */
81
+ public static async verifySolution(
82
+ challenge: IChallengeSolution,
83
+ log: boolean = false,
84
+ ): Promise<boolean> {
85
+ try {
86
+ const verification = challenge.verification;
87
+ const calculatedPreimage = this.calculatePreimage(
88
+ verification.targetChecksum,
89
+ challenge.publicKey.toBuffer(),
90
+ challenge.salt,
91
+ );
92
+
93
+ const computedSolution = await this.sha1(this.bufferToUint8Array(calculatedPreimage));
94
+ const computedSolutionBuffer = this.uint8ArrayToBuffer(computedSolution);
95
+
96
+ if (!computedSolutionBuffer.equals(challenge.solution)) {
97
+ return false;
98
+ }
99
+
100
+ const matchingBits = this.countMatchingBits(
101
+ computedSolutionBuffer,
102
+ verification.targetHash,
103
+ );
104
+
105
+ if (matchingBits !== challenge.difficulty) {
106
+ return false;
107
+ }
108
+
109
+ const expectedStartBlock = challenge.epochNumber * this.BLOCKS_PER_EPOCH;
110
+ const expectedEndBlock = expectedStartBlock + this.BLOCKS_PER_EPOCH - 1n;
111
+
112
+ return !(
113
+ verification.startBlock !== expectedStartBlock ||
114
+ verification.endBlock !== expectedEndBlock
115
+ );
116
+ } catch (error) {
117
+ if (log) console.error('Verification error:', error);
118
+ return false;
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Get the mining target block for an epoch
124
+ */
125
+ public static getMiningTargetBlock(epochNumber: bigint): bigint | null {
126
+ if (epochNumber === 0n) {
127
+ return null; // Epoch 0 cannot be mined
128
+ }
129
+
130
+ // Last block of previous epoch
131
+ return epochNumber * this.BLOCKS_PER_EPOCH - 1n;
132
+ }
133
+
134
+ /**
135
+ * Validate epoch winner from raw data
136
+ */
137
+ public static async validateEpochWinner(epochData: RawChallenge): Promise<boolean> {
138
+ const preimage = new ChallengeSolution(epochData);
139
+ return await this.verifySolution(preimage);
140
+ }
141
+
142
+ /**
143
+ * Validate epoch winner from Preimage instance
144
+ */
145
+ public static async validateChallengeSolution(challenge: IChallengeSolution): Promise<boolean> {
146
+ return await this.verifySolution(challenge);
147
+ }
148
+
149
+ /**
150
+ * Calculate solution hash from preimage components
151
+ * @param targetChecksum The target checksum (32 bytes)
152
+ * @param publicKey The public key buffer (32 bytes)
153
+ * @param salt The salt buffer (32 bytes)
154
+ * @returns The SHA-1 hash of the preimage
155
+ */
156
+ public static async calculateSolution(
157
+ targetChecksum: Buffer,
158
+ publicKey: Buffer,
159
+ salt: Buffer,
160
+ ): Promise<Buffer> {
161
+ const preimage = this.calculatePreimage(targetChecksum, publicKey, salt);
162
+ const hash = await this.sha1(this.bufferToUint8Array(preimage));
163
+ return this.uint8ArrayToBuffer(hash);
164
+ }
165
+
166
+ /**
167
+ * Check if a solution meets the minimum difficulty requirement
168
+ */
169
+ public static checkDifficulty(
170
+ solution: Buffer,
171
+ targetHash: Buffer,
172
+ minDifficulty: number,
173
+ ): { valid: boolean; difficulty: number } {
174
+ const difficulty = this.countMatchingBits(solution, targetHash);
175
+ return {
176
+ valid: difficulty >= minDifficulty,
177
+ difficulty,
178
+ };
179
+ }
180
+ }
@@ -1,7 +1,9 @@
1
1
  import { LoadedStorage } from '../transaction/interfaces/ITransactionParameters.js';
2
+ import { ChallengeSubmission } from '../epoch/ChallengeSolution.js';
2
3
 
3
4
  export enum Features {
4
5
  ACCESS_LIST = 1,
6
+ EPOCH_SUBMISSION = 2,
5
7
  }
6
8
 
7
9
  export interface Feature<T extends Features> {
@@ -12,3 +14,7 @@ export interface Feature<T extends Features> {
12
14
  export interface AccessListFeature extends Feature<Features.ACCESS_LIST> {
13
15
  data: LoadedStorage;
14
16
  }
17
+
18
+ export interface EpochSubmissionFeature extends Feature<Features.EPOCH_SUBMISSION> {
19
+ data: ChallengeSubmission;
20
+ }
@@ -1,6 +1,6 @@
1
1
  import { Network, networks, toXOnly } from '@btc-vision/bitcoin';
2
2
  import { BinaryWriter } from '../buffer/BinaryWriter.js';
3
- import { AccessListFeature, Feature, Features } from './Features.js';
3
+ import { AccessListFeature, EpochSubmissionFeature, Feature, Features } from './Features.js';
4
4
  import { Address } from '../keypair/Address.js';
5
5
  import { Compressor } from '../bytecode/Compressor.js';
6
6
 
@@ -107,10 +107,16 @@ export abstract class Generator {
107
107
 
108
108
  protected encodeFeature(feature: Feature<Features>): Buffer[][] {
109
109
  switch (feature.opcode) {
110
- case Features.ACCESS_LIST:
110
+ case Features.ACCESS_LIST: {
111
111
  return this.splitBufferIntoChunks(
112
112
  this.encodeAccessListFeature(feature as AccessListFeature),
113
113
  );
114
+ }
115
+ case Features.EPOCH_SUBMISSION: {
116
+ return this.splitBufferIntoChunks(
117
+ this.encodeChallengeSubmission(feature as EpochSubmissionFeature),
118
+ );
119
+ }
114
120
  default:
115
121
  throw new Error(`Unknown feature type: ${feature.opcode}`);
116
122
  }
@@ -141,4 +147,20 @@ export abstract class Generator {
141
147
 
142
148
  return Compressor.compress(Buffer.from(writer.getBuffer()));
143
149
  }
150
+
151
+ private encodeChallengeSubmission(feature: EpochSubmissionFeature): Buffer {
152
+ if ('verifySignature' in feature.data && !feature.data.verifySignature()) {
153
+ throw new Error('Invalid signature in challenge submission feature');
154
+ }
155
+
156
+ const writer = new BinaryWriter();
157
+ writer.writeBytes(feature.data.publicKey.originalPublicKeyBuffer());
158
+ writer.writeBytes(feature.data.solution);
159
+
160
+ if (feature.data.graffiti) {
161
+ writer.writeBytesWithLength(feature.data.graffiti);
162
+ }
163
+
164
+ return Buffer.from(writer.getBuffer());
165
+ }
144
166
  }
@@ -4,6 +4,7 @@ import { Compressor } from '../../bytecode/Compressor.js';
4
4
  import { EcKeyPair } from '../../keypair/EcKeyPair.js';
5
5
  import { Feature, Features } from '../Features.js';
6
6
  import { Generator } from '../Generator.js';
7
+ import { ChallengeSolution } from '../../epoch/ChallengeSolution.js';
7
8
 
8
9
  /**
9
10
  * Class to generate bitcoin script for interaction transactions
@@ -56,7 +57,7 @@ export class CalldataGenerator extends Generator {
56
57
  * Compile an interaction bitcoin script
57
58
  * @param {Buffer} calldata - The calldata to use
58
59
  * @param {Buffer} contractSecret - The contract secret
59
- * @param preimage
60
+ * @param {ChallengeSolution} challenge
60
61
  * @param maxPriority - Amount of satoshis to spend max on priority fee
61
62
  * @param {Feature<Features>[]} features - The features to use
62
63
  * @returns {Buffer} - The compiled script
@@ -65,7 +66,7 @@ export class CalldataGenerator extends Generator {
65
66
  public compile(
66
67
  calldata: Buffer,
67
68
  contractSecret: Buffer,
68
- preimage: Buffer,
69
+ challenge: ChallengeSolution,
69
70
  maxPriority: bigint,
70
71
  features: Feature<Features>[] = [],
71
72
  ): Buffer {
@@ -89,7 +90,10 @@ export class CalldataGenerator extends Generator {
89
90
  opcodes.OP_TOALTSTACK,
90
91
 
91
92
  // CHALLENGE PREIMAGE FOR REWARD,
92
- preimage,
93
+ challenge.publicKey.originalPublicKeyBuffer(),
94
+ opcodes.OP_TOALTSTACK,
95
+
96
+ challenge.solution,
93
97
  opcodes.OP_TOALTSTACK,
94
98
 
95
99
  this.xSenderPubKey,
@@ -1,6 +1,7 @@
1
1
  import { crypto, Network, networks, opcodes, script } from '@btc-vision/bitcoin';
2
2
  import { Generator } from '../Generator.js';
3
3
  import { Feature, Features } from '../Features.js';
4
+ import { ChallengeSolution } from '../../epoch/ChallengeSolution.js';
4
5
 
5
6
  export const OPNET_DEPLOYMENT_VERSION = 0x00;
6
7
  export const versionBuffer = Buffer.from([OPNET_DEPLOYMENT_VERSION]);
@@ -18,19 +19,28 @@ export class DeploymentGenerator extends Generator {
18
19
  * Compile a bitcoin script representing a contract deployment
19
20
  * @param {Buffer} contractBytecode - The contract bytecode
20
21
  * @param {Buffer} contractSalt - The contract salt
21
- * @param {Buffer} preimage - The preimage for reward
22
+ * @param {ChallengeSolution} challenge - The challenge for reward
22
23
  * @param {bigint} maxPriority - The maximum priority for the contract
23
24
  * @param {Buffer} [calldata] - The calldata to be passed to the contract
25
+ * @param {Feature<Features>[]} [features] - Optional features to include in the script
24
26
  * @returns {Buffer} - The compiled script
25
27
  */
26
28
  public compile(
27
29
  contractBytecode: Buffer,
28
30
  contractSalt: Buffer,
29
- preimage: Buffer,
31
+ challenge: ChallengeSolution,
30
32
  maxPriority: bigint,
31
33
  calldata?: Buffer,
34
+ features?: Feature<Features>[],
32
35
  ): Buffer {
33
- const asm = this.getAsm(contractBytecode, contractSalt, preimage, maxPriority, calldata);
36
+ const asm = this.getAsm(
37
+ contractBytecode,
38
+ contractSalt,
39
+ challenge,
40
+ maxPriority,
41
+ calldata,
42
+ features,
43
+ );
34
44
  const compiled = script.compile(asm);
35
45
 
36
46
  /**
@@ -47,7 +57,7 @@ export class DeploymentGenerator extends Generator {
47
57
  private getAsm(
48
58
  contractBytecode: Buffer,
49
59
  contractSalt: Buffer,
50
- preimage: Buffer,
60
+ challenge: ChallengeSolution,
51
61
  maxPriority: bigint,
52
62
  calldata?: Buffer,
53
63
  features?: Feature<Features>[],
@@ -55,7 +65,6 @@ export class DeploymentGenerator extends Generator {
55
65
  if (!this.contractSaltPubKey) throw new Error('Contract salt public key not set');
56
66
 
57
67
  const dataChunks: Buffer[][] = this.splitBufferIntoChunks(contractBytecode);
58
-
59
68
  const calldataChunks: Buffer[][] = calldata ? this.splitBufferIntoChunks(calldata) : [];
60
69
 
61
70
  const featuresList: Features[] = [];
@@ -76,7 +85,10 @@ export class DeploymentGenerator extends Generator {
76
85
  opcodes.OP_TOALTSTACK,
77
86
 
78
87
  // CHALLENGE PREIMAGE FOR REWARD,
79
- preimage,
88
+ challenge.publicKey.originalPublicKeyBuffer(),
89
+ opcodes.OP_TOALTSTACK,
90
+
91
+ challenge.solution,
80
92
  opcodes.OP_TOALTSTACK,
81
93
 
82
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,
@@ -5,6 +5,7 @@ import { AddressVerificator } from './AddressVerificator.js';
5
5
  import { EcKeyPair } from './EcKeyPair.js';
6
6
  import { ContractAddress } from '../transaction/ContractAddress.js';
7
7
  import { BitcoinUtils } from '../utils/BitcoinUtils.js';
8
+ import { ITimeLockOutput, TimeLockGenerator } from '../transaction/mineable/TimelockGenerator.js';
8
9
 
9
10
  /**
10
11
  * Objects of type "Address" are the representation of tweaked public keys. They can be converted to different address formats.
@@ -317,6 +318,39 @@ export class Address extends Uint8Array {
317
318
  throw new Error('Public key not set');
318
319
  }
319
320
 
321
+ /**
322
+ * Generate a P2WSH address with CSV (CheckSequenceVerify) timelock
323
+ * The resulting address can only be spent after the specified number of blocks
324
+ * have passed since the UTXO was created.
325
+ *
326
+ * @param {bigint | number | string} duration - The number of blocks that must pass before spending (1-65535)
327
+ * @param {Network} network - The Bitcoin network to use
328
+ * @returns {ITimeLockOutput} The timelocked address and its witness script
329
+ * @throws {Error} If the block number is out of range or public key is not available
330
+ */
331
+ public toCSV(duration: bigint | number | string, network: Network): ITimeLockOutput {
332
+ const n = Number(duration);
333
+
334
+ // First, let's validate the block number to ensure it's within the valid range
335
+ // CSV uses sequence numbers, which have special encoding for block-based locks
336
+ if (n < 1 || n > 65535) {
337
+ throw new Error('CSV block number must be between 1 and 65535');
338
+ }
339
+
340
+ // We need the original public key in compressed format for the script
341
+ // Your class stores this in #originalPublicKey when a key is set
342
+ if (!this.#originalPublicKey) {
343
+ throw new Error('Cannot create CSV address: public key not set');
344
+ }
345
+
346
+ // Convert the public key to Buffer format that TimeLockGenerator expects
347
+ const publicKeyBuffer = Buffer.from(this.#originalPublicKey);
348
+
349
+ // Now we can use your TimeLockGenerator to create the timelocked address
350
+ // Converting bigint to number is safe here because we've already validated the range
351
+ return TimeLockGenerator.generateTimeLockAddress(publicKeyBuffer, network, n);
352
+ }
353
+
320
354
  /**
321
355
  * Get an opnet address encoded in bech32m format.
322
356
  * @param network
@@ -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 { 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
@@ -274,7 +279,7 @@ export class EcKeyPair {
274
279
 
275
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
285
  return Buffer.from(Q.toBytes(true));
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 challenge: 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 challenge: 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
+ challenge: 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
+ challenge: 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 {