@btc-vision/transaction
Version:
OPNet transaction library allows you to create and sign transactions for the OPNet network.
128 lines (107 loc) • 4.02 kB
text/typescript
import { crypto, Network, networks, opcodes, script } from '@btc-vision/bitcoin';
import { Generator } from '../Generator.js';
import { Feature, Features } from '../Features.js';
import { ChallengeSolution } from '../../epoch/ChallengeSolution.js';
export const OPNET_DEPLOYMENT_VERSION = 0x00;
export const versionBuffer = Buffer.from([OPNET_DEPLOYMENT_VERSION]);
export class DeploymentGenerator extends Generator {
constructor(
senderPubKey: Buffer,
contractSaltPubKey: Buffer,
network: Network = networks.bitcoin,
) {
super(senderPubKey, contractSaltPubKey, network);
}
/**
* Compile a bitcoin script representing a contract deployment
* @param {Buffer} contractBytecode - The contract bytecode
* @param {Buffer} contractSalt - The contract salt
* @param {ChallengeSolution} challenge - The challenge for reward
* @param {bigint} maxPriority - The maximum priority for the contract
* @param {Buffer} [calldata] - The calldata to be passed to the contract
* @param {Feature<Features>[]} [features] - Optional features to include in the script
* @returns {Buffer} - The compiled script
*/
public compile(
contractBytecode: Buffer,
contractSalt: Buffer,
challenge: ChallengeSolution,
maxPriority: bigint,
calldata?: Buffer,
features?: Feature<Features>[],
): Buffer {
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: Buffer,
contractSalt: Buffer,
challenge: ChallengeSolution,
maxPriority: bigint,
calldata?: Buffer,
features?: Feature<Features>[],
): (number | Buffer)[] {
if (!this.contractSaltPubKey) throw new Error('Contract salt public key not set');
const dataChunks: Buffer[][] = this.splitBufferIntoChunks(contractBytecode);
const calldataChunks: Buffer[][] = calldata ? this.splitBufferIntoChunks(calldata) : [];
const featuresList: Features[] = [];
const featureData: (number | Buffer | Buffer[])[] = [];
if (features) {
for (let i = 0; i < features.length; i++) {
const feature = features[i];
featuresList.push(feature.opcode);
const data = this.encodeFeature(feature);
featureData.push(...data);
}
}
const compiledData = [
this.getHeader(maxPriority, featuresList),
opcodes.OP_TOALTSTACK,
// CHALLENGE PREIMAGE FOR REWARD,
challenge.publicKey.originalPublicKeyBuffer(),
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();
}
}