Skip to content

Commit

Permalink
Use OP_PUSHNUM_13 as rune protocol identifier (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
summraznboi authored Mar 22, 2024
1 parent 1f3743a commit d19c38b
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 83 deletions.
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import * as bitcoin from 'bitcoinjs-lib';
import { u128 } from './u128';

export const MAX_DIVISIBILITY = 38;
export const MAX_LIMIT = u128(1n << 64n);
export const RESERVED = u128(6402364363415443603228541259936211926n);
export const SUBSIDY_HALVING_INTERVAL = 210_000;
export const MAX_SCRIPT_ELEMENT_SIZE = 520;
export const MAGIC_NUMBER = bitcoin.opcodes.OP_13;
14 changes: 8 additions & 6 deletions src/runestone.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
MAGIC_NUMBER,
MAX_DIVISIBILITY,
MAX_LIMIT,
MAX_SCRIPT_ELEMENT_SIZE,
Expand All @@ -13,7 +14,7 @@ import _ from 'lodash';
import { Option, Some, None } from '@sniptt/monads';
import { Rune } from './rune';
import { Flag } from './flag';
import { Instruction, decompileScriptAllBuffer } from './utils';
import { Instruction, tryConvertInstructionToBuffer } from './utils';
import { RuneId } from './runeid';

export const MAX_SPACERS = 0b00000111_11111111_11111111_11111111;
Expand Down Expand Up @@ -214,7 +215,7 @@ export class Runestone {

const stack: bitcoin.Stack = [];
stack.push(bitcoin.opcodes.OP_RETURN);
stack.push(Buffer.from('RUNE_TEST'));
stack.push(MAGIC_NUMBER);

const payload = Buffer.concat(payloads);
let i = 0;
Expand All @@ -227,7 +228,7 @@ export class Runestone {

static payload(transaction: bitcoin.Transaction): Option<Buffer> {
for (const output of transaction.outs) {
const instructions = decompileScriptAllBuffer(output.script);
const instructions = bitcoin.script.decompile(output.script);
if (instructions === null) {
throw new Error('unable to decompile');
}
Expand All @@ -242,15 +243,16 @@ export class Runestone {
nextInstruction = instructions.shift();
if (
!nextInstruction ||
Instruction.isNumber(nextInstruction) ||
Buffer.compare(nextInstruction, Buffer.from('RUNE_TEST')) !== 0
Instruction.isBuffer(nextInstruction) ||
nextInstruction !== MAGIC_NUMBER
) {
continue;
}

let payloads: Buffer[] = [];

for (const result of instructions) {
for (const instruction of instructions) {
const result = tryConvertInstructionToBuffer(instruction);
if (Instruction.isBuffer(result)) {
payloads.push(result);
}
Expand Down
70 changes: 28 additions & 42 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,48 +12,34 @@ export namespace Instruction {
}
}

export function decompileScriptAllBuffer(script: Buffer) {
const instructions = bitcoin.script.decompile(script);
if (instructions === null) {
return null;
}

const result: Instruction[] = [];
for (const instruction of instructions) {
if (Instruction.isNumber(instruction)) {
switch (instruction) {
case bitcoin.opcodes.OP_0:
result.push(Buffer.alloc(0));
break;
case bitcoin.opcodes.OP_1:
case bitcoin.opcodes.OP_2:
case bitcoin.opcodes.OP_3:
case bitcoin.opcodes.OP_4:
case bitcoin.opcodes.OP_5:
case bitcoin.opcodes.OP_6:
case bitcoin.opcodes.OP_7:
case bitcoin.opcodes.OP_8:
case bitcoin.opcodes.OP_9:
case bitcoin.opcodes.OP_10:
case bitcoin.opcodes.OP_11:
case bitcoin.opcodes.OP_12:
case bitcoin.opcodes.OP_13:
case bitcoin.opcodes.OP_14:
case bitcoin.opcodes.OP_15:
case bitcoin.opcodes.OP_16:
result.push(Buffer.from([instruction - bitcoin.opcodes.OP_1 + 1]));
break;
case bitcoin.opcodes.OP_1NEGATE:
result.push(Buffer.from([0x80]));
break;
default:
result.push(instruction);
break;
}
} else {
result.push(instruction);
export function tryConvertInstructionToBuffer(instruction: Instruction) {
if (Instruction.isNumber(instruction)) {
switch (instruction) {
case bitcoin.opcodes.OP_0:
return Buffer.alloc(0);
case bitcoin.opcodes.OP_1:
case bitcoin.opcodes.OP_2:
case bitcoin.opcodes.OP_3:
case bitcoin.opcodes.OP_4:
case bitcoin.opcodes.OP_5:
case bitcoin.opcodes.OP_6:
case bitcoin.opcodes.OP_7:
case bitcoin.opcodes.OP_8:
case bitcoin.opcodes.OP_9:
case bitcoin.opcodes.OP_10:
case bitcoin.opcodes.OP_11:
case bitcoin.opcodes.OP_12:
case bitcoin.opcodes.OP_13:
case bitcoin.opcodes.OP_14:
case bitcoin.opcodes.OP_15:
case bitcoin.opcodes.OP_16:
return Buffer.from([instruction - bitcoin.opcodes.OP_1 + 1]);
case bitcoin.opcodes.OP_1NEGATE:
return Buffer.from([0x80]);
default:
return instruction;
}
} else {
return instruction;
}

return result;
}
64 changes: 29 additions & 35 deletions test/runestone.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import { u128 } from '../src/u128';
import { None, Option, Some } from '@sniptt/monads';
import { Tag } from '../src/tag';
import { Flag } from '../src/flag';
import { MAX_DIVISIBILITY } from '../src/constants';
import { MAGIC_NUMBER, MAX_DIVISIBILITY } from '../src/constants';
import { Rune } from '../src/rune';
import { SpacedRune } from '../src/spacedrune';
import { decompileScriptAllBuffer } from '../src/utils';
import { Edict } from '../src/edict';
import { Etching } from '../src/etching';
import { RuneId } from '../src/runeid';
Expand All @@ -22,7 +21,7 @@ describe('runestone', () => {
return Runestone.decipher(
getSimpleTransaction([
bitcoin.opcodes.OP_RETURN,
Buffer.from('RUNE_TEST'),
MAGIC_NUMBER,
getPayload(integers),
])
).unwrap();
Expand Down Expand Up @@ -89,7 +88,7 @@ describe('runestone', () => {
test('deciphering_valid_runestone_with_invalid_script_postfix_returns_script_error', () => {
const transaction = getSimpleTransaction([
bitcoin.opcodes.OP_RETURN,
Buffer.from('RUNE_TEST'),
MAGIC_NUMBER,
]);

transaction.outs[0].script = Buffer.concat([
Expand All @@ -105,7 +104,7 @@ describe('runestone', () => {
Runestone.decipher(
getSimpleTransaction([
bitcoin.opcodes.OP_RETURN,
Buffer.from('RUNE_TEST'),
MAGIC_NUMBER,
Buffer.from([128]),
])
).isSome()
Expand All @@ -117,7 +116,7 @@ describe('runestone', () => {
Runestone.decipher(
getSimpleTransaction([
bitcoin.opcodes.OP_RETURN,
Buffer.from('RUNE_TEST'),
MAGIC_NUMBER,
Buffer.concat([
Buffer.from([0]),
u128.encodeVarInt(createRuneId(1).toU128()),
Expand All @@ -140,10 +139,7 @@ describe('runestone', () => {
test('deciphering_empty_runestone_is_successful', () => {
expect(
Runestone.decipher(
getSimpleTransaction([
bitcoin.opcodes.OP_RETURN,
Buffer.from('RUNE_TEST'),
])
getSimpleTransaction([bitcoin.opcodes.OP_RETURN, MAGIC_NUMBER])
).isSome()
).toBe(true);
});
Expand All @@ -154,15 +150,15 @@ describe('runestone', () => {
const transaction = new bitcoin.Transaction();
let scriptPubKey = bitcoin.script.compile([
bitcoin.opcodes.OP_RETURN,
Buffer.from('RUNE_TEST'),
MAGIC_NUMBER,
4,
]);
scriptPubKey = Buffer.concat([scriptPubKey, Buffer.from([4])]);
transaction.addOutput(bitcoin.script.compile(scriptPubKey), 0);
transaction.addOutput(
bitcoin.script.compile([
bitcoin.opcodes.OP_RETURN,
Buffer.from('RUNE_TEST'),
MAGIC_NUMBER,
payload,
]),
0
Expand Down Expand Up @@ -685,7 +681,7 @@ describe('runestone', () => {
const runestone = Runestone.decipher(
getSimpleTransaction([
bitcoin.opcodes.OP_RETURN,
Buffer.from('RUNE_TEST'),
MAGIC_NUMBER,
u128.encodeVarInt(u128(Tag.FLAGS)),
u128.encodeVarInt(Flag.mask(Flag.ETCH)),
u128.encodeVarInt(u128(Tag.DIVISIBILITY)),
Expand Down Expand Up @@ -717,7 +713,7 @@ describe('runestone', () => {
transaction.addOutput(
bitcoin.script.compile([
bitcoin.opcodes.OP_RETURN,
Buffer.from('RUNE_TEST'),
MAGIC_NUMBER,
payload,
]),
0
Expand All @@ -742,7 +738,7 @@ describe('runestone', () => {
transaction.addOutput(
bitcoin.script.compile([
bitcoin.opcodes.OP_RETURN,
Buffer.from('RUNE_TEST'),
MAGIC_NUMBER,
payload,
]),
0
Expand All @@ -758,26 +754,24 @@ describe('runestone', () => {
test('runestone_size', () => {
function testcase(edicts: Edict[], etching: Option<Etching>, size: number) {
expect(
new Runestone(false, None, None, edicts, etching).encipher().length -
1 -
'RUNE_TEST'.length
new Runestone(false, None, None, edicts, etching).encipher().length
).toBe(size);
}

testcase([], None, 1);
testcase([], None, 2);

testcase(
[],
Some(new Etching(0, Some(new Rune(u128(0))), 0, None, None)),
6
7
);

testcase(
[],
Some(
new Etching(MAX_DIVISIBILITY, Some(new Rune(u128(0))), 0, None, None)
),
8
9
);

testcase(
Expand All @@ -795,13 +789,13 @@ describe('runestone', () => {
})
)
),
19
20
);

testcase(
[],
Some(new Etching(0, Some(new Rune(u128.MAX)), 0, None, None)),
24
25
);

testcase(
Expand All @@ -815,7 +809,7 @@ describe('runestone', () => {
Some(
new Etching(MAX_DIVISIBILITY, Some(new Rune(u128.MAX)), 0, None, None)
),
30
31
);

testcase(
Expand All @@ -829,7 +823,7 @@ describe('runestone', () => {
Some(
new Etching(MAX_DIVISIBILITY, Some(new Rune(u128.MAX)), 0, None, None)
),
48
49
);

testcase(
Expand All @@ -841,7 +835,7 @@ describe('runestone', () => {
},
],
None,
11
12
);

testcase(
Expand All @@ -853,7 +847,7 @@ describe('runestone', () => {
},
],
None,
29
30
);

testcase(
Expand All @@ -870,7 +864,7 @@ describe('runestone', () => {
},
],
None,
50
51
);

testcase(
Expand All @@ -892,7 +886,7 @@ describe('runestone', () => {
},
],
None,
71
72
);

testcase(
Expand All @@ -902,7 +896,7 @@ describe('runestone', () => {
output: u128(0),
})),
None,
56
57
);

testcase(
Expand All @@ -912,7 +906,7 @@ describe('runestone', () => {
output: u128(0),
})),
None,
68
69
);

testcase(
Expand All @@ -922,7 +916,7 @@ describe('runestone', () => {
output: u128(0),
})),
None,
65
66
);

testcase(
Expand All @@ -932,7 +926,7 @@ describe('runestone', () => {
output: u128(0),
})),
None,
63
64
);
});

Expand Down Expand Up @@ -1143,7 +1137,7 @@ describe('runestone', () => {
None
).encipher();

const instructions = decompileScriptAllBuffer(script);
const instructions = bitcoin.script.decompile(script);
expect(instructions?.length).toBe(3);
}

Expand All @@ -1160,7 +1154,7 @@ describe('runestone', () => {
None
).encipher();

const instructions = decompileScriptAllBuffer(script);
const instructions = bitcoin.script.decompile(script);
expect(instructions?.length).toBe(4);
}
});
Expand Down

0 comments on commit d19c38b

Please sign in to comment.