diff --git a/modules/ecash-lib/src/op.test.ts b/modules/ecash-lib/src/op.test.ts --- a/modules/ecash-lib/src/op.test.ts +++ b/modules/ecash-lib/src/op.test.ts @@ -7,6 +7,10 @@ import { OP_0, OP_1, + OP_16, + OP_1NEGATE, + OP_2, + OP_3, OP_CHECKSIG, OP_CODESEPARATOR, OP_DUP, @@ -14,9 +18,13 @@ OP_EQUALVERIFY, OP_HASH160, OP_PUSHDATA1, + OP_PUSHDATA2, + OP_PUSHDATA4, } from './opcode.js'; -import { isPushOp, readOp, writeOp } from './op.js'; -import { Bytes, WriterBytes, fromHex, toHex } from './index.js'; +import { isPushOp, pushBytesOp, readOp, writeOp } from './op.js'; +import { fromHex, toHex } from './io/hex.js'; +import { Bytes } from './io/bytes.js'; +import { WriterBytes } from './io/writerbytes.js'; const wrote = (size: number, fn: (writer: WriterBytes) => void) => { const writer = new WriterBytes(size); @@ -223,4 +231,71 @@ 'Inconsistent PushOp, claims to push 2 bytes but actually has 0 bytes attached', ); }); + it('pushBytesOp', () => { + // single push opcodes + expect(pushBytesOp(new Uint8Array())).to.equal(OP_0); + expect(pushBytesOp(new Uint8Array([1]))).to.equal(OP_1); + expect(pushBytesOp(new Uint8Array([2]))).to.equal(OP_2); + expect(pushBytesOp(new Uint8Array([3]))).to.equal(OP_3); + expect(pushBytesOp(new Uint8Array([16]))).to.equal(OP_16); + expect(pushBytesOp(new Uint8Array([0x81]))).to.equal(OP_1NEGATE); + + expect(pushBytesOp(new Uint8Array([1, 0]))).to.deep.equal({ + opcode: 2, + data: new Uint8Array([1, 0]), + }); + expect(pushBytesOp(new Uint8Array([16, 0]))).to.deep.equal({ + opcode: 2, + data: new Uint8Array([16, 0]), + }); + expect(pushBytesOp(new Uint8Array([0x81, 0]))).to.deep.equal({ + opcode: 2, + data: new Uint8Array([0x81, 0]), + }); + + expect(pushBytesOp(new Uint8Array([0]))).to.deep.equal({ + opcode: 1, + data: new Uint8Array([0]), + }); + expect(pushBytesOp(new Uint8Array([17]))).to.deep.equal({ + opcode: 1, + data: new Uint8Array([17]), + }); + expect(pushBytesOp(new Uint8Array([0x80]))).to.deep.equal({ + opcode: 1, + data: new Uint8Array([0x80]), + }); + expect(pushBytesOp(new Uint8Array([0xff]))).to.deep.equal({ + opcode: 1, + data: new Uint8Array([0xff]), + }); + + for (let size = 2; size <= 0x4b; ++size) { + expect(pushBytesOp(new Uint8Array(size))).to.deep.equal({ + opcode: size, + data: new Uint8Array(size), + }); + } + + expect(pushBytesOp(new Uint8Array(0x4c))).to.deep.equal({ + opcode: OP_PUSHDATA1, + data: new Uint8Array(0x4c), + }); + expect(pushBytesOp(new Uint8Array(0xff))).to.deep.equal({ + opcode: OP_PUSHDATA1, + data: new Uint8Array(0xff), + }); + expect(pushBytesOp(new Uint8Array(0x100))).to.deep.equal({ + opcode: OP_PUSHDATA2, + data: new Uint8Array(0x100), + }); + expect(pushBytesOp(new Uint8Array(0xffff))).to.deep.equal({ + opcode: OP_PUSHDATA2, + data: new Uint8Array(0xffff), + }); + expect(pushBytesOp(new Uint8Array(0x10000))).to.deep.equal({ + opcode: OP_PUSHDATA4, + data: new Uint8Array(0x10000), + }); + }); }); diff --git a/modules/ecash-lib/src/op.ts b/modules/ecash-lib/src/op.ts --- a/modules/ecash-lib/src/op.ts +++ b/modules/ecash-lib/src/op.ts @@ -4,7 +4,14 @@ import { Bytes } from './io/bytes.js'; import { Writer } from './io/writer.js'; -import { OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4, Opcode } from './opcode.js'; +import { + OP_0, + OP_1NEGATE, + OP_PUSHDATA1, + OP_PUSHDATA2, + OP_PUSHDATA4, + Opcode, +} from './opcode.js'; /** * A single operation in Bitcoin script, either a singular non-pushop code or @@ -90,3 +97,29 @@ } writer.putBytes(op.data); } + +/** Return an Op that minimally pushes the given bytes onto the stack */ +export function pushBytesOp(data: Uint8Array): Op { + if (data.length == 0) { + return OP_0; + } else if (data.length == 1) { + if (data[0] >= 1 && data[0] <= 16) { + return data[0] + 0x50; + } else if (data[0] == 0x81) { + return OP_1NEGATE; + } + } + let opcode: Opcode; + if (data.length >= 0x01 && data.length <= 0x4b) { + opcode = data.length; + } else if (data.length >= 0x4c && data.length <= 0xff) { + opcode = OP_PUSHDATA1; + } else if (data.length >= 0x100 && data.length <= 0xffff) { + opcode = OP_PUSHDATA2; + } else if (data.length >= 0x10000 && data.length <= 0xffffffff) { + opcode = OP_PUSHDATA4; + } else { + throw 'Bytes way too large'; + } + return { opcode, data }; +}