Merge pull request #5817 from mempool/natsoni/tapscript-multisig-feedback

Tapscript multisig parsing feedback
This commit is contained in:
mononaut
2025-03-29 10:52:49 +08:00
committed by GitHub

View File

@@ -256,6 +256,11 @@ export function detectScriptTemplate(type: ScriptType, script_asm: string, witne
return ScriptTemplates.multisig(tapscriptMultisig.m, tapscriptMultisig.n); return ScriptTemplates.multisig(tapscriptMultisig.m, tapscriptMultisig.n);
} }
const tapscriptUnanimousMultisig = parseTapscriptUnanimousMultisig(script_asm);
if (tapscriptUnanimousMultisig) {
return ScriptTemplates.multisig(tapscriptUnanimousMultisig, tapscriptUnanimousMultisig);
}
return; return;
} }
@@ -310,11 +315,13 @@ export function parseTapscriptMultisig(script: string): undefined | { m: number,
} }
const ops = script.split(' '); const ops = script.split(' ');
// At minimum, one pubkey group (3 tokens) + m push + final opcode = 5 tokens // At minimum, 2 pubkey group (3 tokens) + m push + final opcode = 8 tokens
if (ops.length < 5) return; if (ops.length < 8) {
return;
}
const finalOp = ops.pop(); const finalOp = ops.pop();
if (finalOp !== 'OP_NUMEQUAL' && finalOp !== 'OP_GREATERTHANOREQUAL') { if (!['OP_NUMEQUAL', 'OP_NUMEQUALVERIFY', 'OP_GREATERTHANOREQUAL', 'OP_GREATERTHAN', 'OP_EQUAL', 'OP_EQUALVERIFY'].includes(finalOp)) {
return; return;
} }
@@ -329,6 +336,10 @@ export function parseTapscriptMultisig(script: string): undefined | { m: number,
return; return;
} }
if (finalOp === 'OP_GREATERTHAN') {
m += 1;
}
if (ops.length % 3 !== 0) { if (ops.length % 3 !== 0) {
return; return;
} }
@@ -360,6 +371,53 @@ export function parseTapscriptMultisig(script: string): undefined | { m: number,
return { m, n }; return { m, n };
} }
export function parseTapscriptUnanimousMultisig(script: string): undefined | number {
if (!script) {
return;
}
const ops = script.split(' ');
// At minimum, 2 pubkey group (3 tokens) = 6 tokens
if (ops.length < 6) {
return;
}
if (ops.length % 3 !== 0) {
return;
}
const n = ops.length / 3;
for (let i = 0; i < n; i++) {
const pushOp = ops.shift();
const pubkey = ops.shift();
const sigOp = ops.shift();
if (pushOp !== 'OP_PUSHBYTES_32') {
return;
}
if (!/^[0-9a-fA-F]{64}$/.test(pubkey)) {
return;
}
if (i < n - 1) {
if (sigOp !== 'OP_CHECKSIGVERIFY') {
return;
}
} else {
// Last opcode can be either CHECKSIG or CHECKSIGVERIFY
if (!(sigOp === 'OP_CHECKSIGVERIFY' || sigOp === 'OP_CHECKSIG')) {
return;
}
}
}
if (ops.length) {
return;
}
return n;
}
export function getVarIntLength(n: number): number { export function getVarIntLength(n: number): number {
if (n < 0xfd) { if (n < 0xfd) {
return 1; return 1;