From 3e32cb34a5b7b5054ee9fea23148abcd607814de Mon Sep 17 00:00:00 2001 From: ayman Date: Thu, 28 Mar 2024 17:00:46 +0530 Subject: [PATCH] draft: pwpkh signature --- ...5d1a66c56a9cc41d9fc1530a7ef3e4fdeaee7.json | 80 ++-- src/features/encoding/errors.ts | 6 + src/features/encoding/serializer.ts | 401 +++++++++++------- src/features/encoding/witnessTemplates.ts | 4 + src/features/transaction/components/input.ts | 32 ++ src/features/transaction/components/output.ts | 21 + .../transaction/components/transaction.ts | 108 +++++ src/features/transaction/index.ts | 6 +- src/features/transaction/input.ts | 34 -- src/features/transaction/output.ts | 21 - src/features/transaction/transaction.ts | 108 ----- src/features/validator/signature.ts | 180 +++++--- src/index.ts | 139 ++++-- src/utils.ts | 51 ++- 14 files changed, 703 insertions(+), 488 deletions(-) create mode 100644 src/features/encoding/errors.ts create mode 100644 src/features/encoding/witnessTemplates.ts create mode 100644 src/features/transaction/components/input.ts create mode 100644 src/features/transaction/components/output.ts create mode 100644 src/features/transaction/components/transaction.ts delete mode 100644 src/features/transaction/input.ts delete mode 100644 src/features/transaction/output.ts delete mode 100644 src/features/transaction/transaction.ts diff --git a/mempool/0a768ce65115e0bf1b4fd4b3b1c5d1a66c56a9cc41d9fc1530a7ef3e4fdeaee7.json b/mempool/0a768ce65115e0bf1b4fd4b3b1c5d1a66c56a9cc41d9fc1530a7ef3e4fdeaee7.json index 0fe43e0..9dd7b74 100644 --- a/mempool/0a768ce65115e0bf1b4fd4b3b1c5d1a66c56a9cc41d9fc1530a7ef3e4fdeaee7.json +++ b/mempool/0a768ce65115e0bf1b4fd4b3b1c5d1a66c56a9cc41d9fc1530a7ef3e4fdeaee7.json @@ -1,41 +1,41 @@ { - "version": 1, - "locktime": 0, - "vin": [ - { - "txid": "f3898029a8699bd8b71dc6f20e7ec2762a945a30d6a9f18034ce92a9d6cdd26c", - "vout": 1, - "prevout": { - "scriptpubkey": "00144639af50cc9b5fcc4fc09644c0140078b2d2356c", - "scriptpubkey_asm": "OP_0 OP_PUSHBYTES_20 4639af50cc9b5fcc4fc09644c0140078b2d2356c", - "scriptpubkey_type": "v0_p2wpkh", - "scriptpubkey_address": "bc1qgcu675xvnd0ucn7qjezvq9qq0zedydtv07pqxg", - "value": 338586 - }, - "scriptsig": "", - "scriptsig_asm": "", - "witness": [ - "30450221008f05cd9bc6679ad3b1e5316370a71779d587d9ff9ceaebb9dfa97288e6abf7fb02203951f6ea925965c7719039984929bac73e7934c86237dc40d72459a694f378ec01", - "02bb0543170d1752bfb0d173724effdc58a708c53d5154e56364e6cb19fd993a73" - ], - "is_coinbase": false, - "sequence": 4294967293 - } - ], - "vout": [ - { - "scriptpubkey": "5120b09182bc1fc70f752d4d885ec8e68156325b75881de16bb1b5d3e3bf53ff01fd", - "scriptpubkey_asm": "OP_PUSHNUM_1 OP_PUSHBYTES_32 b09182bc1fc70f752d4d885ec8e68156325b75881de16bb1b5d3e3bf53ff01fd", - "scriptpubkey_type": "v1_p2tr", - "scriptpubkey_address": "bc1pkzgc90qlcu8h2t2d3p0v3e5p2ce9kavgrhskhvd4603m75llq87s2eyxqn", - "value": 2576 - }, - { - "scriptpubkey": "00144639af50cc9b5fcc4fc09644c0140078b2d2356c", - "scriptpubkey_asm": "OP_0 OP_PUSHBYTES_20 4639af50cc9b5fcc4fc09644c0140078b2d2356c", - "scriptpubkey_type": "v0_p2wpkh", - "scriptpubkey_address": "bc1qgcu675xvnd0ucn7qjezvq9qq0zedydtv07pqxg", - "value": 333840 - } - ] -} \ No newline at end of file + "version": 1, + "locktime": 0, + "vin": [ + { + "txid": "f3898029a8699bd8b71dc6f20e7ec2762a945a30d6a9f18034ce92a9d6cdd26c", + "vout": 1, + "prevout": { + "scriptpubkey": "00144639af50cc9b5fcc4fc09644c0140078b2d2356c", + "scriptpubkey_asm": "OP_0 OP_PUSHBYTES_20 4639af50cc9b5fcc4fc09644c0140078b2d2356c", + "scriptpubkey_type": "v0_p2wpkh", + "scriptpubkey_address": "bc1qgcu675xvnd0ucn7qjezvq9qq0zedydtv07pqxg", + "value": 338586 + }, + "scriptsig": "", + "scriptsig_asm": "", + "witness": [ + "30450221008f05cd9bc6679ad3b1e5316370a71779d587d9ff9ceaebb9dfa97288e6abf7fb02203951f6ea925965c7719039984929bac73e7934c86237dc40d72459a694f378ec01", + "02bb0543170d1752bfb0d173724effdc58a708c53d5154e56364e6cb19fd993a73" + ], + "is_coinbase": false, + "sequence": 4294967293 + } + ], + "vout": [ + { + "scriptpubkey": "5120b09182bc1fc70f752d4d885ec8e68156325b75881de16bb1b5d3e3bf53ff01fd", + "scriptpubkey_asm": "OP_PUSHNUM_1 OP_PUSHBYTES_32 b09182bc1fc70f752d4d885ec8e68156325b75881de16bb1b5d3e3bf53ff01fd", + "scriptpubkey_type": "v1_p2tr", + "scriptpubkey_address": "bc1pkzgc90qlcu8h2t2d3p0v3e5p2ce9kavgrhskhvd4603m75llq87s2eyxqn", + "value": 2576 + }, + { + "scriptpubkey": "00144639af50cc9b5fcc4fc09644c0140078b2d2356c", + "scriptpubkey_asm": "OP_0 OP_PUSHBYTES_20 4639af50cc9b5fcc4fc09644c0140078b2d2356c", + "scriptpubkey_type": "v0_p2wpkh", + "scriptpubkey_address": "bc1qgcu675xvnd0ucn7qjezvq9qq0zedydtv07pqxg", + "value": 333840 + } + ] +} diff --git a/src/features/encoding/errors.ts b/src/features/encoding/errors.ts new file mode 100644 index 0000000..48adf63 --- /dev/null +++ b/src/features/encoding/errors.ts @@ -0,0 +1,6 @@ +export const Errors = { + INVALID_WITNESS: "INVALID WITNESS", + INVALID_VOUT: "INVALID VOUT", + PUBKEY_NOT_FOUND: "PUBKEY NOT FOUND", + INVALID_PREVOUT: "INVALID PREVOUT", +}; diff --git a/src/features/encoding/serializer.ts b/src/features/encoding/serializer.ts index 4faab49..7cb769e 100644 --- a/src/features/encoding/serializer.ts +++ b/src/features/encoding/serializer.ts @@ -1,128 +1,210 @@ import { Transaction, Input, Output } from "../transaction"; -import { reversify, sha256 } from "../../utils"; +import { hash160, hash256, reversify, sha256 } from "../../utils"; import { compactSize } from "./compactSize"; import { getNextNBytes } from "../script/utils"; +import { SigHash } from "../../types"; +import { Errors } from "./errors"; +import { TEMPLATES } from "./witnessTemplates"; export class Serializer { - static serializeTx(tx: Transaction) { - let serializedTx = ""; + static serializeTx(tx: Transaction) { + let serializedTx = ""; - const version = Buffer.alloc(4); - version.writeInt16LE(tx.version, 0); - serializedTx += version.toString("hex"); + const version = Buffer.alloc(4); + version.writeInt16LE(tx.version, 0); + serializedTx += version.toString("hex"); - const numInputs = compactSize(BigInt(tx.vin.length)); - serializedTx += numInputs.toString("hex"); + const numInputs = compactSize(BigInt(tx.vin.length)); + serializedTx += numInputs.toString("hex"); - for (let i = 0; i < tx.vin.length; i++) { - serializedTx += Serializer.serializeInput(tx.vin[i]); - } + for (let i = 0; i < tx.vin.length; i++) { + serializedTx += Serializer.serializeInput(tx.vin[i]); + } - const numOutputs = compactSize(BigInt(tx.vout.length)); - serializedTx += numOutputs.toString("hex"); - for (let i = 0; i < tx.vout.length; i++) { - serializedTx += Serializer.serializeOutput(tx.vout[i]); - } + const numOutputs = compactSize(BigInt(tx.vout.length)); + serializedTx += numOutputs.toString("hex"); + for (let i = 0; i < tx.vout.length; i++) { + serializedTx += Serializer.serializeOutput(tx.vout[i]); + } - const locktime = Buffer.alloc(4); - locktime.writeUint32LE(tx.locktime, 0); - serializedTx += locktime.toString("hex"); + const locktime = Buffer.alloc(4); + locktime.writeUint32LE(tx.locktime, 0); + serializedTx += locktime.toString("hex"); - return serializedTx; - } + return serializedTx; + } - static serializeWTx(tx: Transaction) { - let serializedWTx = ""; + static serializeWTx(tx: Transaction) { + let serializedWTx = ""; - const version = Buffer.alloc(4); - version.writeInt16LE(tx.version, 0); - serializedWTx += version.toString("hex"); + const version = Buffer.alloc(4); + version.writeInt16LE(tx.version, 0); + serializedWTx += version.toString("hex"); - serializedWTx += "0001"; + serializedWTx += "0001"; - const numInputs = compactSize(BigInt(tx.vin.length)); - serializedWTx += numInputs.toString("hex"); + const numInputs = compactSize(BigInt(tx.vin.length)); + serializedWTx += numInputs.toString("hex"); - for (let i = 0; i < tx.vin.length; i++) { - serializedWTx += Serializer.serializeInput(tx.vin[i]); - } + for (let i = 0; i < tx.vin.length; i++) { + serializedWTx += Serializer.serializeInput(tx.vin[i]); + } - const numOutputs = compactSize(BigInt(tx.vout.length)); - serializedWTx += numOutputs.toString("hex"); + const numOutputs = compactSize(BigInt(tx.vout.length)); + serializedWTx += numOutputs.toString("hex"); - for (let i = 0; i < tx.vout.length; i++) { - serializedWTx += Serializer.serializeOutput(tx.vout[i]); - } + for (let i = 0; i < tx.vout.length; i++) { + serializedWTx += Serializer.serializeOutput(tx.vout[i]); + } - for (let i = 0; i < tx.vin.length; i++) { - const input = tx.vin[i]; - if ( - !input.witness || - (input && input.witness !== undefined && input.witness.length === 0) - ) { - serializedWTx += compactSize(BigInt(0)).toString("hex"); - } else { - serializedWTx += compactSize(BigInt(input.witness.length)).toString( - "hex" - ); - for (const witness of input.witness) { - serializedWTx += compactSize(BigInt(witness.length / 2)).toString( - "hex" - ); - serializedWTx += witness; + for (let i = 0; i < tx.vin.length; i++) { + const input = tx.vin[i]; + if ( + !input.witness || + (input && + input.witness !== undefined && + input.witness.length === 0) + ) { + serializedWTx += compactSize(BigInt(0)).toString("hex"); + } else { + serializedWTx += compactSize( + BigInt(input.witness.length) + ).toString("hex"); + for (const witness of input.witness) { + serializedWTx += compactSize( + BigInt(witness.length / 2) + ).toString("hex"); + serializedWTx += witness; + } + } } - } + + const locktime = Buffer.alloc(4); + locktime.writeUint32LE(tx.locktime, 0); + serializedWTx += locktime.toString("hex"); + + return serializedWTx; } - const locktime = Buffer.alloc(4); - locktime.writeUint32LE(tx.locktime, 0); - serializedWTx += locktime.toString("hex"); + static serializeInput(input: Input) { + let serializedInput = ""; - return serializedWTx; - } + const txHash = reversify(input.txid); + serializedInput += txHash; - static serializeInput(input: Input) { - let serializedInput = ""; + const outputIndex = Buffer.alloc(4); + outputIndex.writeUint32LE(input.vout, 0); + serializedInput += outputIndex.toString("hex"); - const txHash = reversify(input.txid); - serializedInput += txHash; + const scriptSig = input.scriptsig; + const scriptSigSize = compactSize(BigInt(scriptSig.length / 2)); + const sequence = Buffer.alloc(4); + sequence.writeUint32LE(input.sequence, 0); - const outputIndex = Buffer.alloc(4); - outputIndex.writeUint32LE(input.vout, 0); - serializedInput += outputIndex.toString("hex"); + serializedInput += scriptSigSize.toString("hex"); + serializedInput += scriptSig; + serializedInput += sequence.toString("hex"); - const scriptSig = input.scriptsig; - const scriptSigSize = compactSize(BigInt(scriptSig.length / 2)); - const sequence = Buffer.alloc(4); - sequence.writeUint32LE(input.sequence, 0); + return serializedInput; + } - serializedInput += scriptSigSize.toString("hex"); - serializedInput += scriptSig; - serializedInput += sequence.toString("hex"); + static serializeOutput(output: Output) { + let serializedOutput = ""; + const amount = Buffer.alloc(8); + amount.writeBigInt64LE(BigInt(output.value), 0); - return serializedInput; - } + serializedOutput += amount.toString("hex"); + serializedOutput += compactSize( + BigInt(output.scriptpubkey.length / 2) + ).toString("hex"); + serializedOutput += output.scriptpubkey; - static serializeOutput(output: Output) { - let serializedOutput = ""; - const amount = Buffer.alloc(8); - amount.writeBigInt64LE(BigInt(output.value), 0); + return serializedOutput; + } - serializedOutput += amount.toString("hex"); - serializedOutput += compactSize( - BigInt(output.scriptpubkey.length / 2) - ).toString("hex"); - serializedOutput += output.scriptpubkey; + static serializeWitness(tx: Transaction, index: number, sighash: SigHash) { + //pubkey is tx.input[vout].witness[1] for p2wpkh + if (sighash !== SigHash.ALL) throw new Error("unsupported sighash"); + let serializedWTx = ""; + + const version = Buffer.alloc(4); + version.writeInt32LE(tx.version, 0); + + let prevouts = ""; + let sequences = ""; + for (const input of tx.vin) { + prevouts += input.txid; + const prevoutVout = Buffer.alloc(4); + prevoutVout.writeUint32LE(input.vout, 0); + prevouts += prevoutVout.toString("hex"); + + const sequence = Buffer.alloc(4); + sequence.writeUint32LE(input.sequence, 0); + sequences += sequence.toString("hex"); + } - return serializedOutput; - } -} + const hashPrevouts = hash256(prevouts); + const hashSequence = hash256(sequences); -const weight = (val: Buffer | string, multiplier: number) => { - return val instanceof Buffer - ? (val.toString("hex").length / 2) * multiplier - : (val.length / 2) * multiplier; -}; + let outputs = ""; + for (const output of tx.vout) { + const outputAmount = Buffer.alloc(8); + outputAmount.writeBigInt64LE(BigInt(output.value), 0); + outputs += outputAmount.toString("hex"); + outputs += output.scriptpubkey; + } + + const hashOutputs = hash256(outputs); + + const input = tx.vin[index]; + if (!input) throw new Error(Errors.INVALID_VOUT); + // const outpoint = input.txid; + const vout = Buffer.alloc(4); + vout.writeUint32LE(input.vout, 0); + const outpoint = input.txid + vout.toString("hex"); + + if (!input.witness) throw new Error(Errors.INVALID_WITNESS); + if (!input.witness[1]) throw new Error(Errors.PUBKEY_NOT_FOUND); + const scriptCode = TEMPLATES.P2WPKH(hash160(input.witness[1])); + + if (!input.prevout) throw new Error(Errors.INVALID_PREVOUT); + const amount = Buffer.alloc(8); + amount.writeBigInt64LE(BigInt(input.prevout.value), 0); + + const nSequence = Buffer.alloc(4); + nSequence.writeUint32LE(input.sequence, 0); + + const nLocktime = Buffer.alloc(4); + nLocktime.writeUint32LE(tx.locktime, 0); + + const hashcode = Buffer.alloc(4); + hashcode.writeUint16LE(0x01, 0); + + console.log("version: ", version.toString("hex")); + console.log("hashPrevouts: ", hashPrevouts, "prevouts: ", prevouts); + console.log("hashSequence: ", hashSequence, "sequences: ", sequences); + console.log("outpoint: ", outpoint); + console.log("scriptCode: ", scriptCode); + console.log("amount: ", amount.toString("hex")); + console.log("nSequence: ", nSequence.toString("hex")); + console.log("hashOutputs: ", hashOutputs, "outputs: ", outputs); + console.log("nLocktime: ", nLocktime.toString("hex")); + + serializedWTx += version.toString("hex"); + serializedWTx += hashPrevouts; + serializedWTx += hashSequence; + serializedWTx += outpoint; + serializedWTx += scriptCode; + serializedWTx += amount.toString("hex"); + serializedWTx += nSequence.toString("hex"); + serializedWTx += hashOutputs; + serializedWTx += nLocktime.toString("hex"); + serializedWTx += hashcode.toString("hex"); + + console.log(serializedWTx); + return serializedWTx; + } +} // export const outputSerializer = (outTx: Output) => { // const amount = Buffer.alloc(8); @@ -263,76 +345,77 @@ const weight = (val: Buffer | string, multiplier: number) => { // }; export const extractRSFromSignature = (derEncodedSignature: string) => { - let derEncodingScheme, - signatureLength, - r, - s, - rLength, - sLength, - rest, - prefix, - rPadding = "", - sPadding = ""; - [derEncodingScheme, rest] = getNextNBytes(derEncodedSignature, 1); - if (derEncodingScheme !== "30") - throw new Error("Invalid DER encoding scheme"); - [signatureLength, rest] = getNextNBytes(rest, 1); - [prefix, rest] = getNextNBytes(rest, 1); - [rLength, rest] = getNextNBytes(rest, 1); - [r, rest] = getNextNBytes(rest, parseInt(rLength, 16)); - if (r.length === 66) [rPadding, r] = getNextNBytes(r, 1); //account for 00 padding - - [prefix, rest] = getNextNBytes(rest, 1); - [sLength, rest] = getNextNBytes(rest, 1); - [s, rest] = getNextNBytes(rest, parseInt(sLength, 16)); - if (s.length === 66) [sPadding, s] = getNextNBytes(s, 1); //account for 00 padding - - return r.padStart(64, "0") + s.padStart(64, "0"); + let derEncodingScheme, + signatureLength, + r, + s, + rLength, + sLength, + rest, + prefix, + rPadding = "", + sPadding = ""; + [derEncodingScheme, rest] = getNextNBytes(derEncodedSignature, 1); + if (derEncodingScheme !== "30") + throw new Error("Invalid DER encoding scheme"); + [signatureLength, rest] = getNextNBytes(rest, 1); + [prefix, rest] = getNextNBytes(rest, 1); + [rLength, rest] = getNextNBytes(rest, 1); + [r, rest] = getNextNBytes(rest, parseInt(rLength, 16)); + if (r.length === 66) [rPadding, r] = getNextNBytes(r, 1); //account for 00 padding + + [prefix, rest] = getNextNBytes(rest, 1); + [sLength, rest] = getNextNBytes(rest, 1); + [s, rest] = getNextNBytes(rest, parseInt(sLength, 16)); + if (s.length === 66) [sPadding, s] = getNextNBytes(s, 1); //account for 00 padding + + return r.padStart(64, "0") + s.padStart(64, "0"); }; const tx = { - version: 1, - locktime: 0, - vin: [ - { - txid: "3b7dc918e5671037effad7848727da3d3bf302b05f5ded9bec89449460473bbb", - vout: 16, - prevout: { - scriptpubkey: "0014f8d9f2203c6f0773983392a487d45c0c818f9573", - scriptpubkey_asm: - "OP_0 OP_PUSHBYTES_20 f8d9f2203c6f0773983392a487d45c0c818f9573", - scriptpubkey_type: "v0_p2wpkh", - scriptpubkey_address: "bc1qlrvlygpudurh8xpnj2jg04zupjqcl9tnk5np40", - value: 37079526, - }, - scriptsig: "", - scriptsig_asm: "", - witness: [ - "30440220780ad409b4d13eb1882aaf2e7a53a206734aa302279d6859e254a7f0a7633556022011fd0cbdf5d4374513ef60f850b7059c6a093ab9e46beb002505b7cba0623cf301", - "022bf8c45da789f695d59f93983c813ec205203056e19ec5d3fbefa809af67e2ec", - ], - is_coinbase: false, - sequence: 4294967295, - }, - ], - vout: [ - { - scriptpubkey: "76a9146085312a9c500ff9cc35b571b0a1e5efb7fb9f1688ac", - scriptpubkey_asm: - "OP_DUP OP_HASH160 OP_PUSHBYTES_20 6085312a9c500ff9cc35b571b0a1e5efb7fb9f16 OP_EQUALVERIFY OP_CHECKSIG", - scriptpubkey_type: "p2pkh", - scriptpubkey_address: "19oMRmCWMYuhnP5W61ABrjjxHc6RphZh11", - value: 100000, - }, - { - scriptpubkey: "0014ad4cc1cc859c57477bf90d0f944360d90a3998bf", - scriptpubkey_asm: - "OP_0 OP_PUSHBYTES_20 ad4cc1cc859c57477bf90d0f944360d90a3998bf", - scriptpubkey_type: "v0_p2wpkh", - scriptpubkey_address: "bc1q44xvrny9n3t5w7lep58egsmqmy9rnx9lt6u0tc", - value: 36977942, - }, - ], + version: 1, + locktime: 0, + vin: [ + { + txid: "3b7dc918e5671037effad7848727da3d3bf302b05f5ded9bec89449460473bbb", + vout: 16, + prevout: { + scriptpubkey: "0014f8d9f2203c6f0773983392a487d45c0c818f9573", + scriptpubkey_asm: + "OP_0 OP_PUSHBYTES_20 f8d9f2203c6f0773983392a487d45c0c818f9573", + scriptpubkey_type: "v0_p2wpkh", + scriptpubkey_address: + "bc1qlrvlygpudurh8xpnj2jg04zupjqcl9tnk5np40", + value: 37079526, + }, + scriptsig: "", + scriptsig_asm: "", + witness: [ + "30440220780ad409b4d13eb1882aaf2e7a53a206734aa302279d6859e254a7f0a7633556022011fd0cbdf5d4374513ef60f850b7059c6a093ab9e46beb002505b7cba0623cf301", + "022bf8c45da789f695d59f93983c813ec205203056e19ec5d3fbefa809af67e2ec", + ], + is_coinbase: false, + sequence: 4294967295, + }, + ], + vout: [ + { + scriptpubkey: "76a9146085312a9c500ff9cc35b571b0a1e5efb7fb9f1688ac", + scriptpubkey_asm: + "OP_DUP OP_HASH160 OP_PUSHBYTES_20 6085312a9c500ff9cc35b571b0a1e5efb7fb9f16 OP_EQUALVERIFY OP_CHECKSIG", + scriptpubkey_type: "p2pkh", + scriptpubkey_address: "19oMRmCWMYuhnP5W61ABrjjxHc6RphZh11", + value: 100000, + }, + { + scriptpubkey: "0014ad4cc1cc859c57477bf90d0f944360d90a3998bf", + scriptpubkey_asm: + "OP_0 OP_PUSHBYTES_20 ad4cc1cc859c57477bf90d0f944360d90a3998bf", + scriptpubkey_type: "v0_p2wpkh", + scriptpubkey_address: "bc1q44xvrny9n3t5w7lep58egsmqmy9rnx9lt6u0tc", + value: 36977942, + }, + ], } as unknown as Transaction; // const { serializedTx, serializedWTx } = txSerializer( diff --git a/src/features/encoding/witnessTemplates.ts b/src/features/encoding/witnessTemplates.ts new file mode 100644 index 0000000..1cb6449 --- /dev/null +++ b/src/features/encoding/witnessTemplates.ts @@ -0,0 +1,4 @@ +// const p2wpkhTemplate = (pubkey: string) => `1976a914${pubkey}88ac`; +export const TEMPLATES = { + P2WPKH: (pubkeyhash: string) => `1976a914${pubkeyhash}88ac`, +}; diff --git a/src/features/transaction/components/input.ts b/src/features/transaction/components/input.ts new file mode 100644 index 0000000..05a230f --- /dev/null +++ b/src/features/transaction/components/input.ts @@ -0,0 +1,32 @@ +import { Serializer } from "../../encoding/serializer"; +import { TxIn, TxOut } from "../types"; + +export class Input { + txid: string; + vout: number; + prevout: TxOut | null; //null in the case of coinbase + scriptsig: string; + scriptsig_asm: string; + witness?: string[]; + is_coinbase: boolean; + sequence: number; + inner_redeemscript_asm: string | undefined; + inner_witnessscript_asm: string | undefined; + + constructor(inputConfig: TxIn) { + this.txid = inputConfig.txid; + this.vout = inputConfig.vout; + this.prevout = inputConfig.prevout; + this.scriptsig = inputConfig.scriptsig; + this.scriptsig_asm = inputConfig.scriptsig_asm; + this.witness = inputConfig.witness; + this.is_coinbase = inputConfig.is_coinbase; + this.sequence = inputConfig.sequence; + this.inner_redeemscript_asm = inputConfig.inner_redeemscript_asm; + this.inner_witnessscript_asm = inputConfig.inner_witnessscript_asm; + } + + serialize() { + return Serializer.serializeInput(this); + } +} diff --git a/src/features/transaction/components/output.ts b/src/features/transaction/components/output.ts new file mode 100644 index 0000000..2abfdf6 --- /dev/null +++ b/src/features/transaction/components/output.ts @@ -0,0 +1,21 @@ +import { Serializer } from "../../encoding/serializer"; +import { TxOut } from "../types"; + +export class Output { + scriptpubkey: string; + scriptpubkey_asm: string; + scriptpubkey_type: string; + scriptpubkey_address?: string; + value: number; + constructor(outputConfig: TxOut) { + this.scriptpubkey = outputConfig.scriptpubkey; + this.scriptpubkey_asm = outputConfig.scriptpubkey_asm; + this.scriptpubkey_type = outputConfig.scriptpubkey_type; + this.scriptpubkey_address = outputConfig.scriptpubkey_address; + this.value = outputConfig.value; + } + + serialize() { + return Serializer.serializeOutput(this); + } +} diff --git a/src/features/transaction/components/transaction.ts b/src/features/transaction/components/transaction.ts new file mode 100644 index 0000000..78ae7d7 --- /dev/null +++ b/src/features/transaction/components/transaction.ts @@ -0,0 +1,108 @@ +import { Input } from "./input"; +import { Output } from "./output"; +import { Serializer } from "../../encoding/serializer"; +import { reversify, sha256 } from "../../../utils"; +import { SigHash } from "../../../types"; +import cloneDeep from "lodash.clonedeep"; +import { Errors } from "../errors"; +import { calculateWeight } from "../utils"; + +//depending on static serializer methods, instead use dependency injection +export class Transaction { + private _txid: string | undefined; //cache these values + private _wtxid: string | undefined; + private _serializedTx: string | undefined; + private _serializedWTx: string | undefined; + private _weight: number | undefined; + version: number; + locktime: number; + vin: Input[] = []; + vout: Output[] = []; + isSegwit = false; + + constructor(version: number, locktime: number) { + this.version = version; + this.locktime = locktime; + } + + addInput(input: Input) { + this.resetState(); + if (input.witness && input.witness.length > 0) this.isSegwit = true; + this.vin.push(input); + } + + addOutput(output: Output) { + this.resetState(); + this.vout.push(output); + } + + signWith(inputIndex: number, sighash: SigHash) { + const txCopy = cloneDeep(this); + let hashcode = Buffer.alloc(4); + switch (sighash) { + case SigHash.ALL: + for (let i = 0; i < txCopy.vin.length; i++) { + hashcode.writeUint32LE(1, 0); + if (i === inputIndex) { + const input = txCopy.vin[i].prevout; + if (!input) throw new Error(Errors.INVALID_INPUT); + txCopy.vin[i].scriptsig = input.scriptpubkey; + } else { + txCopy.vin[i].scriptsig = ""; + } + } + break; + case SigHash.ALL_ANYONECANPAY: + hashcode.writeUint32LE(0x81, 0); + txCopy.vin = [txCopy.vin[inputIndex]]; + const input = txCopy.vin[0].prevout; + if (!input) throw new Error(Errors.INVALID_INPUT); + txCopy.vin[0].scriptsig = input.scriptpubkey; + break; + } + + return txCopy.serializedTx + hashcode.toString("hex"); + } + + get serializedTx() { + if (this._serializedTx) return this._serializedTx; + this._serializedTx = Serializer.serializeTx(this); + return this._serializedTx; + } + + get serializedWTx() { + if (this._serializedWTx) return this._serializedWTx; + this._serializedWTx = Serializer.serializeWTx(this); + return this._serializedWTx; + } + + get txid() { + if (this._txid) return this._txid; + const txid = reversify(sha256(sha256(this.serializedTx))); + this._txid = txid; + return this._txid; + } + + get wtxid() { + if (!this.isSegwit) return this.txid; + if (this._wtxid) return this._wtxid; + const wtxid = reversify(sha256(sha256(this.serializedWTx))); + this._wtxid = wtxid; + return this._wtxid; + } + + get weight() { + if (this._weight) return this._weight; + const weight = calculateWeight(this, this.isSegwit); + this._weight = weight; + return this._weight; + } + + private resetState() { + //remove cache as it gets invalidated when tx gets changed such as when you're adding input or outputs; + this._txid = undefined; + this._wtxid = undefined; + this._serializedTx = undefined; + this._serializedWTx = undefined; + } +} diff --git a/src/features/transaction/index.ts b/src/features/transaction/index.ts index b029fca..bec368d 100644 --- a/src/features/transaction/index.ts +++ b/src/features/transaction/index.ts @@ -1,4 +1,4 @@ -export * from "./transaction"; -export * from "./input"; -export * from "./output"; +export * from "./components/transaction"; +export * from "./components/input"; +export * from "./components/output"; export * from "./types"; diff --git a/src/features/transaction/input.ts b/src/features/transaction/input.ts deleted file mode 100644 index 3d65b41..0000000 --- a/src/features/transaction/input.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { stringify } from "querystring"; -import { Output } from "./output"; -import { Serializer } from "../encoding/serializer"; -import { TxIn, TxOut } from "./types"; - -export class Input { - txid: string; - vout: number; - prevout: TxOut | null; //null in the case of coinbase - scriptsig: string; - scriptsig_asm: string; - witness?: string[]; - is_coinbase: boolean; - sequence: number; - inner_redeemscript_asm: string | undefined; - inner_witnessscript_asm: string | undefined; - - constructor(inputConfig: TxIn) { - this.txid = inputConfig.txid; - this.vout = inputConfig.vout; - this.prevout = inputConfig.prevout; - this.scriptsig = inputConfig.scriptsig; - this.scriptsig_asm = inputConfig.scriptsig_asm; - this.witness = inputConfig.witness; - this.is_coinbase = inputConfig.is_coinbase; - this.sequence = inputConfig.sequence; - this.inner_redeemscript_asm = inputConfig.inner_redeemscript_asm; - this.inner_witnessscript_asm = inputConfig.inner_witnessscript_asm; - } - - serialize() { - return Serializer.serializeInput(this); - } -} diff --git a/src/features/transaction/output.ts b/src/features/transaction/output.ts deleted file mode 100644 index 3f53b00..0000000 --- a/src/features/transaction/output.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Serializer } from "../encoding/serializer"; -import { TxOut } from "./types"; - -export class Output { - scriptpubkey: string; - scriptpubkey_asm: string; - scriptpubkey_type: string; - scriptpubkey_address?: string; - value: number; - constructor(outputConfig: TxOut) { - this.scriptpubkey = outputConfig.scriptpubkey; - this.scriptpubkey_asm = outputConfig.scriptpubkey_asm; - this.scriptpubkey_type = outputConfig.scriptpubkey_type; - this.scriptpubkey_address = outputConfig.scriptpubkey_address; - this.value = outputConfig.value; - } - - serialize() { - return Serializer.serializeOutput(this); - } -} diff --git a/src/features/transaction/transaction.ts b/src/features/transaction/transaction.ts deleted file mode 100644 index 74cf982..0000000 --- a/src/features/transaction/transaction.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { Input } from "./input"; -import { Output } from "./output"; -import { Serializer } from "../encoding/serializer"; -import { reversify, sha256 } from "../../utils"; -import { SigHash } from "../../types"; -import cloneDeep from "lodash.clonedeep"; -import { Errors } from "./errors"; -import { calculateWeight } from "./utils"; - -//depending on static serializer methods, instead use dependency injection -export class Transaction { - private _txid: string | undefined; //cache these values - private _wtxid: string | undefined; - private _serializedTx: string | undefined; - private _serializedWTx: string | undefined; - private _weight: number | undefined; - version: number; - locktime: number; - vin: Input[] = []; - vout: Output[] = []; - isSegwit = false; - - constructor(version: number, locktime: number) { - this.version = version; - this.locktime = locktime; - } - - addInput(input: Input) { - this.resetState(); - if (input.witness && input.witness.length > 0) this.isSegwit = true; - this.vin.push(input); - } - - addOutput(output: Output) { - this.resetState(); - this.vout.push(output); - } - - signWith(inputIndex: number, sighash: SigHash) { - const txCopy = cloneDeep(this); - let hashcode = Buffer.alloc(4); - switch (sighash) { - case SigHash.ALL: - for (let i = 0; i < txCopy.vin.length; i++) { - hashcode.writeUint32LE(1, 0); - if (i === inputIndex) { - const input = txCopy.vin[i].prevout; - if (!input) throw new Error(Errors.INVALID_INPUT); - txCopy.vin[i].scriptsig = input.scriptpubkey; - } else { - txCopy.vin[i].scriptsig = ""; - } - } - break; - case SigHash.ALL_ANYONECANPAY: - hashcode.writeUint32LE(0x81, 0); - txCopy.vin = [txCopy.vin[inputIndex]]; - const input = txCopy.vin[0].prevout; - if (!input) throw new Error(Errors.INVALID_INPUT); - txCopy.vin[0].scriptsig = input.scriptpubkey; - break; - } - - return txCopy.serializedTx + hashcode.toString("hex"); - } - - get serializedTx() { - if (this._serializedTx) return this._serializedTx; - this._serializedTx = Serializer.serializeTx(this); - return this._serializedTx; - } - - get serializedWTx() { - if (this._serializedWTx) return this._serializedWTx; - this._serializedWTx = Serializer.serializeWTx(this); - return this._serializedWTx; - } - - get txid() { - if (this._txid) return this._txid; - const txid = reversify(sha256(sha256(this.serializedTx))); - this._txid = txid; - return this._txid; - } - - get wtxid() { - if (!this.isSegwit) return this.txid; - if (this._wtxid) return this._wtxid; - const wtxid = reversify(sha256(sha256(this.serializedWTx))); - this._wtxid = wtxid; - return this._wtxid; - } - - get weight() { - if (this._weight) return this._weight; - const weight = calculateWeight(this, this.isSegwit); - this._weight = weight; - return this._weight; - } - - private resetState() { - //remove cache as it gets invalidated when tx gets changed such as when you're adding input or outputs; - this._txid = undefined; - this._wtxid = undefined; - this._serializedTx = undefined; - this._serializedWTx = undefined; - } -} diff --git a/src/features/validator/signature.ts b/src/features/validator/signature.ts index e1ce455..f8c19b2 100644 --- a/src/features/validator/signature.ts +++ b/src/features/validator/signature.ts @@ -5,73 +5,139 @@ import * as asn1js from "asn1js"; import { ECPairFactory } from "ecpair"; import * as ecc from "tiny-secp256k1"; +import { Serializer } from "../encoding/serializer"; const ECPair = ECPairFactory(ecc); const removePadding = (r: string, s: string) => { - //remove der padding if length === 66 - if (r.length === 66) { - r = r.slice(2); - } - if (s.length === 66) { - s = s.slice(2); - } - - //add padding to make it 32 bytes for ecpair - r = r.padStart(64, "0"); - s = s.padStart(64, "0"); - - return r + s; + //remove der padding if length === 66 + if (r.length === 66) { + r = r.slice(2); + } + if (s.length === 66) { + s = s.slice(2); + } + + //add padding to make it 32 bytes for ecpair + r = r.padStart(64, "0"); + s = s.padStart(64, "0"); + + return r + s; }; const extractSighashFromSignature = (signature: string) => { - return signature.slice(signature.length - 2) as SigHash; + return signature.slice(signature.length - 2) as SigHash; }; export const signatureValidator = (tx: Transaction): boolean => { - let pubkey = ""; - let derEncodedSignature = ""; - for (let i = 0; i < tx.vin.length; i++) { - const input = tx.vin[i]; - if (!input.prevout) return true; //there is nothing to validate - switch (input.prevout.scriptpubkey_type) { - case TransactionType.P2PKH: - const asmTokens = input.scriptsig_asm.split(" "); - derEncodedSignature = asmTokens[1]; - pubkey = asmTokens[asmTokens.length - 1]; - const sighash = extractSighashFromSignature(derEncodedSignature); - const asn1 = asn1js.fromBER(Buffer.from(derEncodedSignature, "hex")); - - let r = Buffer.from( - (asn1.result.valueBlock as any).value[0].valueBlock.valueHexView - ).toString("hex"); - let s = Buffer.from( - (asn1.result.valueBlock as any).value[1].valueBlock.valueHexView - ).toString("hex"); - - const signature = removePadding(r, s); - - const ecpair = ECPair.fromPublicKey(Buffer.from(pubkey, "hex")); - - const msg = tx.signWith(i, sighash); - const hash = sha256(sha256(msg)); - const valid = ecpair.verify( - Buffer.from(hash, "hex"), - Buffer.from(signature, "hex") - ); - if (!valid) return false; - break; - - case TransactionType.P2WPKH: - if (!input.witness) return false; - derEncodedSignature = input.witness[0]; - pubkey = input.witness[1]; - const pubkeyHash = hash160(pubkey); - const pubkeyInScript = input.prevout.scriptpubkey.slice(4); - - if (pubkeyHash !== pubkeyInScript) return false; + for (let i = 0; i < tx.vin.length; i++) { + const input = tx.vin[i]; + if (!input.prevout) return true; //there is nothing to validate + switch (input.prevout.scriptpubkey_type) { + case TransactionType.P2PKH: { + const asmTokens = input.scriptsig_asm.split(" "); + const derEncodedSignature = asmTokens[1]; + const pubkey = asmTokens[asmTokens.length - 1]; + const sighash = + extractSighashFromSignature(derEncodedSignature); + const asn1 = asn1js.fromBER( + Buffer.from(derEncodedSignature, "hex") + ); + + let r = Buffer.from( + (asn1.result.valueBlock as any).value[0].valueBlock + .valueHexView + ).toString("hex"); + let s = Buffer.from( + (asn1.result.valueBlock as any).value[1].valueBlock + .valueHexView + ).toString("hex"); + + const signature = removePadding(r, s); + + const ecpair = ECPair.fromPublicKey(Buffer.from(pubkey, "hex")); //p2wpkh pubkeys must be compressed + + const msg = tx.signWith(i, sighash); + const hash = sha256(sha256(msg)); + const valid = ecpair.verify( + Buffer.from(hash, "hex"), + Buffer.from(signature, "hex") + ); + if (!valid) return false; + break; + } + + case TransactionType.P2WPKH: { + if (!input.witness) return false; + const derEncodedSignature = input.witness[0]; + const pubkey = input.witness[1]; + const pubkeyHash = hash160(pubkey); + const pubkeyInScript = input.prevout.scriptpubkey.slice(4); + + if (pubkeyHash !== pubkeyInScript) return false; + + const sighash = + extractSighashFromSignature(derEncodedSignature); + + const asn1 = asn1js.fromBER( + Buffer.from(derEncodedSignature, "hex") + ); + + let r = Buffer.from( + (asn1.result.valueBlock as any).value[0].valueBlock + .valueHexView + ).toString("hex"); + let s = Buffer.from( + (asn1.result.valueBlock as any).value[1].valueBlock + .valueHexView + ).toString("hex"); + + const signature = removePadding(r, s); + + const ecpair = ECPair.fromPublicKey( + Buffer.from(pubkey, "hex"), + { compressed: true } + ); + const msg = Serializer.serializeWitness(tx, i, sighash); + const hash = sha256(sha256(msg)); + const valid = ecpair.verify( + Buffer.from(hash, "hex"), + Buffer.from(signature, "hex") + ); + if (!valid) { + console.log("valid: ", valid); + return false; + } + break; + } + } } - } - return true; + return true; }; + +// const derEncodedSignature = +// "304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee"; +// const pubkey = +// "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357"; +// const sighash = extractSighashFromSignature(derEncodedSignature); +// const asn1 = asn1js.fromBER(Buffer.from(derEncodedSignature, "hex")); + +// let r = Buffer.from( +// (asn1.result.valueBlock as any).value[0].valueBlock.valueHexView +// ).toString("hex"); +// let s = Buffer.from( +// (asn1.result.valueBlock as any).value[1].valueBlock.valueHexView +// ).toString("hex"); + +// const signature = removePadding(r, s); + +// const ecpair = ECPair.fromPublicKey(Buffer.from(pubkey, "hex")); //p2wpkh pubkeys must be compressed + +// const hash = "c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670"; +// const valid = ecpair.verify( +// Buffer.from(hash, "hex"), +// Buffer.from(signature, "hex") +// ); + +// console.log(valid); diff --git a/src/index.ts b/src/index.ts index 7f71025..affd5e2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,58 +12,109 @@ import { Input, Output, Transaction, Tx } from "./features/transaction"; import { Transaction as BitcoinTx } from "bitcoinjs-lib"; (async () => { - const files = fs.readdirSync("./mempool"); - const outputFile = path.join(__dirname, "..", "output.txt"); - let mempool: Transaction[] = []; + const files = fs.readdirSync("./mempool"); + const outputFile = path.join(__dirname, "..", "output.txt"); + let mempool: Transaction[] = []; - const blockSize = 4 * 1e6; + const blockSize = 4 * 1e6; - for (const file of files) { - const tx = JSON.parse(fs.readFileSync(`./mempool/${file}`, "utf8")) as Tx; + for (const file of files) { + // const tx = JSON.parse(fs.readFileSync(`./mempool/${file}`, "utf8")) as Tx; + const tx = { + version: 1, + locktime: 0, + vin: [ + { + txid: "f3898029a8699bd8b71dc6f20e7ec2762a945a30d6a9f18034ce92a9d6cdd26c", + vout: 1, + prevout: { + scriptpubkey: + "00144639af50cc9b5fcc4fc09644c0140078b2d2356c", + scriptpubkey_asm: + "OP_0 OP_PUSHBYTES_20 4639af50cc9b5fcc4fc09644c0140078b2d2356c", + scriptpubkey_type: "v0_p2wpkh", + scriptpubkey_address: + "bc1qgcu675xvnd0ucn7qjezvq9qq0zedydtv07pqxg", + value: 338586, + }, + scriptsig: "", + scriptsig_asm: "", + witness: [ + "30450221008f05cd9bc6679ad3b1e5316370a71779d587d9ff9ceaebb9dfa97288e6abf7fb02203951f6ea925965c7719039984929bac73e7934c86237dc40d72459a694f378ec01", + "02bb0543170d1752bfb0d173724effdc58a708c53d5154e56364e6cb19fd993a73", + ], + is_coinbase: false, + sequence: 4294967293, + }, + ], + vout: [ + { + scriptpubkey: + "5120b09182bc1fc70f752d4d885ec8e68156325b75881de16bb1b5d3e3bf53ff01fd", + scriptpubkey_asm: + "OP_PUSHNUM_1 OP_PUSHBYTES_32 b09182bc1fc70f752d4d885ec8e68156325b75881de16bb1b5d3e3bf53ff01fd", + scriptpubkey_type: "v1_p2tr", + scriptpubkey_address: + "bc1pkzgc90qlcu8h2t2d3p0v3e5p2ce9kavgrhskhvd4603m75llq87s2eyxqn", + value: 2576, + }, + { + scriptpubkey: + "00144639af50cc9b5fcc4fc09644c0140078b2d2356c", + scriptpubkey_asm: + "OP_0 OP_PUSHBYTES_20 4639af50cc9b5fcc4fc09644c0140078b2d2356c", + scriptpubkey_type: "v0_p2wpkh", + scriptpubkey_address: + "bc1qgcu675xvnd0ucn7qjezvq9qq0zedydtv07pqxg", + value: 333840, + }, + ], + }; - // mempool.set(`${file}`.split(".")[0], { - // ...tx, - // txid: `${file}`.split(".")[0], - // }); - const transaction = new Transaction(tx.version, tx.locktime); - for (const input of tx.vin) { - transaction.addInput(new Input(input)); - } + // mempool.set(`${file}`.split(".")[0], { + // ...tx, + // txid: `${file}`.split(".")[0], + // }); + const transaction = new Transaction(tx.version, tx.locktime); + for (const input of tx.vin) { + transaction.addInput(new Input(input)); + } - for (const output of tx.vout) { - transaction.addOutput(new Output(output)); + for (const output of tx.vout) { + transaction.addOutput(new Output(output)); + } + mempool.push(transaction); + break; } - mempool.push(transaction); - } - // for (const tx of mempool) { - // signatureValidator(tx); - // } + for (const tx of mempool) { + signatureValidator(tx); + } - let txs = []; - mempool.sort((txA, txB) => feePerByte(txB) - feePerByte(txA)); - let blockWeight = 0; - for (const tx of mempool) { - if (tx.weight + blockWeight > blockSize) break; + // let txs = []; + // mempool.sort((txA, txB) => feePerByte(txB) - feePerByte(txA)); + // let blockWeight = 0; + // for (const tx of mempool) { + // if (tx.weight + blockWeight > blockSize) break; - txs.push(tx); - blockWeight += tx.weight; - } + // txs.push(tx); + // blockWeight += tx.weight; + // } - try { - fs.unlinkSync(outputFile); - } catch (err) {} - const { serializedBlock, blockHash, coinbaseTransaction } = mine(txs); + // try { + // fs.unlinkSync(outputFile); + // } catch (err) {} + // const { serializedBlock, blockHash, coinbaseTransaction } = mine(txs); - fs.writeFileSync(outputFile, serializedBlock); - fs.appendFileSync(outputFile, "\n"); - fs.appendFileSync(outputFile, coinbaseTransaction.serializedWTx); - fs.appendFileSync(outputFile, "\n"); - fs.appendFileSync(outputFile, coinbaseTransaction.txid); - fs.appendFileSync(outputFile, "\n"); - for (const tx of txs) { - fs.appendFileSync(outputFile, tx.txid); - fs.appendFileSync(outputFile, "\n"); - } - // console.log(fs.readFileSync(outputFile, "utf8").split("\n").length); + // fs.writeFileSync(outputFile, serializedBlock); + // fs.appendFileSync(outputFile, "\n"); + // fs.appendFileSync(outputFile, coinbaseTransaction.serializedWTx); + // fs.appendFileSync(outputFile, "\n"); + // fs.appendFileSync(outputFile, coinbaseTransaction.txid); + // fs.appendFileSync(outputFile, "\n"); + // for (const tx of txs) { + // fs.appendFileSync(outputFile, tx.txid); + // fs.appendFileSync(outputFile, "\n"); + // } + // console.log(fs.readFileSync(outputFile, "utf8").split("\n").length); })(); diff --git a/src/utils.ts b/src/utils.ts index 14daafb..80eeeb2 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,37 +2,44 @@ import * as crypto from "crypto"; import { OP_CODES } from "./features/script/op_codes"; export const hash160 = (str: string) => { - return crypto - .createHash("ripemd160") - .update(Buffer.from(sha256(str), "hex")) - .digest("hex"); + return crypto + .createHash("ripemd160") + .update(Buffer.from(sha256(str), "hex")) + .digest("hex"); +}; + +export const hash256 = (str: string) => { + return crypto + .createHash("sha256") + .update(Buffer.from(sha256(str), "hex")) + .digest("hex"); }; export const sha256 = (str: string) => { - return crypto - .createHash("sha256") - .update(Buffer.from(str, "hex")) - .digest("hex"); + return crypto + .createHash("sha256") + .update(Buffer.from(str, "hex")) + .digest("hex"); }; export const asmToHex = (asm: string) => { - const tokens = asm.split(" ") as OP_CODES[]; - return [...new Array(tokens.length)] - .map((_, index) => OP_CODES[tokens[index]]) - .map((token, index) => (!token ? tokens[index] : token)) - .join(""); + const tokens = asm.split(" ") as OP_CODES[]; + return [...new Array(tokens.length)] + .map((_, index) => OP_CODES[tokens[index]]) + .map((token, index) => (!token ? tokens[index] : token)) + .join(""); }; //reverses every byte of the string - every 2 hex chars export const reversify = (str: string) => { - return str - .match(/.{1,2}/g)! - .reverse() - .join(""); + return str + .match(/.{1,2}/g)! + .reverse() + .join(""); }; -console.log( - hash160( - "00202791caef68f38a0fa3f14d5f4169894ebc318355d2c33bfc1a9d606403b1dbea" - ) -); +// console.log( +// hash160( +// "00202791caef68f38a0fa3f14d5f4169894ebc318355d2c33bfc1a9d606403b1dbea" +// ) +// );