From ac6a5b21b28c50d3d8d4af01011d915ca867ff8e Mon Sep 17 00:00:00 2001 From: jasonandjay <342690199@qq.com> Date: Fri, 10 May 2024 19:11:01 +0800 Subject: [PATCH] Revert "perfs: optimize script.decomplie return type" This reverts commit 6ec2822357e48cad2c9b1a31c0b97af0d6baad89. --- src/payments/p2sh.js | 2 +- src/payments/p2tr.js | 2 +- src/payments/p2wsh.js | 9 ++++++--- src/psbt.js | 6 +++--- src/psbt/psbtutils.js | 6 +++--- src/script.d.ts | 2 +- src/script.js | 5 +++-- test/script.spec.ts | 2 +- ts_src/payments/embed.ts | 7 ++++--- ts_src/payments/p2ms.ts | 4 ++-- ts_src/payments/p2sh.ts | 9 ++++++--- ts_src/payments/p2tr.ts | 2 +- ts_src/payments/p2wsh.ts | 9 ++++++--- ts_src/psbt.ts | 6 +++--- ts_src/psbt/psbtutils.ts | 6 +++--- ts_src/script.ts | 13 ++++++++----- ts_src/transaction.ts | 2 +- 17 files changed, 53 insertions(+), 39 deletions(-) diff --git a/src/payments/p2sh.js b/src/payments/p2sh.js index 7a7bbd243..1386966be 100644 --- a/src/payments/p2sh.js +++ b/src/payments/p2sh.js @@ -138,7 +138,7 @@ function p2sh(a, opts) { // is the redeem output empty/invalid? if (redeem.output) { const decompile = bscript.decompile(redeem.output); - if (decompile.length < 1) + if (!decompile || decompile.length < 1) throw new TypeError('Redeem.output too short'); if (redeem.output.byteLength > 520) throw new TypeError( diff --git a/src/payments/p2tr.js b/src/payments/p2tr.js index ccf803aa8..33fedb464 100644 --- a/src/payments/p2tr.js +++ b/src/payments/p2tr.js @@ -230,7 +230,7 @@ function p2tr(a, opts) { throw new TypeError('Redeem.redeemVersion and witness mismatch'); } if (a.redeem.output) { - if (!bscript.decompile(a.redeem.output).length) + if (bscript.decompile(a.redeem.output).length === 0) throw new TypeError('Redeem.output is invalid'); // output redeem is constructed from the witness if (o.redeem.output && !a.redeem.output.equals(o.redeem.output)) diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js index b45e3e6e3..a3422e50a 100644 --- a/src/payments/p2wsh.js +++ b/src/payments/p2wsh.js @@ -171,7 +171,8 @@ function p2wsh(a, opts) { // is the redeem output non-empty/valid? if (a.redeem.output) { const decompile = bscript.decompile(a.redeem.output); - if (!decompile.length) throw new TypeError('Redeem.output is invalid'); + if (!decompile || decompile.length < 1) + throw new TypeError('Redeem.output is invalid'); if (a.redeem.output.byteLength > 3600) throw new TypeError( 'Redeem.output unspendable if larger than 3600 bytes', @@ -197,7 +198,9 @@ function p2wsh(a, opts) { if ( (a.redeem.input && _rchunks().some(chunkHasUncompressedPubkey)) || (a.redeem.output && - bscript.decompile(a.redeem.output).some(chunkHasUncompressedPubkey)) + (bscript.decompile(a.redeem.output) || []).some( + chunkHasUncompressedPubkey, + )) ) { throw new TypeError( 'redeem.input or redeem.output contains uncompressed pubkey', @@ -210,7 +213,7 @@ function p2wsh(a, opts) { throw new TypeError('Witness and redeem.output mismatch'); if ( a.witness.some(chunkHasUncompressedPubkey) || - bscript.decompile(wScript).some(chunkHasUncompressedPubkey) + (bscript.decompile(wScript) || []).some(chunkHasUncompressedPubkey) ) throw new TypeError('Witness contains uncompressed pubkey'); } diff --git a/src/psbt.js b/src/psbt.js index b95bd8b9d..cc0e5c8ad 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -1636,7 +1636,7 @@ function pubkeyInOutput(pubkey, output, outputIndex, cache) { function redeemFromFinalScriptSig(finalScript) { if (!finalScript) return; const decomp = bscript.decompile(finalScript); - if (!decomp.length) return; + if (!decomp) return; const lastItem = decomp[decomp.length - 1]; if ( !Buffer.isBuffer(lastItem) || @@ -1645,7 +1645,7 @@ function redeemFromFinalScriptSig(finalScript) { ) return; const sDecomp = bscript.decompile(lastItem); - if (!sDecomp.length) return; + if (!sDecomp) return; return lastItem; } function redeemFromFinalWitnessScript(finalScript) { @@ -1654,7 +1654,7 @@ function redeemFromFinalWitnessScript(finalScript) { const lastItem = decomp[decomp.length - 1]; if (isPubkeyLike(lastItem)) return; const sDecomp = bscript.decompile(lastItem); - if (!sDecomp.length) return; + if (!sDecomp) return; return lastItem; } function compressPubkey(pubkey) { diff --git a/src/psbt/psbtutils.js b/src/psbt/psbtutils.js index bf1cbd2d0..ea5f1d719 100644 --- a/src/psbt/psbtutils.js +++ b/src/psbt/psbtutils.js @@ -79,7 +79,7 @@ function pubkeyPositionInScript(pubkey, script) { const pubkeyHash = (0, crypto_1.hash160)(pubkey); const pubkeyXOnly = pubkey.slice(1, 33); // slice before calling? const decompiled = bscript.decompile(script); - if (!decompiled.length) throw new Error('Unknown script error'); + if (decompiled === null) throw new Error('Unknown script error'); return decompiled.findIndex(element => { if (typeof element === 'number') return false; return ( @@ -173,10 +173,10 @@ function extractPartialSigs(input) { function getPsigsFromInputFinalScripts(input) { const scriptItems = !input.finalScriptSig ? [] - : bscript.decompile(input.finalScriptSig); + : bscript.decompile(input.finalScriptSig) || []; const witnessItems = !input.finalScriptWitness ? [] - : bscript.decompile(input.finalScriptWitness); + : bscript.decompile(input.finalScriptWitness) || []; return scriptItems .concat(witnessItems) .filter(item => { diff --git a/src/script.d.ts b/src/script.d.ts index e8563cf03..ffc8c89bb 100644 --- a/src/script.d.ts +++ b/src/script.d.ts @@ -14,7 +14,7 @@ export declare function countNonPushOnlyOPs(value: Stack): number; * @throws Error if the compilation fails. */ export declare function compile(chunks: Buffer | Stack): Buffer; -export declare function decompile(buffer: Buffer | Array): Stack; +export declare function decompile(buffer: Buffer | Array): Array | null; /** * Converts the given chunks into an ASM (Assembly) string representation. * If the chunks parameter is a Buffer, it will be decompiled into a Stack before conversion. diff --git a/src/script.js b/src/script.js index 996d640d0..be2805190 100644 --- a/src/script.js +++ b/src/script.js @@ -115,6 +115,7 @@ function compile(chunks) { } exports.compile = compile; function decompile(buffer) { + // TODO: remove me if (chunksIsArray(buffer)) return buffer; typeforce(types.Buffer, buffer); const chunks = []; @@ -125,10 +126,10 @@ function decompile(buffer) { if (opcode > ops_1.OPS.OP_0 && opcode <= ops_1.OPS.OP_PUSHDATA4) { const d = pushdata.decode(buffer, i); // did reading a pushDataInt fail? - if (d === null) return []; + if (d === null) return null; i += d.size; // attempt to read too much data? - if (i + d.number > buffer.length) return []; + if (i + d.number > buffer.length) return null; const data = buffer.slice(i, i + d.number); i += d.number; // decompile minimally diff --git a/test/script.spec.ts b/test/script.spec.ts index 570d1ccc0..d593ab17d 100644 --- a/test/script.spec.ts +++ b/test/script.spec.ts @@ -158,7 +158,7 @@ describe('script', () => { () => { const chunks = bscript.decompile(Buffer.from(f.script, 'hex')); - assert.deepStrictEqual(chunks, []); + assert.strictEqual(chunks, null); }, ); }); diff --git a/ts_src/payments/embed.ts b/ts_src/payments/embed.ts index 1acbab6ca..aef14e1b7 100644 --- a/ts_src/payments/embed.ts +++ b/ts_src/payments/embed.ts @@ -36,15 +36,16 @@ export function p2data(a: Payment, opts?: PaymentOpts): Payment { }); lazy.prop(o, 'data', () => { if (!a.output) return; - return bscript.decompile(a.output).slice(1); + return bscript.decompile(a.output)!.slice(1); }); // extended validation if (opts.validate) { if (a.output) { const chunks = bscript.decompile(a.output); - if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid'); - if (!chunks.slice(1).every(typef.Buffer)) + if (chunks![0] !== OPS.OP_RETURN) + throw new TypeError('Output is invalid'); + if (!chunks!.slice(1).every(typef.Buffer)) throw new TypeError('Output is invalid'); if (a.data && !stacksEqual(a.data, o.data as Buffer[])) diff --git a/ts_src/payments/p2ms.ts b/ts_src/payments/p2ms.ts index c5d7d7ac3..ffbf0155b 100644 --- a/ts_src/payments/p2ms.ts +++ b/ts_src/payments/p2ms.ts @@ -55,7 +55,7 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment { function decode(output: Buffer | Stack): void { if (decoded) return; decoded = true; - chunks = bscript.decompile(output); + chunks = bscript.decompile(output) as Stack; o.m = (chunks[0] as number) - OP_INT_BASE; o.n = (chunks[chunks.length - 2] as number) - OP_INT_BASE; o.pubkeys = chunks.slice(1, -2) as Buffer[]; @@ -90,7 +90,7 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment { }); lazy.prop(o, 'signatures', () => { if (!a.input) return; - return bscript.decompile(a.input).slice(1); + return bscript.decompile(a.input)!.slice(1); }); lazy.prop(o, 'input', () => { if (!a.signatures) return; diff --git a/ts_src/payments/p2sh.ts b/ts_src/payments/p2sh.ts index fbb449450..2f5f936c6 100644 --- a/ts_src/payments/p2sh.ts +++ b/ts_src/payments/p2sh.ts @@ -105,7 +105,10 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment { lazy.prop(o, 'input', () => { if (!a.redeem || !a.redeem.input || !a.redeem.output) return; return bscript.compile( - ([] as Stack).concat(bscript.decompile(a.redeem.input), a.redeem.output), + ([] as Stack).concat( + bscript.decompile(a.redeem.input) as Stack, + a.redeem.output, + ), ); }); lazy.prop(o, 'witness', () => { @@ -154,7 +157,7 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment { // is the redeem output empty/invalid? if (redeem.output) { const decompile = bscript.decompile(redeem.output); - if (decompile.length < 1) + if (!decompile || decompile.length < 1) throw new TypeError('Redeem.output too short'); if (redeem.output.byteLength > 520) throw new TypeError( @@ -179,7 +182,7 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment { if (hasInput && hasWitness) throw new TypeError('Input and witness provided'); if (hasInput) { - const richunks = bscript.decompile(redeem.input); + const richunks = bscript.decompile(redeem.input) as Stack; if (!bscript.isPushOnly(richunks)) throw new TypeError('Non push-only scriptSig'); } diff --git a/ts_src/payments/p2tr.ts b/ts_src/payments/p2tr.ts index ac7e8bb92..c1140b715 100644 --- a/ts_src/payments/p2tr.ts +++ b/ts_src/payments/p2tr.ts @@ -252,7 +252,7 @@ export function p2tr(a: Payment, opts?: PaymentOpts): Payment { } if (a.redeem.output) { - if (!bscript.decompile(a.redeem.output).length) + if (bscript.decompile(a.redeem.output)!.length === 0) throw new TypeError('Redeem.output is invalid'); // output redeem is constructed from the witness diff --git a/ts_src/payments/p2wsh.ts b/ts_src/payments/p2wsh.ts index 1456a7008..bd9339277 100644 --- a/ts_src/payments/p2wsh.ts +++ b/ts_src/payments/p2wsh.ts @@ -183,7 +183,8 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment { // is the redeem output non-empty/valid? if (a.redeem.output) { const decompile = bscript.decompile(a.redeem.output); - if (!decompile.length) throw new TypeError('Redeem.output is invalid'); + if (!decompile || decompile.length < 1) + throw new TypeError('Redeem.output is invalid'); if (a.redeem.output.byteLength > 3600) throw new TypeError( 'Redeem.output unspendable if larger than 3600 bytes', @@ -211,7 +212,9 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment { if ( (a.redeem.input && _rchunks().some(chunkHasUncompressedPubkey)) || (a.redeem.output && - bscript.decompile(a.redeem.output).some(chunkHasUncompressedPubkey)) + (bscript.decompile(a.redeem.output) || []).some( + chunkHasUncompressedPubkey, + )) ) { throw new TypeError( 'redeem.input or redeem.output contains uncompressed pubkey', @@ -225,7 +228,7 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment { throw new TypeError('Witness and redeem.output mismatch'); if ( a.witness.some(chunkHasUncompressedPubkey) || - bscript.decompile(wScript).some(chunkHasUncompressedPubkey) + (bscript.decompile(wScript) || []).some(chunkHasUncompressedPubkey) ) throw new TypeError('Witness contains uncompressed pubkey'); } diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 2ed52418f..b2aa36f1f 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -2148,7 +2148,7 @@ function redeemFromFinalScriptSig( ): Buffer | undefined { if (!finalScript) return; const decomp = bscript.decompile(finalScript); - if (!decomp.length) return; + if (!decomp) return; const lastItem = decomp[decomp.length - 1]; if ( !Buffer.isBuffer(lastItem) || @@ -2157,7 +2157,7 @@ function redeemFromFinalScriptSig( ) return; const sDecomp = bscript.decompile(lastItem); - if (!sDecomp.length) return; + if (!sDecomp) return; return lastItem; } @@ -2169,7 +2169,7 @@ function redeemFromFinalWitnessScript( const lastItem = decomp[decomp.length - 1]; if (isPubkeyLike(lastItem)) return; const sDecomp = bscript.decompile(lastItem); - if (!sDecomp.length) return; + if (!sDecomp) return; return lastItem; } diff --git a/ts_src/psbt/psbtutils.ts b/ts_src/psbt/psbtutils.ts index 3b2fcedaf..19cf33e5b 100644 --- a/ts_src/psbt/psbtutils.ts +++ b/ts_src/psbt/psbtutils.ts @@ -75,7 +75,7 @@ export function pubkeyPositionInScript(pubkey: Buffer, script: Buffer): number { const pubkeyXOnly = pubkey.slice(1, 33); // slice before calling? const decompiled = bscript.decompile(script); - if (!decompiled.length) throw new Error('Unknown script error'); + if (decompiled === null) throw new Error('Unknown script error'); return decompiled.findIndex(element => { if (typeof element === 'number') return false; @@ -178,10 +178,10 @@ function extractPartialSigs(input: PsbtInput): Buffer[] { function getPsigsFromInputFinalScripts(input: PsbtInput): PartialSig[] { const scriptItems = !input.finalScriptSig ? [] - : bscript.decompile(input.finalScriptSig); + : bscript.decompile(input.finalScriptSig) || []; const witnessItems = !input.finalScriptWitness ? [] - : bscript.decompile(input.finalScriptWitness); + : bscript.decompile(input.finalScriptWitness) || []; return scriptItems .concat(witnessItems) .filter(item => { diff --git a/ts_src/script.ts b/ts_src/script.ts index f858c467d..54ee98fde 100644 --- a/ts_src/script.ts +++ b/ts_src/script.ts @@ -111,7 +111,10 @@ export function compile(chunks: Buffer | Stack): Buffer { return buffer; } -export function decompile(buffer: Buffer | Array): Stack { +export function decompile( + buffer: Buffer | Array, +): Array | null { + // TODO: remove me if (chunksIsArray(buffer)) return buffer; typeforce(types.Buffer, buffer); @@ -127,11 +130,11 @@ export function decompile(buffer: Buffer | Array): Stack { const d = pushdata.decode(buffer, i); // did reading a pushDataInt fail? - if (d === null) return []; + if (d === null) return null; i += d.size; // attempt to read too much data? - if (i + d.number > buffer.length) return []; + if (i + d.number > buffer.length) return null; const data = buffer.slice(i, i + d.number); i += d.number; @@ -163,7 +166,7 @@ export function decompile(buffer: Buffer | Array): Stack { */ export function toASM(chunks: Buffer | Array): string { if (chunksIsBuffer(chunks)) { - chunks = decompile(chunks); + chunks = decompile(chunks) as Stack; } return chunks @@ -208,7 +211,7 @@ export function fromASM(asm: string): Buffer { * @returns The stack of buffers. */ export function toStack(chunks: Buffer | Array): Buffer[] { - chunks = decompile(chunks); + chunks = decompile(chunks) as Stack; typeforce(isPushOnly, chunks); return chunks.map(op => { diff --git a/ts_src/transaction.ts b/ts_src/transaction.ts index 784f8b01c..665583ec5 100644 --- a/ts_src/transaction.ts +++ b/ts_src/transaction.ts @@ -284,7 +284,7 @@ export class Transaction { // ignore OP_CODESEPARATOR const ourScript = bscript.compile( - bscript.decompile(prevOutScript).filter(x => { + bscript.decompile(prevOutScript)!.filter(x => { return x !== opcodes.OP_CODESEPARATOR; }), );