@btc-vision/transaction
Version:
OPNet transaction library allows you to create and sign transactions for the OPNet network.
143 lines • 5.36 kB
JavaScript
import { alloc, fromUtf8, networks, toXOnly, } from '@btc-vision/bitcoin';
import { BinaryWriter } from '../buffer/BinaryWriter.js';
import { Features, } from './Features.js';
import { Address } from '../keypair/Address.js';
import { Compressor } from '../bytecode/Compressor.js';
/** Bitcoin Script Generator */
export class Generator {
/**
* The maximum size of a data chunk
*/
static DATA_CHUNK_SIZE = 512;
/**
* The magic number of OPNet
*/
static MAGIC = fromUtf8('op');
/**
* The public key of the sender
* @protected
*/
senderPubKey;
/**
* The public key of the sender
* @protected
*/
xSenderPubKey;
/**
* The public key of the contract salt
* @protected
*/
contractSaltPubKey;
/**
* The network to use
* @protected
*/
network = networks.bitcoin;
constructor(senderPubKey, contractSaltPubKey, network = networks.bitcoin) {
this.senderPubKey = senderPubKey;
this.contractSaltPubKey = contractSaltPubKey;
this.network = network;
this.xSenderPubKey = toXOnly(senderPubKey);
}
buildHeader(features) {
let flags = 0;
for (const feature of features) {
flags |= feature;
}
const bytesU24 = alloc(3);
bytesU24[0] = (flags >> 16) & 0xff;
bytesU24[1] = (flags >> 8) & 0xff;
bytesU24[2] = flags & 0xff;
return Uint8Array.from([this.senderPubKey[0], ...bytesU24]);
}
getHeader(maxPriority, features = []) {
const writer = new BinaryWriter(12);
writer.writeBytes(this.buildHeader(features));
writer.writeU64(maxPriority);
return new Uint8Array(writer.getBuffer());
}
/**
* Split a buffer into chunks
* @param {Uint8Array} buffer - The buffer to split
* @param {number} chunkSize - The size of each chunk
* @protected
* @returns {Array<Uint8Array[]>} - The chunks
*/
splitBufferIntoChunks(buffer, chunkSize = Generator.DATA_CHUNK_SIZE) {
const chunks = [];
for (let i = 0; i < buffer.length; i += chunkSize) {
const dataLength = Math.min(chunkSize, buffer.length - i);
const buf2 = alloc(dataLength);
for (let j = 0; j < dataLength; j++) {
buf2[j] = buffer[i + j];
}
chunks.push([buf2]);
}
return chunks;
}
encodeFeature(feature, finalBuffer) {
switch (feature.opcode) {
case Features.ACCESS_LIST: {
return this.encodeAccessListFeature(feature, finalBuffer);
}
case Features.EPOCH_SUBMISSION: {
return this.encodeChallengeSubmission(feature, finalBuffer);
}
case Features.MLDSA_LINK_PUBKEY: {
return this.encodeLinkRequest(feature, finalBuffer);
}
default:
throw new Error(`Unknown feature type: ${feature.opcode}`);
}
}
encodeAccessListFeature(feature, finalBuffer) {
const writer = new BinaryWriter();
writer.writeU16(Object.keys(feature.data).length);
for (const contract in feature.data) {
const parsedContract = Address.fromString(contract);
const data = feature.data[contract];
writer.writeAddress(parsedContract);
writer.writeU32(data.length);
for (const pointer of data) {
const pointerBuffer = Uint8Array.from(atob(pointer), (c) => c.charCodeAt(0));
if (pointerBuffer.length !== 32) {
throw new Error(`Invalid pointer length: ${pointerBuffer.length}`);
}
writer.writeBytes(pointerBuffer);
}
}
finalBuffer.writeBytesWithLength(Compressor.compress(new Uint8Array(writer.getBuffer())));
}
encodeChallengeSubmission(feature, finalBuffer) {
if ('verifySignature' in feature.data && !feature.data.verifySignature()) {
throw new Error('Invalid signature in challenge submission feature');
}
const writer = new BinaryWriter();
writer.writeBytes(feature.data.publicKey.toBuffer());
writer.writeBytes(feature.data.solution);
if (feature.data.graffiti) {
writer.writeBytesWithLength(feature.data.graffiti);
}
finalBuffer.writeBytesWithLength(writer.getBuffer());
}
encodeLinkRequest(feature, finalBuffer) {
const data = feature.data;
const writer = new BinaryWriter();
writer.writeU8(data.level);
writer.writeBytes(data.hashedPublicKey);
writer.writeBoolean(data.verifyRequest);
if (data.verifyRequest) {
if (!data.publicKey || !data.mldsaSignature) {
throw new Error('MLDSA public key and signature required when verifyRequest is true');
}
writer.writeBytes(data.publicKey);
writer.writeBytes(data.mldsaSignature);
}
if (!data.legacySignature || data.legacySignature.length !== 64) {
throw new Error('Legacy signature must be exactly 64 bytes');
}
writer.writeBytes(data.legacySignature);
finalBuffer.writeBytesWithLength(writer.getBuffer());
}
}
//# sourceMappingURL=Generator.js.map