UNPKG

@bsv/sdk

Version:

BSV Blockchain Software Development Kit

134 lines 6.82 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const OP_js_1 = __importDefault(require("../OP.js")); const utils_js_1 = require("../../primitives/utils.js"); const LockingScript_js_1 = __importDefault(require("../LockingScript.js")); const UnlockingScript_js_1 = __importDefault(require("../UnlockingScript.js")); const TransactionSignature_js_1 = __importDefault(require("../../primitives/TransactionSignature.js")); const Hash_js_1 = require("../../primitives/Hash.js"); function verifyTruthy(v) { if (v == null) throw new Error('must have value'); return v; } /** * P2PKH (Pay To Public Key Hash) class implementing ScriptTemplate. * * This class provides methods to create Pay To Public Key Hash locking and unlocking scripts, including the unlocking of P2PKH UTXOs with the private key. */ class P2PKH { /** * Creates a P2PKH locking script for a given public key hash or address string * * @param {number[] | string} pubkeyhash or address - An array or address representing the public key hash. * @returns {LockingScript} - A P2PKH locking script. */ lock(pubkeyhash) { let data; if (typeof pubkeyhash === 'string') { const hash = (0, utils_js_1.fromBase58Check)(pubkeyhash); if (hash.prefix[0] !== 0x00 && hash.prefix[0] !== 0x6f) { throw new Error('only P2PKH is supported'); } data = hash.data; } else { data = pubkeyhash; } if (data.length !== 20) { throw new Error('P2PKH hash length must be 20 bytes'); } return new LockingScript_js_1.default([ { op: OP_js_1.default.OP_DUP }, { op: OP_js_1.default.OP_HASH160 }, { op: data.length, data }, { op: OP_js_1.default.OP_EQUALVERIFY }, { op: OP_js_1.default.OP_CHECKSIG } ]); } /** * Creates a function that generates a P2PKH unlocking script along with its signature and length estimation. * * The returned object contains: * 1. `sign` - A function that, when invoked with a transaction and an input index, * produces an unlocking script suitable for a P2PKH locked output. * 2. `estimateLength` - A function that returns the estimated length of the unlocking script in bytes. * * @param {PrivateKey} privateKey - The private key used for signing the transaction. * @param {'all'|'none'|'single'} signOutputs - The signature scope for outputs. * @param {boolean} anyoneCanPay - Flag indicating if the signature allows for other inputs to be added later. * @param {number} sourceSatoshis - Optional. The amount being unlocked. Otherwise the input.sourceTransaction is required. * @param {Script} lockingScript - Optional. The lockinScript. Otherwise the input.sourceTransaction is required. * @returns {Object} - An object containing the `sign` and `estimateLength` functions. */ unlock(privateKey, signOutputs = 'all', anyoneCanPay = false, sourceSatoshis, lockingScript) { return { sign: async (tx, inputIndex) => { let signatureScope = TransactionSignature_js_1.default.SIGHASH_FORKID; if (signOutputs === 'all') { signatureScope |= TransactionSignature_js_1.default.SIGHASH_ALL; } if (signOutputs === 'none') { signatureScope |= TransactionSignature_js_1.default.SIGHASH_NONE; } if (signOutputs === 'single') { signatureScope |= TransactionSignature_js_1.default.SIGHASH_SINGLE; } if (anyoneCanPay) { signatureScope |= TransactionSignature_js_1.default.SIGHASH_ANYONECANPAY; } const input = tx.inputs[inputIndex]; const otherInputs = tx.inputs.filter((_, index) => index !== inputIndex); const sourceTXID = input.sourceTXID ?? input.sourceTransaction?.id('hex'); if (sourceTXID == null || sourceTXID === undefined) { throw new Error('The input sourceTXID or sourceTransaction is required for transaction signing.'); } if (sourceTXID === '') { throw new Error('The input sourceTXID or sourceTransaction is required for transaction signing.'); } sourceSatoshis || (sourceSatoshis = input.sourceTransaction?.outputs[input.sourceOutputIndex].satoshis); if (sourceSatoshis == null || sourceSatoshis === undefined) { throw new Error('The sourceSatoshis or input sourceTransaction is required for transaction signing.'); } lockingScript || (lockingScript = input.sourceTransaction?.outputs[input.sourceOutputIndex] .lockingScript); if (lockingScript == null) { throw new Error('The lockingScript or input sourceTransaction is required for transaction signing.'); } const preimage = TransactionSignature_js_1.default.format({ sourceTXID, sourceOutputIndex: verifyTruthy(input.sourceOutputIndex), sourceSatoshis, transactionVersion: tx.version, otherInputs, inputIndex, outputs: tx.outputs, inputSequence: verifyTruthy(input.sequence), subscript: lockingScript, lockTime: tx.lockTime, scope: signatureScope }); const rawSignature = privateKey.sign((0, Hash_js_1.sha256)(preimage)); const sig = new TransactionSignature_js_1.default(rawSignature.r, rawSignature.s, signatureScope); const sigForScript = sig.toChecksigFormat(); const pubkeyForScript = privateKey .toPublicKey() .encode(true); return new UnlockingScript_js_1.default([ { op: sigForScript.length, data: sigForScript }, { op: pubkeyForScript.length, data: pubkeyForScript } ]); }, estimateLength: async () => { // public key (1+33) + signature (1+73) // Note: We add 1 to each element's length because of the associated OP_PUSH return 108; } }; } } exports.default = P2PKH; //# sourceMappingURL=P2PKH.js.map