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