UNPKG

@bsv/sdk

Version:

BSV Blockchain Software Development Kit

124 lines 5.67 kB
import OP from '../OP.js'; import LockingScript from '../LockingScript.js'; import UnlockingScript from '../UnlockingScript.js'; import PrivateKey from '../../primitives/PrivateKey.js'; import TransactionSignature from '../../primitives/TransactionSignature.js'; import { sha256 } from '../../primitives/Hash.js'; import Script from '../Script.js'; /** * RPuzzle class implementing ScriptTemplate. * * This class provides methods to create R Puzzle and R Puzzle Hash locking and unlocking scripts, including the unlocking of UTXOs with the correct K value. */ export default class RPuzzle { type = 'raw'; /** * @constructor * Constructs an R Puzzle template instance for a given puzzle type * * @param {'raw'|'SHA1'|'SHA256'|'HASH256'|'RIPEMD160'|'HASH160'} type Denotes the type of puzzle to create */ constructor(type = 'raw') { this.type = type; } /** * Creates an R puzzle locking script for a given R value or R value hash. * * @param {number[]} value - An array representing the R value or its hash. * @returns {LockingScript} - An R puzzle locking script. */ lock(value) { const chunks = [ { op: OP.OP_OVER }, { op: OP.OP_3 }, { op: OP.OP_SPLIT }, { op: OP.OP_NIP }, { op: OP.OP_1 }, { op: OP.OP_SPLIT }, { op: OP.OP_SWAP }, { op: OP.OP_SPLIT }, { op: OP.OP_DROP } ]; if (this.type !== 'raw') { chunks.push({ op: OP['OP_' + this.type] }); } chunks.push({ op: value.length, data: value }); chunks.push({ op: OP.OP_EQUALVERIFY }); chunks.push({ op: OP.OP_CHECKSIG }); return new LockingScript(chunks); } /** * Creates a function that generates an R puzzle 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 an R puzzle locked output. * 2. `estimateLength` - A function that returns the estimated length of the unlocking script in bytes. * * @param {BigNumber} k — The K-value used to unlock the R-puzzle. * @param {PrivateKey} privateKey - The private key used for signing the transaction. If not provided, a random key will be generated. * @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. * @returns {Object} - An object containing the `sign` and `estimateLength` functions. */ unlock(k, privateKey, signOutputs = 'all', anyoneCanPay = false) { return { sign: async (tx, inputIndex) => { if (typeof privateKey === 'undefined') { privateKey = PrivateKey.fromRandom(); } let signatureScope = TransactionSignature.SIGHASH_FORKID; if (signOutputs === 'all') { signatureScope |= TransactionSignature.SIGHASH_ALL; } if (signOutputs === 'none') { signatureScope |= TransactionSignature.SIGHASH_NONE; } if (signOutputs === 'single') { signatureScope |= TransactionSignature.SIGHASH_SINGLE; } if (anyoneCanPay) { signatureScope |= TransactionSignature.SIGHASH_ANYONECANPAY; } const otherInputs = [...tx.inputs]; const [input] = otherInputs.splice(inputIndex, 1); if (typeof input.sourceTransaction !== 'object') { throw new Error('The source transaction is needed for transaction signing.'); } const preimage = TransactionSignature.format({ sourceTXID: input.sourceTransaction?.id('hex') ?? '', sourceOutputIndex: input.sourceOutputIndex ?? 0, sourceSatoshis: input.sourceTransaction?.outputs[input.sourceOutputIndex] ?.satoshis ?? 0, transactionVersion: tx.version, otherInputs, inputIndex, outputs: tx.outputs, inputSequence: input.sequence ?? 0xffffffff, subscript: input.sourceTransaction?.outputs[input.sourceOutputIndex] ?.lockingScript ?? new Script(), lockTime: tx.lockTime, scope: signatureScope }); const rawSignature = privateKey.sign(sha256(preimage), undefined, true, k); const sig = new TransactionSignature(rawSignature.r, rawSignature.s, signatureScope); const sigForScript = sig.toChecksigFormat(); const pubkeyForScript = privateKey .toPublicKey() .encode(true); return new UnlockingScript([ { 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; } }; } } //# sourceMappingURL=RPuzzle.js.map