@bsv/sdk
Version:
BSV Blockchain Software Development Kit
134 lines • 6.82 kB
JavaScript
;
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