From 792c5d75098ab49b003d47e018482974c92f4342 Mon Sep 17 00:00:00 2001 From: PortlandHODL Date: Mon, 23 Feb 2026 07:40:56 +0000 Subject: [PATCH] [Refactor] Unify CLTV decode fn. --- .../shared/components/asm/asm.component.ts | 30 ++------------ .../pipes/asm-styler/asm-styler.pipe.ts | 32 ++------------- frontend/src/app/shared/script.utils.ts | 41 +++++++++++++++++++ 3 files changed, 47 insertions(+), 56 deletions(-) diff --git a/frontend/src/app/shared/components/asm/asm.component.ts b/frontend/src/app/shared/components/asm/asm.component.ts index 533499417..7e6a3a218 100644 --- a/frontend/src/app/shared/components/asm/asm.component.ts +++ b/frontend/src/app/shared/components/asm/asm.component.ts @@ -1,5 +1,6 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, SimpleChanges } from '@angular/core'; import { SigInfo, SighashLabels } from '@app/shared/transaction.utils'; +import { detectCltvTimestamps, formatCltvTimestamp } from '@app/shared/script.utils'; @Component({ selector: 'app-asm', @@ -39,25 +40,7 @@ export class AsmComponent { parseASM(): void { const allInstructions = this.asm.split('OP_').filter(instruction => instruction.trim() !== ''); - const cltvTimestamps: Map = new Map(); - allInstructions.forEach((instruction, index, arr) => { - const parts = instruction.split(' '); - const instructionName = parts[0]; - const args = parts.slice(1); - - if (instructionName === 'PUSHBYTES_4' && args.length > 0 && args[0].length === 8 && /^[0-9a-fA-F]+$/.test(args[0])) { - if (index + 1 < arr.length) { - const nextInstruction = arr[index + 1].split(' ')[0]; - if (nextInstruction === 'CLTV') { - const bytes = args[0].match(/.{2}/g) || []; - const littleEndianValue = bytes.reverse().join(''); - const timestamp = parseInt(littleEndianValue, 16); - cltvTimestamps.set(index, timestamp); - cltvTimestamps.set(index + 1, timestamp); - } - } - } - }); + const cltvTimestamps = detectCltvTimestamps(allInstructions); let instructions = allInstructions; if (this.crop && this.asm.length > this.crop) { @@ -110,14 +93,7 @@ export class AsmComponent { } formatTimestamp(timestamp: number): string { - if (timestamp < 500000000) { - return `Block height: ${timestamp}`; - } - if (timestamp > 4294967295) { - return `Invalid timestamp: ${timestamp}`; - } - const date = new Date(timestamp * 1000); - return date.toISOString().replace('T', ' ').replace(/\.\d{3}Z$/, ' UTC'); + return formatCltvTimestamp(timestamp); } readonly opcodeStyles: Map = new Map([ diff --git a/frontend/src/app/shared/pipes/asm-styler/asm-styler.pipe.ts b/frontend/src/app/shared/pipes/asm-styler/asm-styler.pipe.ts index 0cc55d0dd..9d3d6b41e 100644 --- a/frontend/src/app/shared/pipes/asm-styler/asm-styler.pipe.ts +++ b/frontend/src/app/shared/pipes/asm-styler/asm-styler.pipe.ts @@ -1,4 +1,5 @@ import { Pipe, PipeTransform } from '@angular/core'; +import { detectCltvTimestamps, formatCltvTimestamp } from '@app/shared/script.utils'; @Pipe({ name: 'asmStyler', @@ -11,27 +12,7 @@ export class AsmStylerPipe implements PipeTransform { let out = ''; let chars = -3; - // First pass: find CLTV timestamps - const cltvTimestamps: Map = new Map(); - for (let i = 0; i < instructions.length; i++) { - const instruction = instructions[i]; - const parts = instruction.split(' '); - const opcode = parts[0]; - const args = parts.slice(1); - - if (opcode === 'PUSHBYTES_4' && args.length > 0 && args[0].length === 8 && /^[0-9a-fA-F]+$/.test(args[0])) { - if (i + 1 < instructions.length) { - const nextOpcode = instructions[i + 1].split(' ')[0]; - if (nextOpcode === 'CLTV') { - const bytes = args[0].match(/.{2}/g) || []; - const littleEndianValue = bytes.reverse().join(''); - const timestamp = parseInt(littleEndianValue, 16); - cltvTimestamps.set(i, timestamp); - cltvTimestamps.set(i + 1, timestamp); - } - } - } - } + const cltvTimestamps = detectCltvTimestamps(instructions); for (let i = 0; i < instructions.length; i++) { const instruction = instructions[i]; @@ -349,14 +330,7 @@ export class AsmStylerPipe implements PipeTransform { } formatTimestamp(timestamp: number): string { - if (timestamp < 500000000) { - return `Block height: ${timestamp}`; - } - if (timestamp > 4294967295) { - return `Invalid timestamp: ${timestamp}`; - } - const date = new Date(timestamp * 1000); - return date.toISOString().replace('T', ' ').replace(/\.\d{3}Z$/, ' UTC'); + return formatCltvTimestamp(timestamp); } } diff --git a/frontend/src/app/shared/script.utils.ts b/frontend/src/app/shared/script.utils.ts index 5b86c4d64..170416e69 100644 --- a/frontend/src/app/shared/script.utils.ts +++ b/frontend/src/app/shared/script.utils.ts @@ -555,4 +555,45 @@ export function isPoint(pointHex: string): boolean { // If we square and it's equal, then it was a perfect root and valid point. return (y * y) % curveP === ySquared; } +} + +export function detectCltvTimestamps(instructions: string[]): Map { + const cltvTimestamps: Map = new Map(); + for (let i = 0; i < instructions.length; i++) { + const instruction = instructions[i]; + const parts = instruction.split(' '); + const opcode = parts[0]; + const args = parts.slice(1); + + const pushMatch = opcode.match(/^PUSHBYTES_([1-4])$/); + if (pushMatch && args.length > 0) { + const byteCount = parseInt(pushMatch[1], 10); + const expectedLength = byteCount * 2; + + if (args[0].length === expectedLength && /^[0-9a-fA-F]+$/.test(args[0])) { + if (i + 1 < instructions.length) { + const nextOpcode = instructions[i + 1].split(' ')[0]; + if (nextOpcode === 'CLTV') { + const bytes = args[0].match(/.{2}/g) || []; + const littleEndianValue = bytes.reverse().join(''); + const timestamp = parseInt(littleEndianValue, 16); + cltvTimestamps.set(i, timestamp); + cltvTimestamps.set(i + 1, timestamp); + } + } + } + } + } + return cltvTimestamps; +} + +export function formatCltvTimestamp(timestamp: number): string { + if (timestamp < 500000000) { + return `Block height: ${timestamp}`; + } + if (timestamp > 4294967295) { + return `Invalid timestamp: ${timestamp}`; + } + const date = new Date(timestamp * 1000); + return date.toISOString().replace('T', ' ').replace(/\.\d{3}Z$/, ' UTC'); } \ No newline at end of file