From 16f8f0131c5ddfe545179863adbed41dda4db95b Mon Sep 17 00:00:00 2001 From: BlobMaster41 <96896824+BlobMaster41@users.noreply.github.com> Date: Sat, 18 May 2024 23:22:19 -0400 Subject: [PATCH] Added typed opcode definitions --- .gitignore | 3 +- src/ops.d.ts | 130 ++++++++++++++++++++++++++++++++++++++-- src/ops.js | 8 +-- src/script.js | 4 +- ts_src/ops.ts | 153 +++++++++++++++++++++++++++++++++++++++++++++-- ts_src/script.ts | 8 ++- 6 files changed, 286 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index 664a5520f..dcfd3c0a8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ test/*.js test/integration/*.js !test/ts-node-register.js docs -plugin \ No newline at end of file +plugin +.idea \ No newline at end of file diff --git a/src/ops.d.ts b/src/ops.d.ts index cda7a84a6..dd7c842bc 100644 --- a/src/ops.d.ts +++ b/src/ops.d.ts @@ -1,7 +1,127 @@ -declare const OPS: { - [key: string]: number; -}; -declare const REVERSE_OPS: { - [key: number]: string; +export interface Opcodes { + readonly OP_FALSE: number; + readonly OP_0: number; + readonly OP_PUSHDATA1: number; + readonly OP_PUSHDATA2: number; + readonly OP_PUSHDATA4: number; + readonly OP_1NEGATE: number; + readonly OP_RESERVED: number; + readonly OP_TRUE: number; + readonly OP_1: number; + readonly OP_2: number; + readonly OP_3: number; + readonly OP_4: number; + readonly OP_5: number; + readonly OP_6: number; + readonly OP_7: number; + readonly OP_8: number; + readonly OP_9: number; + readonly OP_10: number; + readonly OP_11: number; + readonly OP_12: number; + readonly OP_13: number; + readonly OP_14: number; + readonly OP_15: number; + readonly OP_16: number; + readonly OP_NOP: number; + readonly OP_VER: number; + readonly OP_IF: number; + readonly OP_NOTIF: number; + readonly OP_VERIF: number; + readonly OP_VERNOTIF: number; + readonly OP_ELSE: number; + readonly OP_ENDIF: number; + readonly OP_VERIFY: number; + readonly OP_RETURN: number; + readonly OP_TOALTSTACK: number; + readonly OP_FROMALTSTACK: number; + readonly OP_2DROP: number; + readonly OP_2DUP: number; + readonly OP_3DUP: number; + readonly OP_2OVER: number; + readonly OP_2ROT: number; + readonly OP_2SWAP: number; + readonly OP_IFDUP: number; + readonly OP_DEPTH: number; + readonly OP_DROP: number; + readonly OP_DUP: number; + readonly OP_NIP: number; + readonly OP_OVER: number; + readonly OP_PICK: number; + readonly OP_ROLL: number; + readonly OP_ROT: number; + readonly OP_SWAP: number; + readonly OP_TUCK: number; + readonly OP_CAT: number; + readonly OP_SUBSTR: number; + readonly OP_LEFT: number; + readonly OP_RIGHT: number; + readonly OP_SIZE: number; + readonly OP_INVERT: number; + readonly OP_AND: number; + readonly OP_OR: number; + readonly OP_XOR: number; + readonly OP_EQUAL: number; + readonly OP_EQUALVERIFY: number; + readonly OP_RESERVED1: number; + readonly OP_RESERVED2: number; + readonly OP_1ADD: number; + readonly OP_1SUB: number; + readonly OP_2MUL: number; + readonly OP_2DIV: number; + readonly OP_NEGATE: number; + readonly OP_ABS: number; + readonly OP_NOT: number; + readonly OP_0NOTEQUAL: number; + readonly OP_ADD: number; + readonly OP_SUB: number; + readonly OP_MUL: number; + readonly OP_DIV: number; + readonly OP_MOD: number; + readonly OP_LSHIFT: number; + readonly OP_RSHIFT: number; + readonly OP_BOOLAND: number; + readonly OP_BOOLOR: number; + readonly OP_NUMEQUAL: number; + readonly OP_NUMEQUALVERIFY: number; + readonly OP_NUMNOTEQUAL: number; + readonly OP_LESSTHAN: number; + readonly OP_GREATERTHAN: number; + readonly OP_LESSTHANOREQUAL: number; + readonly OP_GREATERTHANOREQUAL: number; + readonly OP_MIN: number; + readonly OP_MAX: number; + readonly OP_WITHIN: number; + readonly OP_RIPEMD160: number; + readonly OP_SHA1: number; + readonly OP_SHA256: number; + readonly OP_HASH160: number; + readonly OP_HASH256: number; + readonly OP_CODESEPARATOR: number; + readonly OP_CHECKSIG: number; + readonly OP_CHECKSIGVERIFY: number; + readonly OP_CHECKMULTISIG: number; + readonly OP_CHECKMULTISIGVERIFY: number; + readonly OP_CHECKLOCKTIMEVERIFY: number; + readonly OP_CHECKSEQUENCEVERIFY: number; + readonly OP_CHECKSIGADD: number; + readonly OP_NOP1: number; + readonly OP_NOP2: number; + readonly OP_NOP3: number; + readonly OP_NOP4: number; + readonly OP_NOP5: number; + readonly OP_NOP6: number; + readonly OP_NOP7: number; + readonly OP_NOP8: number; + readonly OP_NOP9: number; + readonly OP_NOP10: number; + readonly OP_PUBKEYHASH: number; + readonly OP_PUBKEY: number; + readonly OP_INVALIDOPCODE: number; +} +declare const OPS: Opcodes; +type ReverseOpcodes = { + [K in keyof Opcodes as Opcodes[K]]: K; }; +declare const REVERSE_OPS: ReverseOpcodes; export { OPS, REVERSE_OPS }; diff --git a/src/ops.js b/src/ops.js index 7853ad0f0..3bc9d861a 100644 --- a/src/ops.js +++ b/src/ops.js @@ -123,9 +123,7 @@ const OPS = { OP_INVALIDOPCODE: 255, }; exports.OPS = OPS; -const REVERSE_OPS = {}; +const REVERSE_OPS = Object.fromEntries( + Object.entries(OPS).map(([key, value]) => [value, key]), +); exports.REVERSE_OPS = REVERSE_OPS; -for (const op of Object.keys(OPS)) { - const code = OPS[op]; - REVERSE_OPS[code] = op; -} diff --git a/src/script.js b/src/script.js index c95f0cc67..5babb06a7 100644 --- a/src/script.js +++ b/src/script.js @@ -185,7 +185,9 @@ function fromASM(asm) { return compile( asm.split(' ').map(chunkStr => { // opcode? - if (ops_1.OPS[chunkStr] !== undefined) return ops_1.OPS[chunkStr]; + if (ops_1.OPS[chunkStr] !== undefined) { + return ops_1.OPS[chunkStr]; + } typeforce(types.Hex, chunkStr); // data! return Buffer.from(chunkStr, 'hex'); diff --git a/ts_src/ops.ts b/ts_src/ops.ts index dd8b1e6da..5fa62a81e 100644 --- a/ts_src/ops.ts +++ b/ts_src/ops.ts @@ -1,4 +1,143 @@ -const OPS: { [key: string]: number } = { +export interface Opcodes { + readonly OP_FALSE: number; + readonly OP_0: number; + readonly OP_PUSHDATA1: number; + readonly OP_PUSHDATA2: number; + readonly OP_PUSHDATA4: number; + readonly OP_1NEGATE: number; + readonly OP_RESERVED: number; + readonly OP_TRUE: number; + readonly OP_1: number; + readonly OP_2: number; + readonly OP_3: number; + readonly OP_4: number; + readonly OP_5: number; + readonly OP_6: number; + readonly OP_7: number; + readonly OP_8: number; + readonly OP_9: number; + readonly OP_10: number; + readonly OP_11: number; + readonly OP_12: number; + readonly OP_13: number; + readonly OP_14: number; + readonly OP_15: number; + readonly OP_16: number; + + // control + readonly OP_NOP: number; + readonly OP_VER: number; + readonly OP_IF: number; + readonly OP_NOTIF: number; + readonly OP_VERIF: number; + readonly OP_VERNOTIF: number; + readonly OP_ELSE: number; + readonly OP_ENDIF: number; + readonly OP_VERIFY: number; + readonly OP_RETURN: number; + + // stack ops + readonly OP_TOALTSTACK: number; + readonly OP_FROMALTSTACK: number; + readonly OP_2DROP: number; + readonly OP_2DUP: number; + readonly OP_3DUP: number; + readonly OP_2OVER: number; + readonly OP_2ROT: number; + readonly OP_2SWAP: number; + readonly OP_IFDUP: number; + readonly OP_DEPTH: number; + readonly OP_DROP: number; + readonly OP_DUP: number; + readonly OP_NIP: number; + readonly OP_OVER: number; + readonly OP_PICK: number; + readonly OP_ROLL: number; + readonly OP_ROT: number; + readonly OP_SWAP: number; + readonly OP_TUCK: number; + + // splice ops + readonly OP_CAT: number; + readonly OP_SUBSTR: number; + readonly OP_LEFT: number; + readonly OP_RIGHT: number; + readonly OP_SIZE: number; + + // bit logic + readonly OP_INVERT: number; + readonly OP_AND: number; + readonly OP_OR: number; + readonly OP_XOR: number; + readonly OP_EQUAL: number; + readonly OP_EQUALVERIFY: number; + readonly OP_RESERVED1: number; + readonly OP_RESERVED2: number; + + // numeric + readonly OP_1ADD: number; + readonly OP_1SUB: number; + readonly OP_2MUL: number; + readonly OP_2DIV: number; + readonly OP_NEGATE: number; + readonly OP_ABS: number; + readonly OP_NOT: number; + readonly OP_0NOTEQUAL: number; + readonly OP_ADD: number; + readonly OP_SUB: number; + readonly OP_MUL: number; + readonly OP_DIV: number; + readonly OP_MOD: number; + readonly OP_LSHIFT: number; + readonly OP_RSHIFT: number; + readonly OP_BOOLAND: number; + readonly OP_BOOLOR: number; + readonly OP_NUMEQUAL: number; + readonly OP_NUMEQUALVERIFY: number; + readonly OP_NUMNOTEQUAL: number; + readonly OP_LESSTHAN: number; + readonly OP_GREATERTHAN: number; + readonly OP_LESSTHANOREQUAL: number; + readonly OP_GREATERTHANOREQUAL: number; + readonly OP_MIN: number; + readonly OP_MAX: number; + readonly OP_WITHIN: number; + + // crypto + readonly OP_RIPEMD160: number; + readonly OP_SHA1: number; + readonly OP_SHA256: number; + readonly OP_HASH160: number; + readonly OP_HASH256: number; + readonly OP_CODESEPARATOR: number; + readonly OP_CHECKSIG: number; + readonly OP_CHECKSIGVERIFY: number; + readonly OP_CHECKMULTISIG: number; + readonly OP_CHECKMULTISIGVERIFY: number; + readonly OP_CHECKLOCKTIMEVERIFY: number; + readonly OP_CHECKSEQUENCEVERIFY: number; + + readonly OP_CHECKSIGADD: number; + + // expansion + readonly OP_NOP1: number; + readonly OP_NOP2: number; + readonly OP_NOP3: number; + readonly OP_NOP4: number; + readonly OP_NOP5: number; + readonly OP_NOP6: number; + readonly OP_NOP7: number; + readonly OP_NOP8: number; + readonly OP_NOP9: number; + readonly OP_NOP10: number; + + // template matching params + readonly OP_PUBKEYHASH: number; + readonly OP_PUBKEY: number; + readonly OP_INVALIDOPCODE: number; +} + +const OPS: Opcodes = { OP_FALSE: 0, OP_0: 0, OP_PUSHDATA1: 76, @@ -134,10 +273,12 @@ const OPS: { [key: string]: number } = { OP_INVALIDOPCODE: 255, }; -const REVERSE_OPS: { [key: number]: string } = {}; -for (const op of Object.keys(OPS)) { - const code = OPS[op]; - REVERSE_OPS[code] = op; -} +type ReverseOpcodes = { + [K in keyof Opcodes as Opcodes[K]]: K; +}; + +const REVERSE_OPS: ReverseOpcodes = Object.fromEntries( + Object.entries(OPS).map(([key, value]) => [value, key]) +) as ReverseOpcodes; export { OPS, REVERSE_OPS }; diff --git a/ts_src/script.ts b/ts_src/script.ts index c2a033569..11c8e8a21 100644 --- a/ts_src/script.ts +++ b/ts_src/script.ts @@ -3,7 +3,7 @@ * @packageDocumentation */ import * as bip66 from './bip66'; -import { OPS, REVERSE_OPS } from './ops'; +import { Opcodes, OPS, REVERSE_OPS } from './ops'; import { Stack } from './payments'; import * as pushdata from './push_data'; import * as scriptNumber from './script_number'; @@ -168,9 +168,11 @@ export function toASM(chunks: Buffer | Array): string { if (chunksIsBuffer(chunks)) { chunks = decompile(chunks) as Stack; } + if (!chunks) { throw new Error('Could not convert invalid chunks to ASM'); } + return chunks .map(chunk => { // data? @@ -197,7 +199,9 @@ export function fromASM(asm: string): Buffer { return compile( asm.split(' ').map(chunkStr => { // opcode? - if (OPS[chunkStr] !== undefined) return OPS[chunkStr]; + if (OPS[chunkStr as keyof Opcodes] !== undefined) { + return OPS[chunkStr as keyof Opcodes]; + } typeforce(types.Hex, chunkStr); // data!