UNPKG

libnexa-ts

Version:

A pure and powerful Nexa SDK library.

110 lines (93 loc) 5.36 kB
import PublicKey from "../../../keys/publickey"; import ValidationUtils from "../../../utils/validation.utils"; import { Opcode } from "../../script/opcode"; import Script from "../../script/script"; import Input from "./input"; import type PrivateKey from "../../../keys/privatekey"; import type TxSignature from "../txsignature"; import ScriptFactory from "../../script/script.factory"; import Hash from "../../../crypto/hash"; import BufferUtils from "../../../utils/buffer.utils"; import type { IInput } from "../interfaces"; export default class ScriptTemplateInput extends Input { public templateScript: Script; public constraintScript?: Script; public publicKey?: PublicKey; /** * Represents a special kind of input of generic ScriptTemplate kind. * * WARNING: this is a general case where the signature is similar to p2pkt and added to scriptSig as push signature data. * If you have complex smart contract, consider extending this class (or Input class) and implement the necessary logic, * or sign it manually. */ constructor(arg: IInput) { ValidationUtils.validateArgument(typeof arg?.templateData === 'object' && arg?.templateData !== null, 'Missing template object'); ValidationUtils.validateArgument(typeof arg?.output === 'object' && arg?.output !== null, 'Missing associated utxo'); let td = arg.templateData!; ValidationUtils.validateArgument(td.templateScript instanceof Script || typeof td.templateScript === 'string', 'Invalid template'); ValidationUtils.validateArgument(td.constraintScript instanceof Script || typeof td.constraintScript === 'string' || td.constraintScript == null, 'Invalid constraint'); ValidationUtils.validateArgument(td.publicKey instanceof PublicKey || typeof td.publicKey === 'string' || td.publicKey == null, 'Invalid pubkey'); super(arg); this.templateScript = typeof td.templateScript === 'string' ? Script.fromString(td.templateScript) : td.templateScript; this.constraintScript = typeof td.constraintScript === 'string' ? Script.fromString(td.constraintScript) : td.constraintScript; this.publicKey = typeof td.publicKey === 'string' ? PublicKey.fromString(td.publicKey) : td.publicKey; let templateHash = this.output!.scriptPubKey.getTemplateHash(); ValidationUtils.validateState(BufferUtils.isBuffer(templateHash) && BufferUtils.equals(templateHash, Hash.sha256ripemd160(this.templateScript.toBuffer())), "Provided template doesn't match to the provided output"); let constraintHash = this.output!.scriptPubKey.getConstraintHash(); if (BufferUtils.isBuffer(constraintHash)) { ValidationUtils.validateState(this.constraintScript != null && !this.constraintScript.isEmpty(), "Provided constraint must not be undefined or empty if constraint hash is provided in output"); ValidationUtils.validateState(BufferUtils.equals(constraintHash, Hash.sha256ripemd160(this.constraintScript!.toBuffer())), "Provided constraint doesn't match to the provided output"); } else { ValidationUtils.validateState(constraintHash === Opcode.OP_FALSE, "Provided output has invalid constraint hash, must be a hash or OP_FALSE"); ValidationUtils.validateState(this.constraintScript == null, "Provided constraint must be undefined if constraint hash is OP_FALSE"); } } public override toJSON = this.toObject; public override toObject(): IInput { let input = super.toObject() return { ...input, templateData: { templateScript: this.templateScript.toHex(), constraintScript: this.constraintScript?.toHex(), publicKey: this.publicKey?.toString() } } } public static override fromObject(obj: IInput): ScriptTemplateInput { ValidationUtils.validateArgument(typeof obj === 'object' && obj !== null, "obj"); return new ScriptTemplateInput(obj); } public override getSubscript(): Script { return this.templateScript; } public override canSign(privateKey: PrivateKey): boolean { return this.publicKey?.toString() === privateKey.publicKey.toString(); } public override isFullySigned(): boolean { return this.scriptSig.isScriptTemplateIn() && this.templateScript.equals(Script.fromBuffer(this.scriptSig.chunks[0].buf!)) && (!(this.constraintScript instanceof Script) || this.constraintScript.equals(Script.fromBuffer(this.scriptSig.chunks[1].buf!))); } public override addSignature(signature: TxSignature): this { let satisfier = Script.empty().add(signature.toTxSatisfier()); this.scriptSig = ScriptFactory.buildScriptTemplateIn(this.templateScript, this.constraintScript, satisfier); return this; } public override estimateSize(): number { if (this.isFullySigned()) { return super.estimateSize(); } let scriptSize = this._estimateScriptSize(); // type + outpoint + scriptlen + script + sequence + amount return 1 + 32 + (scriptSize < 253 ? 1 : 3) + scriptSize + 4 + 8; } protected _estimateScriptSize(): number { // here we calculate by template size, constraint size if not op_false, and satisfier size as sigsize (1 + 64) let s = Script.empty().add(this.templateScript.toBuffer()); if (this.constraintScript instanceof Script) { s.add(this.constraintScript.toBuffer()); } return s.toBuffer().length + 1 + 64; } }