@btc-vision/transaction 1.8.7 → 1.8.8

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.
@@ -6,9 +6,31 @@ export declare class EpochValidator {
6
6
  */
7
7
  static sha1(data: Uint8Array): Uint8Array;
8
8
  /**
9
- * Calculate mining preimage
9
+ * Calculate mining preimage.
10
+ *
11
+ * When useConcatenatedPreimage is false the legacy XOR construction is used
12
+ * (checksumRoot ^ publicKey ^ salt). That construction is malleable: a
13
+ * miner can set salt == publicKey to force preimage == checksumRoot (max
14
+ * difficulty for free), or transform any solution to another key via
15
+ * salt' = publicKey' ^ publicKey ^ salt. When useConcatenatedPreimage is true
16
+ * the preimage is the 96-byte concatenation checksumRoot || publicKey || salt,
17
+ * which removes both (the SHA-1 proof-of-work over it is unchanged).
18
+ *
19
+ * The switch is a consensus rule: callers pass useConcatenatedPreimage =
20
+ * (epochBlockHeight >= activationHeight) using the activation height for the
21
+ * network they are on (opnet-node exposes it as
22
+ * consensusEpochPatches.PREIMAGE_CONCAT_PATCH_BLOCK_HEIGHT). The value MUST be
23
+ * identical across opnet-node, this library and the mining pool.
10
24
  */
11
- static calculatePreimage(checksumRoot: Uint8Array, publicKey: Uint8Array, salt: Uint8Array): Uint8Array;
25
+ static calculatePreimage(checksumRoot: Uint8Array, publicKey: Uint8Array, salt: Uint8Array, useConcatenatedPreimage?: boolean): Uint8Array;
26
+ /**
27
+ * Non-malleable mining preimage: the 96-byte concatenation
28
+ * checksumRoot || publicKey || salt. The public key sits in its own segment,
29
+ * so a miner-chosen salt can neither cancel it nor be transformed to
30
+ * re-attribute another miner's solution. The SHA-1 proof-of-work over it is
31
+ * unchanged.
32
+ */
33
+ static concatenatePreimage(checksumRoot: Uint8Array, publicKey: Uint8Array, salt: Uint8Array): Uint8Array;
12
34
  /**
13
35
  * Count matching bits between two hashes
14
36
  */
@@ -16,7 +38,7 @@ export declare class EpochValidator {
16
38
  /**
17
39
  * Verify an epoch solution using IPreimage
18
40
  */
19
- static verifySolution(challenge: IChallengeSolution, log?: boolean): boolean;
41
+ static verifySolution(challenge: IChallengeSolution, log?: boolean, useHashedPreimage?: boolean): boolean;
20
42
  /**
21
43
  * Get the mining target block for an epoch
22
44
  */
@@ -24,11 +46,11 @@ export declare class EpochValidator {
24
46
  /**
25
47
  * Validate epoch winner from raw data
26
48
  */
27
- static validateEpochWinner(epochData: RawChallenge): boolean;
49
+ static validateEpochWinner(epochData: RawChallenge, useHashedPreimage?: boolean): boolean;
28
50
  /**
29
51
  * Validate epoch winner from Preimage instance
30
52
  */
31
- static validateChallengeSolution(challenge: IChallengeSolution): boolean;
53
+ static validateChallengeSolution(challenge: IChallengeSolution, useHashedPreimage?: boolean): boolean;
32
54
  /**
33
55
  * Calculate solution hash from preimage components
34
56
  * @param targetChecksum The target checksum (32 bytes)
@@ -36,7 +58,7 @@ export declare class EpochValidator {
36
58
  * @param salt The salt buffer (32 bytes)
37
59
  * @returns The SHA-1 hash of the preimage
38
60
  */
39
- static calculateSolution(targetChecksum: Uint8Array, publicKey: Uint8Array, salt: Uint8Array): Uint8Array;
61
+ static calculateSolution(targetChecksum: Uint8Array, publicKey: Uint8Array, salt: Uint8Array, useHashedPreimage?: boolean): Uint8Array;
40
62
  /**
41
63
  * Check if a solution meets the minimum difficulty requirement
42
64
  */
@@ -10,13 +10,30 @@ export class EpochValidator {
10
10
  return crypto.sha1(data);
11
11
  }
12
12
  /**
13
- * Calculate mining preimage
13
+ * Calculate mining preimage.
14
+ *
15
+ * When useConcatenatedPreimage is false the legacy XOR construction is used
16
+ * (checksumRoot ^ publicKey ^ salt). That construction is malleable: a
17
+ * miner can set salt == publicKey to force preimage == checksumRoot (max
18
+ * difficulty for free), or transform any solution to another key via
19
+ * salt' = publicKey' ^ publicKey ^ salt. When useConcatenatedPreimage is true
20
+ * the preimage is the 96-byte concatenation checksumRoot || publicKey || salt,
21
+ * which removes both (the SHA-1 proof-of-work over it is unchanged).
22
+ *
23
+ * The switch is a consensus rule: callers pass useConcatenatedPreimage =
24
+ * (epochBlockHeight >= activationHeight) using the activation height for the
25
+ * network they are on (opnet-node exposes it as
26
+ * consensusEpochPatches.PREIMAGE_CONCAT_PATCH_BLOCK_HEIGHT). The value MUST be
27
+ * identical across opnet-node, this library and the mining pool.
14
28
  */
15
- static calculatePreimage(checksumRoot, publicKey, salt) {
29
+ static calculatePreimage(checksumRoot, publicKey, salt, useConcatenatedPreimage = false) {
16
30
  // Ensure all are 32 bytes
17
31
  if (checksumRoot.length !== 32 || publicKey.length !== 32 || salt.length !== 32) {
18
32
  throw new Error('All inputs must be 32 bytes');
19
33
  }
34
+ if (useConcatenatedPreimage) {
35
+ return this.concatenatePreimage(checksumRoot, publicKey, salt);
36
+ }
20
37
  const preimage = new Uint8Array(32);
21
38
  for (let i = 0; i < 32; i++) {
22
39
  preimage[i] =
@@ -24,6 +41,23 @@ export class EpochValidator {
24
41
  }
25
42
  return preimage;
26
43
  }
44
+ /**
45
+ * Non-malleable mining preimage: the 96-byte concatenation
46
+ * checksumRoot || publicKey || salt. The public key sits in its own segment,
47
+ * so a miner-chosen salt can neither cancel it nor be transformed to
48
+ * re-attribute another miner's solution. The SHA-1 proof-of-work over it is
49
+ * unchanged.
50
+ */
51
+ static concatenatePreimage(checksumRoot, publicKey, salt) {
52
+ if (checksumRoot.length !== 32 || publicKey.length !== 32 || salt.length !== 32) {
53
+ throw new Error('All inputs must be 32 bytes');
54
+ }
55
+ const message = new Uint8Array(96);
56
+ message.set(checksumRoot, 0);
57
+ message.set(publicKey, 32);
58
+ message.set(salt, 64);
59
+ return message;
60
+ }
27
61
  /**
28
62
  * Count matching bits between two hashes
29
63
  */
@@ -56,10 +90,10 @@ export class EpochValidator {
56
90
  /**
57
91
  * Verify an epoch solution using IPreimage
58
92
  */
59
- static verifySolution(challenge, log = false) {
93
+ static verifySolution(challenge, log = false, useHashedPreimage = false) {
60
94
  try {
61
95
  const verification = challenge.verification;
62
- const calculatedPreimage = this.calculatePreimage(verification.targetChecksum, challenge.publicKey.toBuffer(), challenge.salt);
96
+ const calculatedPreimage = this.calculatePreimage(verification.targetChecksum, challenge.publicKey.toBuffer(), challenge.salt, useHashedPreimage);
63
97
  const computedSolution = this.sha1(calculatedPreimage);
64
98
  if (!equals(computedSolution, challenge.solution)) {
65
99
  return false;
@@ -92,7 +126,7 @@ export class EpochValidator {
92
126
  /**
93
127
  * Validate epoch winner from raw data
94
128
  */
95
- static validateEpochWinner(epochData) {
129
+ static validateEpochWinner(epochData, useHashedPreimage = false) {
96
130
  try {
97
131
  const epochNumber = BigInt(epochData.epochNumber);
98
132
  const publicKey = Address.fromString(epochData.mldsaPublicKey, epochData.legacyPublicKey);
@@ -108,7 +142,7 @@ export class EpochValidator {
108
142
  endBlock: BigInt(epochData.verification.endBlock),
109
143
  proofs: Object.freeze(epochData.verification.proofs.map((p) => stringToBuffer(p))),
110
144
  };
111
- const calculatedPreimage = this.calculatePreimage(verification.targetChecksum, publicKey.toBuffer(), salt);
145
+ const calculatedPreimage = this.calculatePreimage(verification.targetChecksum, publicKey.toBuffer(), salt, useHashedPreimage);
112
146
  const computedSolution = this.sha1(calculatedPreimage);
113
147
  if (!equals(computedSolution, solution)) {
114
148
  return false;
@@ -129,8 +163,8 @@ export class EpochValidator {
129
163
  /**
130
164
  * Validate epoch winner from Preimage instance
131
165
  */
132
- static validateChallengeSolution(challenge) {
133
- return this.verifySolution(challenge);
166
+ static validateChallengeSolution(challenge, useHashedPreimage = false) {
167
+ return this.verifySolution(challenge, false, useHashedPreimage);
134
168
  }
135
169
  /**
136
170
  * Calculate solution hash from preimage components
@@ -139,8 +173,8 @@ export class EpochValidator {
139
173
  * @param salt The salt buffer (32 bytes)
140
174
  * @returns The SHA-1 hash of the preimage
141
175
  */
142
- static calculateSolution(targetChecksum, publicKey, salt) {
143
- const preimage = this.calculatePreimage(targetChecksum, publicKey, salt);
176
+ static calculateSolution(targetChecksum, publicKey, salt, useHashedPreimage = false) {
177
+ const preimage = this.calculatePreimage(targetChecksum, publicKey, salt, useHashedPreimage);
144
178
  return this.sha1(preimage);
145
179
  }
146
180
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@btc-vision/transaction",
3
3
  "type": "module",
4
- "version": "1.8.7",
4
+ "version": "1.8.8",
5
5
  "author": "BlobMaster41",
6
6
  "description": "OPNet transaction library allows you to create and sign transactions for the OPNet network.",
7
7
  "engines": {
@@ -14,18 +14,37 @@ export class EpochValidator {
14
14
  }
15
15
 
16
16
  /**
17
- * Calculate mining preimage
17
+ * Calculate mining preimage.
18
+ *
19
+ * When useConcatenatedPreimage is false the legacy XOR construction is used
20
+ * (checksumRoot ^ publicKey ^ salt). That construction is malleable: a
21
+ * miner can set salt == publicKey to force preimage == checksumRoot (max
22
+ * difficulty for free), or transform any solution to another key via
23
+ * salt' = publicKey' ^ publicKey ^ salt. When useConcatenatedPreimage is true
24
+ * the preimage is the 96-byte concatenation checksumRoot || publicKey || salt,
25
+ * which removes both (the SHA-1 proof-of-work over it is unchanged).
26
+ *
27
+ * The switch is a consensus rule: callers pass useConcatenatedPreimage =
28
+ * (epochBlockHeight >= activationHeight) using the activation height for the
29
+ * network they are on (opnet-node exposes it as
30
+ * consensusEpochPatches.PREIMAGE_CONCAT_PATCH_BLOCK_HEIGHT). The value MUST be
31
+ * identical across opnet-node, this library and the mining pool.
18
32
  */
19
33
  public static calculatePreimage(
20
34
  checksumRoot: Uint8Array,
21
35
  publicKey: Uint8Array,
22
36
  salt: Uint8Array,
37
+ useConcatenatedPreimage: boolean = false,
23
38
  ): Uint8Array {
24
39
  // Ensure all are 32 bytes
25
40
  if (checksumRoot.length !== 32 || publicKey.length !== 32 || salt.length !== 32) {
26
41
  throw new Error('All inputs must be 32 bytes');
27
42
  }
28
43
 
44
+ if (useConcatenatedPreimage) {
45
+ return this.concatenatePreimage(checksumRoot, publicKey, salt);
46
+ }
47
+
29
48
  const preimage = new Uint8Array(32);
30
49
  for (let i = 0; i < 32; i++) {
31
50
  preimage[i] =
@@ -35,6 +54,30 @@ export class EpochValidator {
35
54
  return preimage;
36
55
  }
37
56
 
57
+ /**
58
+ * Non-malleable mining preimage: the 96-byte concatenation
59
+ * checksumRoot || publicKey || salt. The public key sits in its own segment,
60
+ * so a miner-chosen salt can neither cancel it nor be transformed to
61
+ * re-attribute another miner's solution. The SHA-1 proof-of-work over it is
62
+ * unchanged.
63
+ */
64
+ public static concatenatePreimage(
65
+ checksumRoot: Uint8Array,
66
+ publicKey: Uint8Array,
67
+ salt: Uint8Array,
68
+ ): Uint8Array {
69
+ if (checksumRoot.length !== 32 || publicKey.length !== 32 || salt.length !== 32) {
70
+ throw new Error('All inputs must be 32 bytes');
71
+ }
72
+
73
+ const message = new Uint8Array(96);
74
+ message.set(checksumRoot, 0);
75
+ message.set(publicKey, 32);
76
+ message.set(salt, 64);
77
+
78
+ return message;
79
+ }
80
+
38
81
  /**
39
82
  * Count matching bits between two hashes
40
83
  */
@@ -69,13 +112,18 @@ export class EpochValidator {
69
112
  /**
70
113
  * Verify an epoch solution using IPreimage
71
114
  */
72
- public static verifySolution(challenge: IChallengeSolution, log: boolean = false): boolean {
115
+ public static verifySolution(
116
+ challenge: IChallengeSolution,
117
+ log: boolean = false,
118
+ useHashedPreimage: boolean = false,
119
+ ): boolean {
73
120
  try {
74
121
  const verification = challenge.verification;
75
122
  const calculatedPreimage = this.calculatePreimage(
76
123
  verification.targetChecksum,
77
124
  challenge.publicKey.toBuffer(),
78
125
  challenge.salt,
126
+ useHashedPreimage,
79
127
  );
80
128
 
81
129
  const computedSolution = this.sha1(calculatedPreimage);
@@ -118,7 +166,10 @@ export class EpochValidator {
118
166
  /**
119
167
  * Validate epoch winner from raw data
120
168
  */
121
- public static validateEpochWinner(epochData: RawChallenge): boolean {
169
+ public static validateEpochWinner(
170
+ epochData: RawChallenge,
171
+ useHashedPreimage: boolean = false,
172
+ ): boolean {
122
173
  try {
123
174
  const epochNumber = BigInt(epochData.epochNumber);
124
175
  const publicKey = Address.fromString(
@@ -143,6 +194,7 @@ export class EpochValidator {
143
194
  verification.targetChecksum,
144
195
  publicKey.toBuffer(),
145
196
  salt,
197
+ useHashedPreimage,
146
198
  );
147
199
 
148
200
  const computedSolution = this.sha1(calculatedPreimage);
@@ -172,8 +224,11 @@ export class EpochValidator {
172
224
  /**
173
225
  * Validate epoch winner from Preimage instance
174
226
  */
175
- public static validateChallengeSolution(challenge: IChallengeSolution): boolean {
176
- return this.verifySolution(challenge);
227
+ public static validateChallengeSolution(
228
+ challenge: IChallengeSolution,
229
+ useHashedPreimage: boolean = false,
230
+ ): boolean {
231
+ return this.verifySolution(challenge, false, useHashedPreimage);
177
232
  }
178
233
 
179
234
  /**
@@ -187,8 +242,14 @@ export class EpochValidator {
187
242
  targetChecksum: Uint8Array,
188
243
  publicKey: Uint8Array,
189
244
  salt: Uint8Array,
245
+ useHashedPreimage: boolean = false,
190
246
  ): Uint8Array {
191
- const preimage = this.calculatePreimage(targetChecksum, publicKey, salt);
247
+ const preimage = this.calculatePreimage(
248
+ targetChecksum,
249
+ publicKey,
250
+ salt,
251
+ useHashedPreimage,
252
+ );
192
253
  return this.sha1(preimage);
193
254
  }
194
255