@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
@@ -13,7 +13,7 @@ export class InteractionTransaction extends SharedInteractionTransaction {
13
13
  if (this.contractSecret.length !== 32) {
14
14
  throw new Error('Invalid contract secret length. Expected 32 bytes.');
15
15
  }
16
- this.compiledTargetScript = this.calldataGenerator.compile(this.calldata, this.contractSecret, this.preimage, this.priorityFee, this.generateFeatures(parameters));
16
+ this.compiledTargetScript = this.calldataGenerator.compile(this.calldata, this.contractSecret, this.challenge, this.priorityFee, this.generateFeatures(parameters));
17
17
  this.scriptTree = this.getScriptTree();
18
18
  this.internalInit();
19
19
  }
@@ -25,6 +25,13 @@ export class InteractionTransaction extends SharedInteractionTransaction {
25
25
  data: parameters.loadedStorage,
26
26
  });
27
27
  }
28
+ const submission = parameters.challenge.getSubmission();
29
+ if (submission) {
30
+ features.push({
31
+ opcode: Features.EPOCH_SUBMISSION,
32
+ data: submission,
33
+ });
34
+ }
28
35
  return features;
29
36
  }
30
37
  }
@@ -4,7 +4,8 @@ import { TransactionBuilder } from './TransactionBuilder.js';
4
4
  import { TransactionType } from '../enums/TransactionType.js';
5
5
  import { CalldataGenerator } from '../../generators/builders/CalldataGenerator.js';
6
6
  import { SharedInteractionParameters } from '../interfaces/ITransactionParameters.js';
7
- import { IMineableReward } from '../mineable/ChallengeGenerator.js';
7
+ import { ITimeLockOutput } from '../mineable/TimelockGenerator.js';
8
+ import { ChallengeSolution } from '../../epoch/ChallengeSolution.js';
8
9
  export declare abstract class SharedInteractionTransaction<T extends TransactionType> extends TransactionBuilder<T> {
9
10
  static readonly MAXIMUM_CALLDATA_SIZE: number;
10
11
  readonly randomBytes: Buffer;
@@ -12,8 +13,8 @@ export declare abstract class SharedInteractionTransaction<T extends Transaction
12
13
  protected leftOverFundsScriptRedeem: P2TRPayment | null;
13
14
  protected abstract readonly compiledTargetScript: Buffer;
14
15
  protected abstract readonly scriptTree: Taptree;
15
- protected readonly preimage: Buffer;
16
- protected readonly rewardChallenge: IMineableReward;
16
+ protected readonly challenge: ChallengeSolution;
17
+ protected readonly epochChallenge: ITimeLockOutput;
17
18
  protected calldataGenerator: CalldataGenerator;
18
19
  protected readonly calldata: Buffer;
19
20
  protected abstract readonly contractSecret: Buffer;
@@ -22,7 +23,7 @@ export declare abstract class SharedInteractionTransaction<T extends Transaction
22
23
  protected constructor(parameters: SharedInteractionParameters);
23
24
  getContractSecret(): Buffer;
24
25
  getRndBytes(): Buffer;
25
- getPreimage(): Buffer;
26
+ getPreimage(): ChallengeSolution;
26
27
  protected scriptSignerXOnlyPubKey(): Buffer;
27
28
  protected generateKeyPairFromSeed(): ECPairInterface;
28
29
  protected buildTransaction(): Promise<void>;
@@ -4,7 +4,7 @@ import { CalldataGenerator } from '../../generators/builders/CalldataGenerator.j
4
4
  import { Compressor } from '../../bytecode/Compressor.js';
5
5
  import { EcKeyPair } from '../../keypair/EcKeyPair.js';
6
6
  import { BitcoinUtils } from '../../utils/BitcoinUtils.js';
7
- import { ChallengeGenerator } from '../mineable/ChallengeGenerator.js';
7
+ import { TimeLockGenerator } from '../mineable/TimelockGenerator.js';
8
8
  export class SharedInteractionTransaction extends TransactionBuilder {
9
9
  constructor(parameters) {
10
10
  super(parameters);
@@ -28,12 +28,12 @@ export class SharedInteractionTransaction extends TransactionBuilder {
28
28
  if (!parameters.calldata) {
29
29
  throw new Error('Calldata is required');
30
30
  }
31
- if (!parameters.preimage) {
32
- throw new Error('Preimage is required');
31
+ if (!parameters.challenge) {
32
+ throw new Error('Challenge solution is required');
33
33
  }
34
- this.preimage = parameters.preimage;
34
+ this.challenge = parameters.challenge;
35
35
  this.disableAutoRefund = parameters.disableAutoRefund || false;
36
- this.rewardChallenge = ChallengeGenerator.generateMineableReward(this.preimage, this.network);
36
+ this.epochChallenge = TimeLockGenerator.generateTimeLockAddress(this.challenge.publicKey.originalPublicKeyBuffer(), this.network);
37
37
  this.calldata = Compressor.compress(parameters.calldata);
38
38
  this.randomBytes = parameters.randomBytes || BitcoinUtils.rndBytes();
39
39
  this.scriptSigner = this.generateKeyPairFromSeed();
@@ -46,7 +46,7 @@ export class SharedInteractionTransaction extends TransactionBuilder {
46
46
  return this.randomBytes;
47
47
  }
48
48
  getPreimage() {
49
- return this.preimage;
49
+ return this.challenge;
50
50
  }
51
51
  scriptSignerXOnlyPubKey() {
52
52
  return toXOnly(Buffer.from(this.scriptSigner.publicKey));
@@ -196,7 +196,7 @@ export class SharedInteractionTransaction extends TransactionBuilder {
196
196
  amountSpent - MINIMUM_AMOUNT_CA > MINIMUM_AMOUNT_REWARD) {
197
197
  this.addOutput({
198
198
  value: Number(amountSpent - amountToCA),
199
- address: this.rewardChallenge.address,
199
+ address: this.epochChallenge.address,
200
200
  });
201
201
  }
202
202
  const amount = this.addOptionalOutputsAndGetAmount();
@@ -33,9 +33,11 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
33
33
  protected from: string;
34
34
  protected _maximumFeeRate: number;
35
35
  protected isPubKeyDestination: boolean;
36
+ protected note?: Buffer;
36
37
  protected constructor(parameters: ITransactionParameters);
37
38
  static getFrom(from: string | undefined, keypair: ECPairInterface | Signer, network: Network): string;
38
39
  static witnessStackToScriptWitness(witness: Buffer[]): Buffer;
40
+ addOPReturn(buffer: Buffer): void;
39
41
  getFundingTransactionParameters(): Promise<IFundingTransactionParameters>;
40
42
  setDestinationAddress(address: string): void;
41
43
  setMaximumFeeRate(feeRate: number): void;
@@ -28,6 +28,14 @@ export class TransactionBuilder extends TweakedTransaction {
28
28
  this.utxos = parameters.utxos;
29
29
  this.optionalInputs = parameters.optionalInputs || [];
30
30
  this.to = parameters.to || undefined;
31
+ if (parameters.note) {
32
+ if (typeof parameters.note === 'string') {
33
+ this.note = Buffer.from(parameters.note, 'utf8');
34
+ }
35
+ else {
36
+ this.note = parameters.note;
37
+ }
38
+ }
31
39
  this.isPubKeyDestination = this.to
32
40
  ? AddressVerificator.isValidPublicKey(this.to, this.network)
33
41
  : false;
@@ -67,6 +75,13 @@ export class TransactionBuilder extends TweakedTransaction {
67
75
  writeVector(witness);
68
76
  return buffer;
69
77
  }
78
+ addOPReturn(buffer) {
79
+ const compileScript = script.compile([opcodes.OP_RETURN, buffer]);
80
+ this.addOutput({
81
+ value: 0,
82
+ script: compileScript,
83
+ });
84
+ }
70
85
  async getFundingTransactionParameters() {
71
86
  if (!this.estimatedFees) {
72
87
  this.estimatedFees = await this.estimateTransactionFees();
@@ -141,9 +156,19 @@ export class TransactionBuilder extends TweakedTransaction {
141
156
  this.inputs.push(input);
142
157
  }
143
158
  addOutput(output) {
144
- if (output.value === 0)
145
- return;
146
- if (output.value < TransactionBuilder.MINIMUM_DUST) {
159
+ if (output.value === 0) {
160
+ const script = output;
161
+ if (!script.script || script.script.length === 0) {
162
+ throw new Error('Output value is 0 and no script provided');
163
+ }
164
+ if (script.script.length < 2) {
165
+ throw new Error('Output script is too short');
166
+ }
167
+ if (script.script[0] !== opcodes.OP_RETURN) {
168
+ throw new Error('Output script must start with OP_RETURN when value is 0');
169
+ }
170
+ }
171
+ else if (output.value < TransactionBuilder.MINIMUM_DUST) {
147
172
  throw new Error(`Output value is less than the minimum dust ${output.value} < ${TransactionBuilder.MINIMUM_DUST}`);
148
173
  }
149
174
  this.outputs.push(output);
@@ -203,6 +228,9 @@ export class TransactionBuilder extends TweakedTransaction {
203
228
  return total;
204
229
  }
205
230
  async addRefundOutput(amountSpent) {
231
+ if (this.note) {
232
+ this.addOPReturn(this.note);
233
+ }
206
234
  const sendBackAmount = this.totalInputAmount - amountSpent;
207
235
  if (sendBackAmount >= TransactionBuilder.MINIMUM_DUST) {
208
236
  if (AddressVerificator.isValidP2TRAddress(this.from, this.network)) {
@@ -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
  export interface LoadedStorage {
6
7
  [key: string]: string[];
7
8
  }
@@ -15,6 +16,7 @@ export interface ITransactionParameters extends ITweakedTransactionData {
15
16
  optionalOutputs?: PsbtOutputExtended[];
16
17
  chainId?: ChainId;
17
18
  noSignatures?: boolean;
19
+ readonly note?: string | Buffer;
18
20
  readonly feeRate: number;
19
21
  readonly priorityFee: bigint;
20
22
  readonly gasSatFee: bigint;
@@ -23,14 +25,10 @@ export interface IFundingTransactionParameters extends ITransactionParameters {
23
25
  amount: bigint;
24
26
  splitInputsInto?: number;
25
27
  }
26
- export interface IChallengeSolutionTransactionParameters extends ITransactionParameters {
27
- amount: bigint;
28
- readonly challengeSolution: Buffer;
29
- }
30
28
  export interface SharedInteractionParameters extends ITransactionParameters {
31
29
  calldata?: Buffer;
32
30
  disableAutoRefund?: boolean;
33
- readonly preimage: Buffer;
31
+ readonly challenge: ChallengeSolution;
34
32
  readonly randomBytes?: Buffer;
35
33
  readonly loadedStorage?: LoadedStorage;
36
34
  }
@@ -43,5 +41,5 @@ export interface IDeploymentParameters extends Omit<ITransactionParameters, 'to'
43
41
  readonly bytecode: Buffer;
44
42
  readonly calldata?: Buffer;
45
43
  readonly randomBytes?: Buffer;
46
- readonly preimage: Buffer;
44
+ readonly challenge: ChallengeSolution;
47
45
  }
@@ -0,0 +1,9 @@
1
+ import { Network } from '@btc-vision/bitcoin';
2
+ export interface ITimeLockOutput {
3
+ address: string;
4
+ witnessScript: Buffer;
5
+ }
6
+ export declare class TimeLockGenerator {
7
+ private static readonly CSV_BLOCKS;
8
+ static generateTimeLockAddress(publicKey: Buffer, network?: Network, csvBlocks?: number): ITimeLockOutput;
9
+ }
@@ -0,0 +1,24 @@
1
+ import bitcoin, { networks, opcodes, script } from '@btc-vision/bitcoin';
2
+ export class TimeLockGenerator {
3
+ static generateTimeLockAddress(publicKey, network = networks.bitcoin, csvBlocks = TimeLockGenerator.CSV_BLOCKS) {
4
+ const witnessScript = script.compile([
5
+ script.number.encode(csvBlocks),
6
+ opcodes.OP_CHECKSEQUENCEVERIFY,
7
+ opcodes.OP_DROP,
8
+ publicKey,
9
+ opcodes.OP_CHECKSIG,
10
+ ]);
11
+ const p2wsh = bitcoin.payments.p2wsh({
12
+ redeem: { output: witnessScript },
13
+ network,
14
+ });
15
+ if (!p2wsh.address) {
16
+ throw new Error('Failed to generate P2WSH address');
17
+ }
18
+ return {
19
+ address: p2wsh.address,
20
+ witnessScript: witnessScript,
21
+ };
22
+ }
23
+ }
24
+ TimeLockGenerator.CSV_BLOCKS = 75;
@@ -0,0 +1 @@
1
+ export declare function stringToBuffer(str: string): Buffer;
@@ -0,0 +1,3 @@
1
+ export function stringToBuffer(str) {
2
+ return Buffer.from(str.replace('0x', ''), 'hex');
3
+ }
@@ -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],
@@ -1,11 +1,14 @@
1
1
  import { Network, Taptree } from '@btc-vision/bitcoin';
2
+ import { ChallengeSolution } from '../epoch/ChallengeSolution.js';
3
+ import { Feature, Features } from '../generators/Features.js';
2
4
  export interface ContractAddressVerificationParams {
3
5
  readonly deployerPubKey: Buffer;
4
6
  readonly contractSaltPubKey: Buffer;
5
7
  readonly originalSalt: Buffer;
6
8
  readonly bytecode: Buffer;
7
- readonly preimage: Buffer;
9
+ readonly challenge: ChallengeSolution;
8
10
  readonly priorityFee: bigint;
11
+ readonly features: Feature<Features>[];
9
12
  readonly calldata?: Buffer;
10
13
  readonly network?: Network;
11
14
  }
@@ -5,7 +5,7 @@ export class TapscriptVerificator {
5
5
  static getContractAddress(params) {
6
6
  const network = params.network || networks.bitcoin;
7
7
  const scriptBuilder = new DeploymentGenerator(params.deployerPubKey, toXOnly(params.contractSaltPubKey), network);
8
- const compiledTargetScript = scriptBuilder.compile(params.bytecode, params.originalSalt, params.preimage, params.priorityFee, params.calldata);
8
+ const compiledTargetScript = scriptBuilder.compile(params.bytecode, params.originalSalt, params.challenge, params.priorityFee, params.calldata, params.features);
9
9
  const scriptTree = [
10
10
  {
11
11
  output: compiledTargetScript,
@@ -21,7 +21,7 @@ export class TapscriptVerificator {
21
21
  static verifyControlBlock(params, controlBlock) {
22
22
  const network = params.network || networks.bitcoin;
23
23
  const scriptBuilder = new DeploymentGenerator(params.deployerPubKey, toXOnly(params.contractSaltPubKey), network);
24
- const compiledTargetScript = scriptBuilder.compile(params.bytecode, params.originalSalt, params.preimage, params.priorityFee, params.calldata);
24
+ const compiledTargetScript = scriptBuilder.compile(params.bytecode, params.originalSalt, params.challenge, params.priorityFee, params.calldata, params.features);
25
25
  const scriptTree = [
26
26
  {
27
27
  output: compiledTargetScript,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@btc-vision/transaction",
3
3
  "type": "module",
4
- "version": "1.5.4",
4
+ "version": "1.6.1",
5
5
  "author": "BlobMaster41",
6
6
  "description": "OPNet transaction library allows you to create and sign transactions for the OPNet network.",
7
7
  "engines": {
@@ -64,39 +64,39 @@
64
64
  "docs": "typedoc --out docs --exclude 'src/tests/*.ts' --tsconfig tsconfig.json --readme README.md --name OPNet --plugin typedoc-material-theme --themeColor '#cb9820' --exclude src/tests/test.ts --exclude src/index.ts src"
65
65
  },
66
66
  "devDependencies": {
67
- "@babel/core": "^7.27.4",
67
+ "@babel/core": "^7.28.0",
68
68
  "@babel/plugin-proposal-class-properties": "^7.18.6",
69
- "@babel/plugin-transform-runtime": "^7.27.4",
70
- "@babel/preset-env": "^7.27.2",
69
+ "@babel/plugin-transform-runtime": "^7.28.0",
70
+ "@babel/preset-env": "^7.28.0",
71
71
  "@babel/preset-flow": "^7.27.1",
72
72
  "@babel/preset-react": "^7.27.1",
73
73
  "@babel/preset-typescript": "^7.27.1",
74
- "@types/node": "^22.15.29",
74
+ "@types/node": "^24.1.0",
75
75
  "@types/sha.js": "^2.4.4",
76
- "eslint": "^9.28.0",
76
+ "eslint": "^9.32.0",
77
77
  "gulp": "^5.0.1",
78
78
  "gulp-cached": "^1.1.1",
79
79
  "gulp-typescript": "^6.0.0-alpha.1",
80
80
  "https-browserify": "^1.0.0",
81
81
  "os-browserify": "^0.3.0",
82
- "prettier": "^3.5.3",
82
+ "prettier": "^3.6.2",
83
83
  "stream-browserify": "^3.0.0",
84
84
  "stream-http": "^3.2.0",
85
- "typedoc": "^0.28.5",
86
- "typescript-eslint": "^8.33.0",
85
+ "typedoc": "^0.28.7",
86
+ "typescript-eslint": "^8.38.0",
87
87
  "webpack-cli": "^6.0.1"
88
88
  },
89
89
  "dependencies": {
90
- "@babel/plugin-proposal-object-rest-spread": "^7.20.7",
90
+ "@babel/plugin-proposal-object-rest-spread": "^7.21.4-esm",
91
91
  "@bitcoinerlab/secp256k1": "^1.2.0",
92
92
  "@btc-vision/bitcoin": "^6.4.6",
93
93
  "@btc-vision/bitcoin-rpc": "^1.0.2",
94
94
  "@btc-vision/logger": "^1.0.6",
95
- "@eslint/js": "^9.28.0",
96
- "@noble/secp256k1": "^2.2.3",
95
+ "@eslint/js": "^9.32.0",
96
+ "@noble/secp256k1": "^2.3.0",
97
97
  "assert": "^2.1.0",
98
98
  "babel-loader": "^10.0.0",
99
- "babel-plugin-transform-import-meta": "^2.3.2",
99
+ "babel-plugin-transform-import-meta": "^2.3.3",
100
100
  "babel-preset-react": "^6.24.1",
101
101
  "babelify": "^10.0.0",
102
102
  "bech32": "^2.0.0",
@@ -109,10 +109,10 @@
109
109
  "gulp-eslint-new": "^2.4.0",
110
110
  "gulp-logger-new": "^1.0.1",
111
111
  "process": "^0.11.10",
112
- "sha.js": "^2.4.11",
112
+ "sha.js": "^2.4.12",
113
113
  "ts-loader": "^9.5.2",
114
114
  "ts-node": "^10.9.2",
115
115
  "typescript": "^5.8.3",
116
- "webpack": "^5.99.9"
116
+ "webpack": "^5.100.2"
117
117
  }
118
118
  }
package/src/_version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '1.5.3';
1
+ export const version = '1.6.1';
@@ -0,0 +1,196 @@
1
+ import { stringToBuffer } from '../utils/StringToBuffer.js';
2
+ import {
3
+ IChallengeSolution,
4
+ IChallengeSubmission,
5
+ IChallengeVerification,
6
+ RawChallenge,
7
+ RawChallengeSubmission,
8
+ RawChallengeVerification,
9
+ } from './interfaces/IChallengeSolution.js';
10
+ import { Address } from '../keypair/Address.js';
11
+ import { EpochValidator } from './validator/EpochValidator.js';
12
+ import { BinaryWriter } from '../buffer/BinaryWriter.js';
13
+ import { MessageSigner } from '../keypair/MessageSigner.js';
14
+
15
+ export class ChallengeVerification implements IChallengeVerification {
16
+ public readonly epochHash: Buffer;
17
+ public readonly epochRoot: Buffer;
18
+ public readonly targetHash: Buffer;
19
+ public readonly targetChecksum: Buffer;
20
+ public readonly startBlock: bigint;
21
+ public readonly endBlock: bigint;
22
+ public readonly proofs: readonly Buffer[];
23
+
24
+ constructor(data: RawChallengeVerification) {
25
+ this.epochHash = stringToBuffer(data.epochHash);
26
+ this.epochRoot = stringToBuffer(data.epochRoot);
27
+ this.targetHash = stringToBuffer(data.targetHash);
28
+ this.targetChecksum = stringToBuffer(data.targetChecksum);
29
+ this.startBlock = BigInt(data.startBlock);
30
+ this.endBlock = BigInt(data.endBlock);
31
+ this.proofs = Object.freeze(data.proofs.map((proof) => stringToBuffer(proof)));
32
+ }
33
+ }
34
+
35
+ export class ChallengeSubmission implements IChallengeSubmission {
36
+ public readonly publicKey: Address;
37
+ public readonly solution: Buffer;
38
+ public readonly graffiti: Buffer | undefined;
39
+ public readonly signature: Buffer;
40
+
41
+ constructor(
42
+ data: RawChallengeSubmission,
43
+ public readonly epochNumber: bigint,
44
+ ) {
45
+ this.publicKey = Address.fromString(data.publicKey);
46
+ this.solution = stringToBuffer(data.solution);
47
+ this.graffiti = data.graffiti ? stringToBuffer(data.graffiti) : undefined;
48
+ this.signature = stringToBuffer(data.signature);
49
+ }
50
+
51
+ public verifySignature(): boolean {
52
+ const signatureDataWriter = new BinaryWriter();
53
+ signatureDataWriter.writeAddress(this.publicKey);
54
+ signatureDataWriter.writeU64(this.epochNumber);
55
+ signatureDataWriter.writeBytes(this.solution);
56
+
57
+ if (this.graffiti) {
58
+ signatureDataWriter.writeBytes(this.graffiti);
59
+ }
60
+
61
+ const buffer = signatureDataWriter.getBuffer();
62
+ return MessageSigner.verifySignature(this.publicKey, buffer, this.signature);
63
+ }
64
+ }
65
+
66
+ export class ChallengeSolution implements IChallengeSolution {
67
+ public readonly epochNumber: bigint;
68
+ public readonly publicKey: Address;
69
+ public readonly solution: Buffer;
70
+ public readonly salt: Buffer;
71
+ public readonly graffiti: Buffer;
72
+ public readonly difficulty: number;
73
+ public readonly verification: ChallengeVerification;
74
+
75
+ private readonly submission?: ChallengeSubmission;
76
+
77
+ constructor(data: RawChallenge) {
78
+ this.epochNumber = BigInt(data.epochNumber);
79
+ this.publicKey = Address.fromString(data.publicKey);
80
+ this.solution = stringToBuffer(data.solution);
81
+ this.salt = stringToBuffer(data.salt);
82
+ this.graffiti = stringToBuffer(data.graffiti);
83
+ this.difficulty = data.difficulty;
84
+ this.verification = new ChallengeVerification(data.verification);
85
+ this.submission = data.submission
86
+ ? new ChallengeSubmission(data.submission, this.epochNumber + 2n)
87
+ : data.submission;
88
+ }
89
+
90
+ /**
91
+ * Static method to validate from raw data directly
92
+ */
93
+ public static async validateRaw(data: RawChallenge): Promise<boolean> {
94
+ return EpochValidator.validateEpochWinner(data);
95
+ }
96
+
97
+ public verifySubmissionSignature(): boolean {
98
+ if (!this.submission) {
99
+ throw new Error('No submission provided in request.');
100
+ }
101
+
102
+ return this.submission.verifySignature();
103
+ }
104
+
105
+ public getSubmission(): ChallengeSubmission | undefined {
106
+ if (!this.submission) {
107
+ return;
108
+ }
109
+
110
+ if (!this.verifySubmissionSignature()) {
111
+ throw new Error('Invalid submission signature.');
112
+ }
113
+
114
+ return this.submission;
115
+ }
116
+
117
+ /**
118
+ * Verify this challenge
119
+ * @returns {Promise<boolean>} True if the challenge is valid
120
+ */
121
+ public async verify(): Promise<boolean> {
122
+ return EpochValidator.validateChallengeSolution(this);
123
+ }
124
+
125
+ /**
126
+ * Get the preimage challenge
127
+ * @returns {Buffer} The solution/challenge as a buffer
128
+ */
129
+ public toBuffer(): Buffer {
130
+ return this.solution;
131
+ }
132
+
133
+ /**
134
+ * Get the solution as a hex string
135
+ * @returns {string} The solution as a hex string with 0x prefix
136
+ */
137
+ public toHex(): string {
138
+ return '0x' + this.solution.toString('hex');
139
+ }
140
+
141
+ /**
142
+ * Convert to raw format for serialization
143
+ */
144
+ public toRaw(): RawChallenge {
145
+ return {
146
+ epochNumber: this.epochNumber.toString(),
147
+ publicKey: this.publicKey.toHex(),
148
+ solution: this.toHex(),
149
+ salt: '0x' + this.salt.toString('hex'),
150
+ graffiti: '0x' + this.graffiti.toString('hex'),
151
+ difficulty: this.difficulty,
152
+ verification: {
153
+ epochHash: '0x' + this.verification.epochHash.toString('hex'),
154
+ epochRoot: '0x' + this.verification.epochRoot.toString('hex'),
155
+ targetHash: '0x' + this.verification.targetHash.toString('hex'),
156
+ targetChecksum: '0x' + this.verification.targetChecksum.toString('hex'),
157
+ startBlock: this.verification.startBlock.toString(),
158
+ endBlock: this.verification.endBlock.toString(),
159
+ proofs: this.verification.proofs.map((p) => '0x' + p.toString('hex')),
160
+ },
161
+ };
162
+ }
163
+
164
+ /**
165
+ * Calculate the expected solution hash for this challenge
166
+ * @returns {Promise<Buffer>} The calculated solution hash
167
+ */
168
+ public async calculateSolution(): Promise<Buffer> {
169
+ return EpochValidator.calculateSolution(
170
+ this.verification.targetChecksum,
171
+ this.publicKey.toBuffer(),
172
+ this.salt,
173
+ );
174
+ }
175
+
176
+ /**
177
+ * Check if the challenge meets a specific difficulty requirement
178
+ * @param {number} minDifficulty The minimum difficulty required
179
+ * @returns {Promise<{valid: boolean; difficulty: number}>} Validation result
180
+ */
181
+ public checkDifficulty(minDifficulty: number): { valid: boolean; difficulty: number } {
182
+ return EpochValidator.checkDifficulty(
183
+ this.solution,
184
+ this.verification.targetHash,
185
+ minDifficulty,
186
+ );
187
+ }
188
+
189
+ /**
190
+ * Get the mining target block for this epoch
191
+ * @returns {bigint | null} The target block number or null if epoch 0
192
+ */
193
+ public getMiningTargetBlock(): bigint | null {
194
+ return EpochValidator.getMiningTargetBlock(this.epochNumber);
195
+ }
196
+ }
@@ -0,0 +1,56 @@
1
+ import { Address } from '../../keypair/Address.js';
2
+
3
+ export interface IChallengeVerification {
4
+ readonly epochHash: Buffer;
5
+ readonly epochRoot: Buffer;
6
+ readonly targetHash: Buffer;
7
+ readonly targetChecksum: Buffer;
8
+ readonly startBlock: bigint;
9
+ readonly endBlock: bigint;
10
+ readonly proofs: readonly Buffer[];
11
+ }
12
+
13
+ export interface IChallengeSolution {
14
+ readonly epochNumber: bigint;
15
+ readonly publicKey: Address;
16
+ readonly solution: Buffer;
17
+ readonly salt: Buffer;
18
+ readonly graffiti: Buffer;
19
+ readonly difficulty: number;
20
+ readonly verification: IChallengeVerification;
21
+ }
22
+
23
+ export interface RawChallengeVerification {
24
+ readonly epochHash: string;
25
+ readonly epochRoot: string;
26
+ readonly targetHash: string;
27
+ readonly targetChecksum: string;
28
+ readonly startBlock: string;
29
+ readonly endBlock: string;
30
+ readonly proofs: readonly string[];
31
+ }
32
+
33
+ export interface RawChallengeSubmission {
34
+ readonly publicKey: string;
35
+ readonly solution: string;
36
+ readonly graffiti?: string;
37
+ readonly signature: string;
38
+ }
39
+
40
+ export interface IChallengeSubmission {
41
+ readonly publicKey: Address;
42
+ readonly solution: Buffer;
43
+ readonly graffiti?: Buffer;
44
+ readonly signature: Buffer;
45
+ }
46
+
47
+ export interface RawChallenge {
48
+ readonly epochNumber: string;
49
+ readonly publicKey: string;
50
+ readonly solution: string;
51
+ readonly salt: string;
52
+ readonly graffiti: string;
53
+ readonly difficulty: number;
54
+ readonly verification: RawChallengeVerification;
55
+ readonly submission?: RawChallengeSubmission;
56
+ }