UNPKG

@btc-vision/transaction

Version:

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

138 lines (121 loc) 4.38 kB
import { concat, crypto as bitCrypto, equals, type Network, networks, opcodes, type P2TRPayment, type Payment, payments, type PublicKey, script, type Script, type Taptree, toXOnly, } from '@btc-vision/bitcoin'; import { DeploymentGenerator } from '../generators/builders/DeploymentGenerator.js'; import type { IChallengeSolution } from '../epoch/interfaces/IChallengeSolution.js'; import { type Feature, Features } from '../generators/Features.js'; export interface ContractAddressVerificationParams { readonly deployerPubKey: PublicKey; readonly contractSaltPubKey: Uint8Array; readonly originalSalt: Uint8Array; readonly bytecode: Uint8Array; readonly challenge: IChallengeSolution; readonly priorityFee: bigint; readonly features: Feature<Features>[]; readonly calldata?: Uint8Array; readonly network?: Network; } interface ScriptTreeComponents { readonly scriptTree: Taptree; readonly compiledTargetScript: Uint8Array; readonly network: Network; } export class TapscriptVerificator { private static readonly TAP_SCRIPT_VERSION: number = 192; public static getContractAddress( params: ContractAddressVerificationParams, ): string | undefined { const { scriptTree } = TapscriptVerificator.buildScriptTree(params); return TapscriptVerificator.generateAddressFromScript(params, scriptTree); } public static verifyControlBlock( params: ContractAddressVerificationParams, controlBlock: Uint8Array, ): boolean { const { scriptTree, compiledTargetScript, network } = TapscriptVerificator.buildScriptTree(params); const tapData = payments.p2tr({ internalPubkey: toXOnly(params.deployerPubKey), network: network, scriptTree: scriptTree, redeem: { output: compiledTargetScript as Script, redeemVersion: TapscriptVerificator.TAP_SCRIPT_VERSION, }, }); const witness = tapData.witness; if (!witness || witness.length === 0) { return false; } const requiredControlBlock: Uint8Array = witness[witness.length - 1] as Uint8Array; return equals(requiredControlBlock, controlBlock); } public static getContractSeed( deployerPubKey: Uint8Array, bytecode: Uint8Array, saltHash: Uint8Array, ): Uint8Array { const sha256OfBytecode: Uint8Array = bitCrypto.hash256(bytecode); const buf: Uint8Array = concat([deployerPubKey, saltHash, sha256OfBytecode]); return bitCrypto.hash256(buf); } public static generateAddressFromScript( params: ContractAddressVerificationParams, scriptTree: Taptree, ): string | undefined { const network = params.network || networks.bitcoin; const transactionData: Omit<P2TRPayment, 'name'> = { internalPubkey: toXOnly(params.deployerPubKey), network: network, scriptTree: scriptTree, }; const tx: Payment = payments.p2tr(transactionData); return tx.address; } private static buildScriptTree( params: ContractAddressVerificationParams, ): ScriptTreeComponents { const network = params.network || networks.bitcoin; const scriptBuilder: DeploymentGenerator = new DeploymentGenerator( params.deployerPubKey, toXOnly(params.contractSaltPubKey as PublicKey), network, ); const compiledTargetScript: Uint8Array = scriptBuilder.compile( params.bytecode, params.originalSalt, params.challenge, params.priorityFee, params.calldata, params.features, ); const lockLeafScript: Script = script.compile([ toXOnly(params.deployerPubKey), opcodes.OP_CHECKSIG, ]); const scriptTree: Taptree = [ { output: compiledTargetScript, version: TapscriptVerificator.TAP_SCRIPT_VERSION, }, { output: lockLeafScript, version: TapscriptVerificator.TAP_SCRIPT_VERSION, }, ]; return { scriptTree, compiledTargetScript, network }; } }