Address & script parsing refactor
This commit is contained in:
@@ -145,8 +145,116 @@ for (let i = 187; i <= 255; i++) {
|
||||
|
||||
export { opcodes };
|
||||
|
||||
export type ScriptType = 'scriptpubkey'
|
||||
| 'scriptsig'
|
||||
| 'inner_witnessscript'
|
||||
| 'inner_redeemscript'
|
||||
|
||||
export interface ScriptTemplate {
|
||||
type: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export const ScriptTemplates: { [type: string]: (...args: any) => ScriptTemplate } = {
|
||||
liquid_peg_out: () => ({ type: 'liquid_peg_out', label: 'Liquid Peg Out' }),
|
||||
liquid_peg_out_emergency: () => ({ type: 'liquid_peg_out_emergency', label: 'Emergency Liquid Peg Out' }),
|
||||
ln_force_close: () => ({ type: 'ln_force_close', label: 'Lightning Force Close' }),
|
||||
ln_force_close_revoked: () => ({ type: 'ln_force_close_revoked', label: 'Revoked Lightning Force Close' }),
|
||||
ln_htlc: () => ({ type: 'ln_htlc', label: 'Lightning HTLC' }),
|
||||
ln_htlc_revoked: () => ({ type: 'ln_htlc_revoked', label: 'Revoked Lightning HTLC' }),
|
||||
ln_htlc_expired: () => ({ type: 'ln_htlc_expired', label: 'Expired Lightning HTLC' }),
|
||||
ln_anchor: () => ({ type: 'ln_anchor', label: 'Lightning Anchor' }),
|
||||
ln_anchor_swept: () => ({ type: 'ln_anchor_swept', label: 'Swept Lightning Anchor' }),
|
||||
multisig: (m: number, n: number) => ({ type: 'multisig', m, n, label: $localize`:@@address-label.multisig:Multisig ${m}:multisigM: of ${n}:multisigN:` }),
|
||||
};
|
||||
|
||||
export class ScriptInfo {
|
||||
type: ScriptType;
|
||||
scriptPath?: string;
|
||||
hex?: string;
|
||||
asm?: string;
|
||||
template: ScriptTemplate;
|
||||
|
||||
constructor(type: ScriptType, hex?: string, asm?: string, witness?: string[], scriptPath?: string) {
|
||||
this.type = type;
|
||||
this.hex = hex;
|
||||
this.asm = asm;
|
||||
if (scriptPath) {
|
||||
this.scriptPath = scriptPath;
|
||||
}
|
||||
if (this.asm) {
|
||||
this.template = detectScriptTemplate(this.type, this.asm, witness);
|
||||
}
|
||||
}
|
||||
|
||||
public clone(): ScriptInfo {
|
||||
return { ...this };
|
||||
}
|
||||
|
||||
get key(): string {
|
||||
return this.type + (this.scriptPath || '');
|
||||
}
|
||||
}
|
||||
|
||||
/** parses an inner_witnessscript + witness stack, and detects named script types */
|
||||
export function detectScriptTemplate(type: ScriptType, script_asm: string, witness?: string[]): ScriptTemplate | undefined {
|
||||
if (type === 'inner_witnessscript' && witness?.length) {
|
||||
if (script_asm.indexOf('OP_DEPTH OP_PUSHNUM_12 OP_EQUAL OP_IF OP_PUSHNUM_11') === 0 || script_asm.indexOf('OP_PUSHNUM_15 OP_CHECKMULTISIG OP_IFDUP OP_NOTIF OP_PUSHBYTES_2') === 1259) {
|
||||
if (witness.length > 11) {
|
||||
return ScriptTemplates.liquid_peg_out();
|
||||
} else {
|
||||
return ScriptTemplates.liquid_peg_out_emergency();
|
||||
}
|
||||
}
|
||||
|
||||
const topElement = witness[witness.length - 2];
|
||||
if (/^OP_IF OP_PUSHBYTES_33 \w{66} OP_ELSE OP_PUSH(NUM_\d+|BYTES_(1 \w{2}|2 \w{4})) OP_CSV OP_DROP OP_PUSHBYTES_33 \w{66} OP_ENDIF OP_CHECKSIG$/.test(script_asm)) {
|
||||
// https://github.com/lightning/bolts/blob/master/03-transactions.md#commitment-transaction-outputs
|
||||
if (topElement === '01') {
|
||||
// top element is '01' to get in the revocation path
|
||||
return ScriptTemplates.ln_force_close_revoked();
|
||||
} else {
|
||||
// top element is '', this is a delayed to_local output
|
||||
return ScriptTemplates.ln_force_close();
|
||||
}
|
||||
} else if (
|
||||
/^OP_DUP OP_HASH160 OP_PUSHBYTES_20 \w{40} OP_EQUAL OP_IF OP_CHECKSIG OP_ELSE OP_PUSHBYTES_33 \w{66} OP_SWAP OP_SIZE OP_PUSHBYTES_1 20 OP_EQUAL OP_NOTIF OP_DROP OP_PUSHNUM_2 OP_SWAP OP_PUSHBYTES_33 \w{66} OP_PUSHNUM_2 OP_CHECKMULTISIG OP_ELSE OP_HASH160 OP_PUSHBYTES_20 \w{40} OP_EQUALVERIFY OP_CHECKSIG OP_ENDIF (OP_PUSHNUM_1 OP_CSV OP_DROP |)OP_ENDIF$/.test(script_asm) ||
|
||||
/^OP_DUP OP_HASH160 OP_PUSHBYTES_20 \w{40} OP_EQUAL OP_IF OP_CHECKSIG OP_ELSE OP_PUSHBYTES_33 \w{66} OP_SWAP OP_SIZE OP_PUSHBYTES_1 20 OP_EQUAL OP_IF OP_HASH160 OP_PUSHBYTES_20 \w{40} OP_EQUALVERIFY OP_PUSHNUM_2 OP_SWAP OP_PUSHBYTES_33 \w{66} OP_PUSHNUM_2 OP_CHECKMULTISIG OP_ELSE OP_DROP OP_PUSHBYTES_3 \w{6} OP_CLTV OP_DROP OP_CHECKSIG OP_ENDIF (OP_PUSHNUM_1 OP_CSV OP_DROP |)OP_ENDIF$/.test(script_asm)
|
||||
) {
|
||||
// https://github.com/lightning/bolts/blob/master/03-transactions.md#offered-htlc-outputs
|
||||
// https://github.com/lightning/bolts/blob/master/03-transactions.md#received-htlc-outputs
|
||||
if (topElement.length === 66) {
|
||||
// top element is a public key
|
||||
return ScriptTemplates.ln_htlc_revoked();
|
||||
} else if (topElement) {
|
||||
// top element is a preimage
|
||||
return ScriptTemplates.ln_htlc();
|
||||
} else {
|
||||
// top element is '' to get in the expiry of the script
|
||||
return ScriptTemplates.ln_htlc_expired();
|
||||
}
|
||||
} else if (/^OP_PUSHBYTES_33 \w{66} OP_CHECKSIG OP_IFDUP OP_NOTIF OP_PUSHNUM_16 OP_CSV OP_ENDIF$/.test(script_asm)) {
|
||||
// https://github.com/lightning/bolts/blob/master/03-transactions.md#to_local_anchor-and-to_remote_anchor-output-option_anchors
|
||||
if (topElement) {
|
||||
// top element is a signature
|
||||
return ScriptTemplates.ln_anchor();
|
||||
} else {
|
||||
// top element is '', it has been swept after 16 blocks
|
||||
return ScriptTemplates.ln_anchor_swept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const multisig = parseMultisigScript(script_asm);
|
||||
if (multisig) {
|
||||
return ScriptTemplates.multisig(multisig.m, multisig.n);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** extracts m and n from a multisig script (asm), returns nothing if it is not a multisig script */
|
||||
export function parseMultisigScript(script: string): void | { m: number, n: number } {
|
||||
export function parseMultisigScript(script: string): undefined | { m: number, n: number } {
|
||||
if (!script) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user