@btc-vision/transaction
Version:
OPNet transaction library allows you to create and sign transactions for the OPNet network.
145 lines (121 loc) • 4.54 kB
text/typescript
import {
crypto,
type Network,
networks,
opcodes,
type PublicKey,
script,
} from '@btc-vision/bitcoin';
import { Generator } from '../Generator.js';
import { type Feature, Features } from '../Features.js';
import type { IChallengeSolution } from '../../epoch/interfaces/IChallengeSolution.js';
import { BinaryWriter } from '../../buffer/BinaryWriter.js';
export const OPNET_DEPLOYMENT_VERSION = 0x00;
export const versionBuffer = Uint8Array.from([OPNET_DEPLOYMENT_VERSION]);
export class DeploymentGenerator extends Generator {
constructor(
senderPubKey: PublicKey,
contractSaltPubKey: Uint8Array,
network: Network = networks.bitcoin,
) {
super(senderPubKey, contractSaltPubKey, network);
}
/**
* Compile a bitcoin script representing a contract deployment
* @param {Uint8Array} contractBytecode - The contract bytecode
* @param {Uint8Array} contractSalt - The contract salt
* @param {ChallengeSolution} challenge - The challenge for reward
* @param {bigint} maxPriority - The maximum priority for the contract
* @param {Uint8Array} [calldata] - The calldata to be passed to the contract
* @param {Feature<Features>[]} [features] - Optional features to include in the script
* @returns {Uint8Array} - The compiled script
*/
public compile(
contractBytecode: Uint8Array,
contractSalt: Uint8Array,
challenge: IChallengeSolution,
maxPriority: bigint,
calldata?: Uint8Array,
features?: Feature<Features>[],
): Uint8Array {
const asm = this.getAsm(
contractBytecode,
contractSalt,
challenge,
maxPriority,
calldata,
features,
);
const compiled = script.compile(asm);
/**
* Verify that the script can be decompiled
*/
const decompiled = script.decompile(compiled);
if (!decompiled) {
throw new Error('Failed to decompile script??');
}
return compiled;
}
private getAsm(
contractBytecode: Uint8Array,
contractSalt: Uint8Array,
challenge: IChallengeSolution,
maxPriority: bigint,
calldata?: Uint8Array,
featuresRaw?: Feature<Features>[],
): (number | Uint8Array)[] {
if (!this.contractSaltPubKey) throw new Error('Contract salt public key not set');
const dataChunks: Uint8Array[][] = this.splitBufferIntoChunks(contractBytecode);
const calldataChunks: Uint8Array[][] = calldata ? this.splitBufferIntoChunks(calldata) : [];
const featuresList: Features[] = [];
const featureData: (number | Uint8Array | Uint8Array[])[] = [];
if (featuresRaw && featuresRaw.length) {
const features: Feature<Features>[] = featuresRaw.sort(
(a, b) => a.priority - b.priority,
);
const finalBuffer = new BinaryWriter();
for (let i = 0; i < features.length; i++) {
const feature = features[i] as Feature<Features>;
featuresList.push(feature.opcode);
this.encodeFeature(feature, finalBuffer);
}
featureData.push(
...this.splitBufferIntoChunks(new Uint8Array(finalBuffer.getBuffer())),
);
}
const compiledData = [
this.getHeader(maxPriority, featuresList),
opcodes.OP_TOALTSTACK,
// CHALLENGE PREIMAGE FOR REWARD,
challenge.publicKey.toBuffer(),
opcodes.OP_TOALTSTACK,
challenge.solution,
opcodes.OP_TOALTSTACK,
this.xSenderPubKey,
opcodes.OP_DUP,
opcodes.OP_HASH256,
crypto.hash256(this.xSenderPubKey),
opcodes.OP_EQUALVERIFY,
opcodes.OP_CHECKSIGVERIFY,
this.contractSaltPubKey,
opcodes.OP_CHECKSIGVERIFY,
opcodes.OP_HASH256,
crypto.hash256(contractSalt),
opcodes.OP_EQUALVERIFY,
opcodes.OP_DEPTH,
opcodes.OP_1,
opcodes.OP_NUMEQUAL,
opcodes.OP_IF,
Generator.MAGIC,
...featureData,
opcodes.OP_0,
...calldataChunks,
opcodes.OP_1NEGATE,
...dataChunks,
opcodes.OP_ELSE,
opcodes.OP_1,
opcodes.OP_ENDIF,
];
return compiledData.flat();
}
}