@btc-vision/transaction
Version:
OPNet transaction library allows you to create and sign transactions for the OPNet network.
159 lines • 5.81 kB
JavaScript
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