UNPKG

@btc-vision/transaction

Version:

OPNet transaction library allows you to create and sign transactions for the OPNet network.

159 lines 5.81 kB
import { stringToBuffer } from '../utils/StringToBuffer.js'; import { Address } from '../keypair/Address.js'; import { EpochValidator } from './validator/EpochValidator.js'; import { BinaryWriter } from '../buffer/BinaryWriter.js'; import { MessageSigner } from '../keypair/MessageSigner.js'; import { toHex } from '@btc-vision/bitcoin'; export class ChallengeVerification { epochHash; epochRoot; targetHash; targetChecksum; startBlock; endBlock; proofs; constructor(data) { this.epochHash = stringToBuffer(data.epochHash); this.epochRoot = stringToBuffer(data.epochRoot); this.targetHash = stringToBuffer(data.targetHash); this.targetChecksum = stringToBuffer(data.targetChecksum); this.startBlock = BigInt(data.startBlock); this.endBlock = BigInt(data.endBlock); this.proofs = Object.freeze(data.proofs.map((proof) => stringToBuffer(proof))); } } export class ChallengeSubmission { epochNumber; publicKey; solution; graffiti; signature; constructor(data, epochNumber) { this.epochNumber = epochNumber; this.publicKey = Address.fromString(data.mldsaPublicKey, data.legacyPublicKey); this.solution = stringToBuffer(data.solution); this.graffiti = data.graffiti ? stringToBuffer(data.graffiti) : undefined; this.signature = stringToBuffer(data.signature); } verifySignature() { const signatureDataWriter = new BinaryWriter(); signatureDataWriter.writeAddress(this.publicKey); signatureDataWriter.writeU64(this.epochNumber); signatureDataWriter.writeBytes(this.solution); if (this.graffiti) { signatureDataWriter.writeBytes(this.graffiti); } const buffer = signatureDataWriter.getBuffer(); return MessageSigner.verifySignature(this.publicKey.tweakedPublicKeyToBuffer(), buffer, this.signature); } } export class ChallengeSolution { epochNumber; publicKey; solution; salt; graffiti; difficulty; verification; submission; constructor(data) { this.epochNumber = BigInt(data.epochNumber); this.publicKey = Address.fromString(data.mldsaPublicKey, data.legacyPublicKey); this.solution = stringToBuffer(data.solution); this.salt = stringToBuffer(data.salt); this.graffiti = stringToBuffer(data.graffiti); this.difficulty = data.difficulty; this.verification = new ChallengeVerification(data.verification); this.submission = data.submission ? new ChallengeSubmission(data.submission, this.epochNumber + 2n) : data.submission; } /** * Static method to validate from raw data directly */ static validateRaw(data) { return EpochValidator.validateEpochWinner(data); } verifySubmissionSignature() { if (!this.submission) { throw new Error('No submission provided in request.'); } return this.submission.verifySignature(); } getSubmission() { if (!this.submission) { return; } if (!this.verifySubmissionSignature()) { throw new Error('Invalid submission signature.'); } return this.submission; } /** * Verify this challenge * @returns {boolean} True if the challenge is valid */ verify() { return EpochValidator.validateChallengeSolution(this); } /** * Get the preimage challenge * @returns {Uint8Array} The solution/challenge as a Uint8Array */ toBuffer() { return this.solution; } /** * Get the solution as a hex string * @returns {string} The solution as a hex string with 0x prefix */ toHex() { return '0x' + toHex(this.solution); } /** * Convert to raw format for serialization */ toRaw() { return { epochNumber: this.epochNumber.toString(), mldsaPublicKey: this.publicKey.toHex(), legacyPublicKey: this.publicKey.tweakedToHex(), solution: this.toHex(), salt: '0x' + toHex(this.salt), graffiti: '0x' + toHex(this.graffiti), difficulty: this.difficulty, verification: { epochHash: '0x' + toHex(this.verification.epochHash), epochRoot: '0x' + toHex(this.verification.epochRoot), targetHash: '0x' + toHex(this.verification.targetHash), targetChecksum: '0x' + toHex(this.verification.targetChecksum), startBlock: this.verification.startBlock.toString(), endBlock: this.verification.endBlock.toString(), proofs: this.verification.proofs.map((p) => '0x' + toHex(p)), }, }; } /** * Calculate the expected solution hash for this challenge * @returns {Uint8Array} The calculated solution hash */ calculateSolution() { return EpochValidator.calculateSolution(this.verification.targetChecksum, this.publicKey.toBuffer(), this.salt); } /** * Check if the challenge meets a specific difficulty requirement * @param {number} minDifficulty The minimum difficulty required * @returns {Promise<{valid: boolean; difficulty: number}>} Validation result */ checkDifficulty(minDifficulty) { return EpochValidator.checkDifficulty(this.solution, this.verification.targetHash, minDifficulty); } /** * Get the mining target block for this epoch * @returns {bigint | null} The target block number or null if epoch 0 */ getMiningTargetBlock() { return EpochValidator.getMiningTargetBlock(this.epochNumber); } } //# sourceMappingURL=ChallengeSolution.js.map