UNPKG

@bsv/sdk

Version:

BSV Blockchain Software Development Kit

235 lines 10.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const Signature_js_1 = __importDefault(require("./Signature.js")); const BigNumber_js_1 = __importDefault(require("./BigNumber.js")); const Hash = __importStar(require("./Hash.js")); const utils_js_1 = require("./utils.js"); const EMPTY_SCRIPT = new Uint8Array(0); class TransactionSignature extends Signature_js_1.default { /** * Formats the SIGHASH preimage for the targeted input, optionally using a cache to skip recomputing shared hash prefixes. * @param params - Context for the signing input plus transaction metadata. * @param params.cache - Optional cache storing previously computed `hashPrevouts`, `hashSequence`, or `hashOutputs*` values; it will be populated if present. */ static format(params) { return Array.from(this.formatBytes(params)); } /** * Formats the same SIGHASH preimage bytes as `format`, supporting the optional cache for hash reuse. * @param params - Context for the signing operation. * @param params.cache - Optional `SignatureHashCache` that may already contain hashed prefixes and is populated during formatting. * @returns Bytes for signing. */ static formatBytes(params) { const cache = params.cache; const currentInput = { sourceTXID: params.sourceTXID, sourceOutputIndex: params.sourceOutputIndex, sequence: params.inputSequence }; const inputs = [...params.otherInputs]; inputs.splice(params.inputIndex, 0, currentInput); const getPrevoutHash = () => { const writer = new utils_js_1.Writer(); for (const input of inputs) { if (typeof input.sourceTXID === 'undefined') { if (input.sourceTransaction == null) { throw new Error('Missing sourceTransaction for input'); } writer.write(input.sourceTransaction.hash()); } else { writer.writeReverse((0, utils_js_1.toArray)(input.sourceTXID, 'hex')); } writer.writeUInt32LE(input.sourceOutputIndex); } return Hash.hash256(writer.toUint8Array()); }; const getSequenceHash = () => { const writer = new utils_js_1.Writer(); for (const input of inputs) { const sequence = input.sequence ?? 0xffffffff; // Default to max sequence number writer.writeUInt32LE(sequence); } return Hash.hash256(writer.toUint8Array()); }; function getOutputsHash(outputIndex) { const writer = new utils_js_1.Writer(); if (typeof outputIndex === 'undefined') { for (const output of params.outputs) { const satoshis = output.satoshis ?? 0; // Default to 0 if undefined writer.writeUInt64LE(satoshis); const script = output.lockingScript?.toUint8Array() ?? EMPTY_SCRIPT; writer.writeVarIntNum(script.length); writer.write(script); } } else { const output = params.outputs[outputIndex]; if (output === undefined) { // ✅ Explicitly check for undefined throw new Error(`Output at index ${outputIndex} does not exist`); } const satoshis = output.satoshis ?? 0; // Default to 0 if undefined writer.writeUInt64LE(satoshis); const script = output.lockingScript?.toUint8Array() ?? EMPTY_SCRIPT; writer.writeVarIntNum(script.length); writer.write(script); } return Hash.hash256(writer.toUint8Array()); } let hashPrevouts = new Array(32).fill(0); let hashSequence = new Array(32).fill(0); let hashOutputs = new Array(32).fill(0); if ((params.scope & TransactionSignature.SIGHASH_ANYONECANPAY) === 0) { if (cache?.hashPrevouts != null) { hashPrevouts = cache.hashPrevouts; } else { hashPrevouts = getPrevoutHash(); if (cache != null) cache.hashPrevouts = hashPrevouts; } } if ((params.scope & TransactionSignature.SIGHASH_ANYONECANPAY) === 0 && (params.scope & 31) !== TransactionSignature.SIGHASH_SINGLE && (params.scope & 31) !== TransactionSignature.SIGHASH_NONE) { if (cache?.hashSequence != null) { hashSequence = cache.hashSequence; } else { hashSequence = getSequenceHash(); if (cache != null) cache.hashSequence = hashSequence; } } if ((params.scope & 31) !== TransactionSignature.SIGHASH_SINGLE && (params.scope & 31) !== TransactionSignature.SIGHASH_NONE) { if (cache?.hashOutputsAll != null) { hashOutputs = cache.hashOutputsAll; } else { hashOutputs = getOutputsHash(); if (cache != null) cache.hashOutputsAll = hashOutputs; } } else if ((params.scope & 31) === TransactionSignature.SIGHASH_SINGLE && params.inputIndex < params.outputs.length) { const key = params.inputIndex; const cachedSingle = cache?.hashOutputsSingle?.get(key); if (cachedSingle != null) { hashOutputs = cachedSingle; } else { hashOutputs = getOutputsHash(key); if (cache != null) { if (cache.hashOutputsSingle == null) cache.hashOutputsSingle = new Map(); cache.hashOutputsSingle.set(key, hashOutputs); } } } const writer = new utils_js_1.Writer(); // Version writer.writeInt32LE(params.transactionVersion); // Input prevouts/nSequence (none/all, depending on flags) writer.write(hashPrevouts); writer.write(hashSequence); // outpoint (32-byte hash + 4-byte little endian) writer.writeReverse((0, utils_js_1.toArray)(params.sourceTXID, 'hex')); writer.writeUInt32LE(params.sourceOutputIndex); // scriptCode of the input (serialized as scripts inside CTxOuts) const subscriptBin = params.subscript.toUint8Array(); writer.writeVarIntNum(subscriptBin.length); writer.write(subscriptBin); // value of the output spent by this input (8-byte little endian) writer.writeUInt64LE(params.sourceSatoshis); // nSequence of the input (4-byte little endian) const sequenceNumber = currentInput.sequence; writer.writeUInt32LE(sequenceNumber); // Outputs (none/one/all, depending on flags) writer.write(hashOutputs); // Locktime writer.writeUInt32LE(params.lockTime); // sighashType writer.writeUInt32LE(params.scope >>> 0); return writer.toUint8Array(); } // The format used in a tx static fromChecksigFormat(buf) { if (buf.length === 0) { // allow setting a "blank" signature const r = new BigNumber_js_1.default(1); const s = new BigNumber_js_1.default(1); const scope = 1; return new TransactionSignature(r, s, scope); } const scope = buf[buf.length - 1]; const derbuf = buf.slice(0, buf.length - 1); const tempSig = Signature_js_1.default.fromDER(derbuf); return new TransactionSignature(tempSig.r, tempSig.s, scope); } constructor(r, s, scope) { super(r, s); this.scope = scope; } /** * Compares to bitcoind's IsLowDERSignature * See also Ecdsa signature algorithm which enforces this. * See also Bip 62, "low S values in signatures" */ hasLowS() { if (this.s.ltn(1) || this.s.gt(new BigNumber_js_1.default('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0', 'hex'))) { return false; } return true; } toChecksigFormat() { const derbuf = this.toDER(); return [...derbuf, this.scope]; } } TransactionSignature.SIGHASH_ALL = 0x00000001; TransactionSignature.SIGHASH_NONE = 0x00000002; TransactionSignature.SIGHASH_SINGLE = 0x00000003; TransactionSignature.SIGHASH_FORKID = 0x00000040; TransactionSignature.SIGHASH_ANYONECANPAY = 0x00000080; exports.default = TransactionSignature; //# sourceMappingURL=TransactionSignature.js.map