UNPKG

@ecash/lib

Version:

Library for eCash transaction building

142 lines 4.84 kB
"use strict"; // Copyright (c) 2024 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. Object.defineProperty(exports, "__esModule", { value: true }); exports.pushNumberOp = exports.pushBytesOp = exports.writeOp = exports.readOp = exports.isPushOp = void 0; const opcode_js_1 = require("./opcode.js"); /** Returns true if the given object is a `PushOp` */ function isPushOp(op) { if (!op || typeof op !== 'object') { return false; } if (!op.hasOwnProperty('opcode') || !op.hasOwnProperty('data')) { return false; } return typeof op.opcode === 'number' && op.data instanceof Uint8Array; } exports.isPushOp = isPushOp; /** Read a single Script operation from the bytes */ function readOp(bytes) { const opcode = bytes.readU8(); let numBytes; switch (opcode) { case opcode_js_1.OP_PUSHDATA1: numBytes = bytes.readU8(); break; case opcode_js_1.OP_PUSHDATA2: numBytes = bytes.readU16(); break; case opcode_js_1.OP_PUSHDATA4: numBytes = bytes.readU32(); break; default: if (opcode < 0x01 || opcode > 0x4b) { // Non-push opcode return opcode; } numBytes = opcode; } const data = bytes.readBytes(numBytes); return { opcode, data }; } exports.readOp = readOp; /** Write a Script operation to the writer */ function writeOp(op, writer) { if (typeof op == 'number') { writer.putU8(op); return; } if (!isPushOp(op)) { throw `Unexpected op: ${op}`; } writer.putU8(op.opcode); switch (op.opcode) { case opcode_js_1.OP_PUSHDATA1: writer.putU8(op.data.length); break; case opcode_js_1.OP_PUSHDATA2: writer.putU16(op.data.length); break; case opcode_js_1.OP_PUSHDATA4: writer.putU32(op.data.length); break; default: if (op.opcode < 0 || op.opcode > 0x4b) { throw `Not a pushop opcode: 0x${op.opcode.toString(16)}`; } if (op.opcode != op.data.length) { throw (`Inconsistent PushOp, claims to push ${op.opcode} bytes ` + `but actually has ${op.data.length} bytes attached`); } } writer.putBytes(op.data); } exports.writeOp = writeOp; /** Return an Op that minimally pushes the given bytes onto the stack */ function pushBytesOp(data) { if (data.length == 0) { return opcode_js_1.OP_0; } else if (data.length == 1) { if (data[0] >= 1 && data[0] <= 16) { return data[0] + 0x50; } else if (data[0] == 0x81) { return opcode_js_1.OP_1NEGATE; } } let opcode; if (data.length >= 0x01 && data.length <= 0x4b) { opcode = data.length; } else if (data.length >= 0x4c && data.length <= 0xff) { opcode = opcode_js_1.OP_PUSHDATA1; } else if (data.length >= 0x100 && data.length <= 0xffff) { opcode = opcode_js_1.OP_PUSHDATA2; } else if (data.length >= 0x10000 && data.length <= 0xffffffff) { opcode = opcode_js_1.OP_PUSHDATA4; } else { throw 'Bytes way too large'; } return { opcode, data }; } exports.pushBytesOp = pushBytesOp; /** * Returns an Op that pushes the minimally encoded byte representation of a * number to the stack. The bytes pushed to the stack can be used directly * without the need for OP_BIN2NUM. */ function pushNumberOp(value) { if (value == 0) { return opcode_js_1.OP_0; } // Prepare number for encoding. The algorithm below replicates the one used // in `src/script/script.h` intentionally to avoid discrepancies. const auxValue = BigInt(value); let bytes = []; let negative = auxValue < 0; let absvalue = negative ? ~auxValue + 1n : auxValue; // Encode value in little endian byte order by iteratively pushing the // least significant byte until shifting right 1 more byte produces 0 while (absvalue) { bytes.push(Number(absvalue & 0xffn)); absvalue >>= 8n; } // The MSB will encode the sign which means that, if the previous encoding // of the absolute value uses that bit, a new byte must be added to encode // the sign. If bit is not set, then it must be set for negative numbers. let last = bytes[bytes.length - 1]; if (last & 0x80) { bytes.push(negative ? 0x80 : 0x00); } else if (negative) { bytes[bytes.length - 1] = last | 0x80; } return pushBytesOp(Uint8Array.from(bytes)); } exports.pushNumberOp = pushNumberOp; //# sourceMappingURL=op.js.map