@vbyte/btc-dev 1.1.8 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +127 -0
- package/LICENSE +21 -121
- package/README.md +69 -3
- package/dist/const.d.ts +3 -0
- package/dist/const.js +23 -22
- package/dist/error.d.ts +11 -0
- package/dist/error.js +20 -0
- package/dist/index.d.ts +12 -11
- package/dist/index.js +11 -10
- package/dist/lib/address/api.d.ts +2 -2
- package/dist/lib/address/api.js +13 -12
- package/dist/lib/address/encode.d.ts +1 -1
- package/dist/lib/address/encode.js +26 -24
- package/dist/lib/address/index.d.ts +6 -6
- package/dist/lib/address/index.js +6 -6
- package/dist/lib/address/p2pkh.d.ts +2 -2
- package/dist/lib/address/p2pkh.js +15 -15
- package/dist/lib/address/p2sh.d.ts +2 -2
- package/dist/lib/address/p2sh.js +14 -14
- package/dist/lib/address/p2tr.d.ts +2 -2
- package/dist/lib/address/p2tr.js +14 -14
- package/dist/lib/address/p2wpkh.d.ts +2 -2
- package/dist/lib/address/p2wpkh.js +15 -15
- package/dist/lib/address/p2wsh.d.ts +2 -2
- package/dist/lib/address/p2wsh.js +14 -14
- package/dist/lib/address/script.d.ts +1 -1
- package/dist/lib/address/script.js +16 -16
- package/dist/lib/address/util.d.ts +1 -1
- package/dist/lib/address/util.js +24 -22
- package/dist/lib/meta/index.d.ts +4 -4
- package/dist/lib/meta/index.js +4 -4
- package/dist/lib/meta/locktime.d.ts +1 -1
- package/dist/lib/meta/locktime.js +13 -12
- package/dist/lib/meta/ref.js +13 -9
- package/dist/lib/meta/scribe.d.ts +2 -2
- package/dist/lib/meta/scribe.js +71 -56
- package/dist/lib/meta/sequence.d.ts +1 -1
- package/dist/lib/meta/sequence.js +21 -19
- package/dist/lib/script/decode.d.ts +2 -2
- package/dist/lib/script/decode.js +53 -17
- package/dist/lib/script/encode.d.ts +1 -1
- package/dist/lib/script/encode.js +21 -16
- package/dist/lib/script/index.d.ts +5 -13
- package/dist/lib/script/index.js +5 -14
- package/dist/lib/script/lock.d.ts +2 -2
- package/dist/lib/script/lock.js +15 -12
- package/dist/lib/script/util.js +4 -4
- package/dist/lib/script/words.js +131 -130
- package/dist/lib/sighash/index.d.ts +3 -3
- package/dist/lib/sighash/index.js +3 -3
- package/dist/lib/sighash/segwit.d.ts +2 -2
- package/dist/lib/sighash/segwit.js +18 -14
- package/dist/lib/sighash/taproot.d.ts +2 -2
- package/dist/lib/sighash/taproot.js +27 -23
- package/dist/lib/sighash/util.d.ts +2 -2
- package/dist/lib/sighash/util.js +8 -7
- package/dist/lib/signer/index.d.ts +2 -2
- package/dist/lib/signer/index.js +2 -2
- package/dist/lib/signer/sign.d.ts +1 -1
- package/dist/lib/signer/sign.js +43 -7
- package/dist/lib/signer/verify.d.ts +17 -3
- package/dist/lib/signer/verify.js +232 -3
- package/dist/lib/taproot/cblock.d.ts +1 -1
- package/dist/lib/taproot/cblock.js +16 -17
- package/dist/lib/taproot/encode.d.ts +1 -1
- package/dist/lib/taproot/encode.js +9 -8
- package/dist/lib/taproot/index.d.ts +4 -4
- package/dist/lib/taproot/index.js +4 -4
- package/dist/lib/taproot/parse.d.ts +1 -1
- package/dist/lib/taproot/parse.js +15 -15
- package/dist/lib/taproot/tree.d.ts +2 -2
- package/dist/lib/taproot/tree.js +12 -7
- package/dist/lib/tx/create.d.ts +1 -1
- package/dist/lib/tx/create.js +28 -12
- package/dist/lib/tx/decode.d.ts +2 -2
- package/dist/lib/tx/decode.js +52 -17
- package/dist/lib/tx/encode.d.ts +2 -2
- package/dist/lib/tx/encode.js +13 -16
- package/dist/lib/tx/index.d.ts +7 -7
- package/dist/lib/tx/index.js +7 -7
- package/dist/lib/tx/parse.d.ts +1 -1
- package/dist/lib/tx/parse.js +9 -9
- package/dist/lib/tx/size.d.ts +2 -2
- package/dist/lib/tx/size.js +9 -11
- package/dist/lib/tx/util.d.ts +2 -2
- package/dist/lib/tx/util.js +22 -20
- package/dist/lib/tx/validate.d.ts +1 -1
- package/dist/lib/tx/validate.js +37 -9
- package/dist/lib/witness/index.d.ts +2 -2
- package/dist/lib/witness/index.js +2 -2
- package/dist/lib/witness/parse.d.ts +2 -2
- package/dist/lib/witness/parse.js +24 -23
- package/dist/lib/witness/util.d.ts +2 -2
- package/dist/lib/witness/util.js +5 -5
- package/dist/main.cjs +3305 -2035
- package/dist/main.cjs.map +1 -1
- package/dist/module.mjs +3303 -2036
- package/dist/module.mjs.map +1 -1
- package/dist/package.json +24 -19
- package/dist/schema/base.d.ts +1 -1
- package/dist/schema/base.js +17 -13
- package/dist/schema/index.d.ts +2 -2
- package/dist/schema/index.js +2 -2
- package/dist/schema/taproot.d.ts +1 -1
- package/dist/schema/taproot.js +2 -2
- package/dist/schema/tx.d.ts +1 -1
- package/dist/schema/tx.js +4 -4
- package/dist/script.js +10 -12
- package/dist/script.js.map +1 -1
- package/dist/types/address.d.ts +4 -4
- package/dist/types/index.d.ts +8 -8
- package/dist/types/index.js +8 -8
- package/dist/types/meta.d.ts +4 -4
- package/dist/types/psbt.d.ts +2 -2
- package/dist/types/script.d.ts +2 -2
- package/dist/types/sighash.d.ts +2 -2
- package/dist/types/witness.d.ts +5 -5
- package/docs/API.md +1145 -0
- package/docs/CONVENTIONS.md +316 -0
- package/docs/FAQ.md +396 -0
- package/docs/GUIDE.md +1102 -0
- package/package.json +24 -19
- package/src/const.ts +0 -61
- package/src/index.ts +0 -13
- package/src/lib/address/api.ts +0 -50
- package/src/lib/address/encode.ts +0 -183
- package/src/lib/address/index.ts +0 -7
- package/src/lib/address/p2pkh.ts +0 -94
- package/src/lib/address/p2sh.ts +0 -96
- package/src/lib/address/p2tr.ts +0 -91
- package/src/lib/address/p2wpkh.ts +0 -94
- package/src/lib/address/p2wsh.ts +0 -92
- package/src/lib/address/script.ts +0 -63
- package/src/lib/address/util.ts +0 -87
- package/src/lib/meta/index.ts +0 -4
- package/src/lib/meta/locktime.ts +0 -57
- package/src/lib/meta/ref.ts +0 -107
- package/src/lib/meta/scribe.ts +0 -256
- package/src/lib/meta/sequence.ts +0 -146
- package/src/lib/script/decode.ts +0 -85
- package/src/lib/script/encode.ts +0 -129
- package/src/lib/script/index.ts +0 -20
- package/src/lib/script/lock.ts +0 -73
- package/src/lib/script/util.ts +0 -78
- package/src/lib/script/words.ts +0 -182
- package/src/lib/sighash/index.ts +0 -3
- package/src/lib/sighash/segwit.ts +0 -152
- package/src/lib/sighash/taproot.ts +0 -206
- package/src/lib/sighash/util.ts +0 -51
- package/src/lib/signer/index.ts +0 -2
- package/src/lib/signer/sign.ts +0 -39
- package/src/lib/signer/verify.ts +0 -88
- package/src/lib/taproot/cblock.ts +0 -96
- package/src/lib/taproot/encode.ts +0 -49
- package/src/lib/taproot/index.ts +0 -4
- package/src/lib/taproot/parse.ts +0 -65
- package/src/lib/taproot/tree.ts +0 -94
- package/src/lib/tx/create.ts +0 -90
- package/src/lib/tx/decode.ts +0 -123
- package/src/lib/tx/encode.ts +0 -155
- package/src/lib/tx/index.ts +0 -7
- package/src/lib/tx/parse.ts +0 -69
- package/src/lib/tx/size.ts +0 -68
- package/src/lib/tx/util.ts +0 -111
- package/src/lib/tx/validate.ts +0 -49
- package/src/lib/witness/index.ts +0 -2
- package/src/lib/witness/parse.ts +0 -127
- package/src/lib/witness/util.ts +0 -18
- package/src/schema/base.ts +0 -57
- package/src/schema/index.ts +0 -2
- package/src/schema/taproot.ts +0 -12
- package/src/schema/tx.ts +0 -48
- package/src/types/address.ts +0 -35
- package/src/types/index.ts +0 -8
- package/src/types/meta.ts +0 -48
- package/src/types/psbt.ts +0 -15
- package/src/types/script.ts +0 -18
- package/src/types/sighash.ts +0 -16
- package/src/types/taproot.ts +0 -41
- package/src/types/txdata.ts +0 -85
- package/src/types/witness.ts +0 -42
package/dist/lib/meta/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
1
|
+
export * from "./locktime.js";
|
|
2
|
+
export * from "./ref.js";
|
|
3
|
+
export * from "./scribe.js";
|
|
4
|
+
export * from "./sequence.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Assert } from
|
|
1
|
+
import { Assert } from "@vbyte/util";
|
|
2
|
+
import { ConfigError } from "../../error.js";
|
|
2
3
|
const LOCKTIME_THRESHOLD = 500000000;
|
|
3
4
|
export var LocktimeField;
|
|
4
5
|
(function (LocktimeField) {
|
|
@@ -7,31 +8,31 @@ export var LocktimeField;
|
|
|
7
8
|
})(LocktimeField || (LocktimeField = {}));
|
|
8
9
|
export function encode_locktime(locktime) {
|
|
9
10
|
switch (locktime.type) {
|
|
10
|
-
case
|
|
11
|
-
Assert.ok(locktime.stamp >= LOCKTIME_THRESHOLD,
|
|
11
|
+
case "timelock":
|
|
12
|
+
Assert.ok(locktime.stamp >= LOCKTIME_THRESHOLD, "Invalid timestamp");
|
|
12
13
|
return locktime.stamp;
|
|
13
|
-
case
|
|
14
|
-
Assert.ok(locktime.height > 0,
|
|
15
|
-
Assert.ok(locktime.height < LOCKTIME_THRESHOLD,
|
|
14
|
+
case "heightlock":
|
|
15
|
+
Assert.ok(locktime.height > 0, "height must be greater than 0");
|
|
16
|
+
Assert.ok(locktime.height < LOCKTIME_THRESHOLD, "invalid block height");
|
|
16
17
|
return locktime.height;
|
|
17
18
|
default:
|
|
18
|
-
throw new
|
|
19
|
+
throw new ConfigError(`Invalid locktime type: expected 'timelock' or 'heightlock'`);
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
22
|
export function decode_locktime(locktime) {
|
|
22
|
-
if (isNaN(locktime) || locktime <= 0) {
|
|
23
|
+
if (Number.isNaN(locktime) || locktime <= 0) {
|
|
23
24
|
return null;
|
|
24
25
|
}
|
|
25
26
|
if (locktime < LOCKTIME_THRESHOLD) {
|
|
26
27
|
return {
|
|
27
|
-
type:
|
|
28
|
-
height: locktime
|
|
28
|
+
type: "heightlock",
|
|
29
|
+
height: locktime,
|
|
29
30
|
};
|
|
30
31
|
}
|
|
31
32
|
else {
|
|
32
33
|
return {
|
|
33
|
-
type:
|
|
34
|
-
stamp: locktime
|
|
34
|
+
type: "timelock",
|
|
35
|
+
stamp: locktime,
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
38
|
}
|
package/dist/lib/meta/ref.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ValidationError } from "../../error.js";
|
|
1
2
|
export var RefPointer;
|
|
2
3
|
(function (RefPointer) {
|
|
3
4
|
RefPointer.outpoint = {
|
|
@@ -24,15 +25,15 @@ function encode_inscription_id(txid, order = 0) {
|
|
|
24
25
|
}
|
|
25
26
|
function decode_inscription_id(inscription_id) {
|
|
26
27
|
assert_inscription_id(inscription_id);
|
|
27
|
-
const [txid, order] = inscription_id.split(
|
|
28
|
-
return { txid, order: parseInt(order) };
|
|
28
|
+
const [txid, order] = inscription_id.split("i");
|
|
29
|
+
return { txid, order: parseInt(order, 10) };
|
|
29
30
|
}
|
|
30
31
|
function verify_inscription_id(inscription_id) {
|
|
31
32
|
return inscription_id.match(/^[a-f0-9]{64}i\d+$/) !== null;
|
|
32
33
|
}
|
|
33
34
|
function assert_inscription_id(inscription_id) {
|
|
34
35
|
if (!verify_inscription_id(inscription_id)) {
|
|
35
|
-
throw new
|
|
36
|
+
throw new ValidationError(`invalid inscription id: "${inscription_id}". Expected format: <64-char-txid>i<index> (e.g., "abc123...i0")`);
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
function encode_rune_id(block_height, block_index) {
|
|
@@ -40,15 +41,18 @@ function encode_rune_id(block_height, block_index) {
|
|
|
40
41
|
}
|
|
41
42
|
function decode_rune_id(rune_id) {
|
|
42
43
|
assert_rune_id(rune_id);
|
|
43
|
-
const [block_height, block_index] = rune_id.split(
|
|
44
|
-
return {
|
|
44
|
+
const [block_height, block_index] = rune_id.split(":");
|
|
45
|
+
return {
|
|
46
|
+
block_height: parseInt(block_height, 10),
|
|
47
|
+
block_index: parseInt(block_index, 10),
|
|
48
|
+
};
|
|
45
49
|
}
|
|
46
50
|
function verify_rune_id(rune_id) {
|
|
47
51
|
return rune_id.match(/^\d+:\d+$/) !== null;
|
|
48
52
|
}
|
|
49
53
|
function assert_rune_id(rune_id) {
|
|
50
54
|
if (!verify_rune_id(rune_id)) {
|
|
51
|
-
throw new
|
|
55
|
+
throw new ValidationError(`invalid rune id: "${rune_id}". Expected format: <block_height>:<block_index> (e.g., "840000:1")`);
|
|
52
56
|
}
|
|
53
57
|
}
|
|
54
58
|
function encode_outpoint(txid, vout) {
|
|
@@ -56,14 +60,14 @@ function encode_outpoint(txid, vout) {
|
|
|
56
60
|
}
|
|
57
61
|
function decode_outpoint(outpoint) {
|
|
58
62
|
assert_outpoint(outpoint);
|
|
59
|
-
const [txid, vout] = outpoint.split(
|
|
60
|
-
return { txid, vout: parseInt(vout) };
|
|
63
|
+
const [txid, vout] = outpoint.split(":");
|
|
64
|
+
return { txid, vout: parseInt(vout, 10) };
|
|
61
65
|
}
|
|
62
66
|
function verify_outpoint(outpoint) {
|
|
63
67
|
return outpoint.match(/^[a-f0-9]{64}:[0-9]+$/) !== null;
|
|
64
68
|
}
|
|
65
69
|
function assert_outpoint(outpoint) {
|
|
66
70
|
if (!verify_outpoint(outpoint)) {
|
|
67
|
-
throw new
|
|
71
|
+
throw new ValidationError(`invalid outpoint: "${outpoint}". Expected format: <64-char-txid>:<vout> (e.g., "abc123...:0")`);
|
|
68
72
|
}
|
|
69
73
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Buff, Bytes } from
|
|
2
|
-
import type { InscriptionData } from
|
|
1
|
+
import { Buff, type Bytes } from "@vbyte/buff";
|
|
2
|
+
import type { InscriptionData } from "../../types/index.js";
|
|
3
3
|
export declare namespace InscriptionUtil {
|
|
4
4
|
type Type = InscriptionData;
|
|
5
5
|
const encode: typeof encode_inscription;
|
package/dist/lib/meta/scribe.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Buff, Stream } from
|
|
2
|
-
import { Assert } from
|
|
3
|
-
import {
|
|
4
|
-
import { decode_script } from
|
|
1
|
+
import { Buff, Stream } from "@vbyte/buff";
|
|
2
|
+
import { Assert } from "@vbyte/util";
|
|
3
|
+
import { ValidationError } from "../../error.js";
|
|
4
|
+
import { decode_script } from "../../lib/script/decode.js";
|
|
5
|
+
import { encode_script } from "../../lib/script/encode.js";
|
|
5
6
|
const _0n = BigInt(0);
|
|
6
7
|
const _1n = BigInt(1);
|
|
7
8
|
const _26n = BigInt(26);
|
|
@@ -18,54 +19,55 @@ export function encode_inscription(data) {
|
|
|
18
19
|
return Buff.join(data.map(create_envelope));
|
|
19
20
|
}
|
|
20
21
|
function create_envelope(data) {
|
|
21
|
-
|
|
22
|
-
if (typeof data.delegate ===
|
|
22
|
+
const asm = ["OP_0", "OP_IF", "6f7264"];
|
|
23
|
+
if (typeof data.delegate === "string") {
|
|
23
24
|
const id = encode_id(data.delegate);
|
|
24
|
-
asm.push(
|
|
25
|
+
asm.push("OP_11", id);
|
|
25
26
|
}
|
|
26
|
-
if (typeof data.ref ===
|
|
27
|
-
asm.push(
|
|
27
|
+
if (typeof data.ref === "string") {
|
|
28
|
+
asm.push("OP_WITHIN", data.ref);
|
|
28
29
|
}
|
|
29
|
-
if (typeof data.parent ===
|
|
30
|
+
if (typeof data.parent === "string") {
|
|
30
31
|
const id = encode_id(data.parent);
|
|
31
|
-
asm.push(
|
|
32
|
+
asm.push("OP_3", id);
|
|
32
33
|
}
|
|
33
|
-
if (typeof data.opcode ===
|
|
34
|
+
if (typeof data.opcode === "number") {
|
|
34
35
|
const code = encode_pointer(data.opcode);
|
|
35
|
-
asm.push(
|
|
36
|
+
asm.push("OP_NOP", code);
|
|
36
37
|
}
|
|
37
|
-
if (typeof data.pointer ===
|
|
38
|
+
if (typeof data.pointer === "number") {
|
|
38
39
|
const ptr = encode_pointer(data.pointer);
|
|
39
|
-
asm.push(
|
|
40
|
+
asm.push("OP_2", ptr);
|
|
40
41
|
}
|
|
41
|
-
if (typeof data.rune ===
|
|
42
|
+
if (typeof data.rune === "string") {
|
|
42
43
|
const label = encode_rune_label(data.rune);
|
|
43
|
-
asm.push(
|
|
44
|
+
asm.push("OP_13", label);
|
|
44
45
|
}
|
|
45
|
-
if (typeof data.mimetype ===
|
|
46
|
+
if (typeof data.mimetype === "string") {
|
|
46
47
|
const label = encode_label(data.mimetype);
|
|
47
|
-
asm.push(
|
|
48
|
+
asm.push("OP_1", label);
|
|
48
49
|
}
|
|
49
|
-
if (typeof data.content ===
|
|
50
|
+
if (typeof data.content === "string") {
|
|
50
51
|
const chunks = encode_content(data.content);
|
|
51
|
-
asm.push(
|
|
52
|
+
asm.push("OP_0", ...chunks);
|
|
52
53
|
}
|
|
53
|
-
asm.push(
|
|
54
|
+
asm.push("OP_ENDIF");
|
|
54
55
|
return encode_script(asm);
|
|
55
56
|
}
|
|
56
57
|
function parse_envelopes(script) {
|
|
57
58
|
const words = decode_script(script);
|
|
58
|
-
const start_idx = words.
|
|
59
|
-
Assert.ok(start_idx !== -1,
|
|
59
|
+
const start_idx = words.indexOf("OP_0");
|
|
60
|
+
Assert.ok(start_idx !== -1, "inscription envelope not found");
|
|
60
61
|
const envelopes = [];
|
|
61
62
|
for (let idx = start_idx; idx < words.length; idx++) {
|
|
62
|
-
Assert.ok(
|
|
63
|
-
Assert.ok(words[idx +
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
Assert.ok(idx + 2 < words.length, "incomplete envelope: missing OP_IF or magic bytes");
|
|
64
|
+
Assert.ok(words[idx + 1] === "OP_IF", "OP_IF missing from envelope");
|
|
65
|
+
Assert.ok(words[idx + 2] === "6f7264", "magic bytes missing from envelope");
|
|
66
|
+
const stop_idx = words.indexOf("OP_ENDIF", idx);
|
|
67
|
+
Assert.ok(stop_idx !== -1, "inscription envelope missing OP_ENDIF statement");
|
|
66
68
|
const env = words.slice(idx + 3, stop_idx);
|
|
67
69
|
envelopes.push(env);
|
|
68
|
-
idx
|
|
70
|
+
idx = stop_idx;
|
|
69
71
|
}
|
|
70
72
|
return envelopes;
|
|
71
73
|
}
|
|
@@ -73,35 +75,35 @@ function parse_record(envelope) {
|
|
|
73
75
|
const record = {};
|
|
74
76
|
for (let i = 0; i < envelope.length; i++) {
|
|
75
77
|
switch (envelope[i]) {
|
|
76
|
-
case
|
|
78
|
+
case "OP_1":
|
|
77
79
|
record.mimetype = decode_label(envelope[i + 1]);
|
|
78
80
|
i += 1;
|
|
79
81
|
break;
|
|
80
|
-
case
|
|
82
|
+
case "OP_2":
|
|
81
83
|
record.pointer = decode_pointer(envelope[i + 1]);
|
|
82
84
|
i += 1;
|
|
83
85
|
break;
|
|
84
|
-
case
|
|
86
|
+
case "OP_3":
|
|
85
87
|
record.parent = decode_id(envelope[i + 1]);
|
|
86
88
|
i += 1;
|
|
87
89
|
break;
|
|
88
|
-
case
|
|
90
|
+
case "OP_11":
|
|
89
91
|
record.delegate = decode_id(envelope[i + 1]);
|
|
90
92
|
i += 1;
|
|
91
93
|
break;
|
|
92
|
-
case
|
|
94
|
+
case "OP_13":
|
|
93
95
|
record.rune = decode_rune_label(envelope[i + 1]);
|
|
94
96
|
i += 1;
|
|
95
97
|
break;
|
|
96
|
-
case
|
|
98
|
+
case "OP_WITHIN":
|
|
97
99
|
record.ref = decode_bytes(envelope[i + 1]);
|
|
98
100
|
i += 1;
|
|
99
101
|
break;
|
|
100
|
-
case
|
|
102
|
+
case "OP_NOP":
|
|
101
103
|
record.opcode = decode_pointer(envelope[i + 1]);
|
|
102
104
|
i += 1;
|
|
103
105
|
break;
|
|
104
|
-
case
|
|
106
|
+
case "OP_0":
|
|
105
107
|
record.content = decode_content(envelope.slice(i + 1));
|
|
106
108
|
return record;
|
|
107
109
|
}
|
|
@@ -111,25 +113,41 @@ function parse_record(envelope) {
|
|
|
111
113
|
function decode_bytes(bytes) {
|
|
112
114
|
return Buff.bytes(bytes).hex;
|
|
113
115
|
}
|
|
116
|
+
function normalize_script_word(word) {
|
|
117
|
+
if (typeof word === "string" && word.startsWith("OP_")) {
|
|
118
|
+
if (word === "OP_0")
|
|
119
|
+
return Buff.num(0).hex;
|
|
120
|
+
const match = word.match(/^OP_(\d+)$/);
|
|
121
|
+
if (match) {
|
|
122
|
+
const num = parseInt(match[1], 10);
|
|
123
|
+
if (num >= 1 && num <= 16)
|
|
124
|
+
return Buff.num(num).hex;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return word;
|
|
128
|
+
}
|
|
114
129
|
function encode_id(identifier) {
|
|
115
|
-
Assert.ok(identifier.includes(
|
|
116
|
-
const parts = identifier.split(
|
|
130
|
+
Assert.ok(identifier.includes("i"), "identifier must include an index");
|
|
131
|
+
const parts = identifier.split("i");
|
|
117
132
|
const bytes = Buff.hex(parts[0]);
|
|
118
133
|
const idx = Number(parts[1]);
|
|
119
134
|
const txid = bytes.reverse().hex;
|
|
120
|
-
return
|
|
135
|
+
return idx !== 0 ? txid + Buff.num(idx).hex : txid;
|
|
121
136
|
}
|
|
122
137
|
function decode_id(identifier) {
|
|
123
138
|
const bytes = Buff.bytes(identifier);
|
|
139
|
+
if (bytes.length === 32) {
|
|
140
|
+
return `${bytes.reverse().hex}i0`;
|
|
141
|
+
}
|
|
124
142
|
const idx = bytes.at(-1) ?? 0;
|
|
125
143
|
const txid = bytes.slice(0, -1).reverse().hex;
|
|
126
|
-
return txid
|
|
144
|
+
return `${txid}i${String(idx)}`;
|
|
127
145
|
}
|
|
128
146
|
function encode_pointer(pointer) {
|
|
129
147
|
return Buff.num(pointer).reverse().hex;
|
|
130
148
|
}
|
|
131
149
|
function decode_pointer(bytes) {
|
|
132
|
-
return Buff.bytes(bytes).reverse().num;
|
|
150
|
+
return Buff.bytes(normalize_script_word(bytes)).reverse().num;
|
|
133
151
|
}
|
|
134
152
|
function encode_label(label) {
|
|
135
153
|
return Buff.str(label).hex;
|
|
@@ -138,9 +156,7 @@ function decode_label(label) {
|
|
|
138
156
|
return Buff.bytes(label).str;
|
|
139
157
|
}
|
|
140
158
|
function encode_content(content) {
|
|
141
|
-
const bytes = Buff.is_hex(content)
|
|
142
|
-
? Buff.hex(content)
|
|
143
|
-
: Buff.str(content);
|
|
159
|
+
const bytes = Buff.is_hex(content) ? Buff.hex(content) : Buff.str(content);
|
|
144
160
|
const stream = new Stream(bytes);
|
|
145
161
|
const chunks = [];
|
|
146
162
|
while (stream.size > 0) {
|
|
@@ -155,38 +171,37 @@ function encode_content(content) {
|
|
|
155
171
|
}
|
|
156
172
|
return chunks;
|
|
157
173
|
}
|
|
158
|
-
function decode_content(chunks, format =
|
|
174
|
+
function decode_content(chunks, format = "hex") {
|
|
159
175
|
const data = Buff.join(chunks);
|
|
160
|
-
return
|
|
161
|
-
? data.hex
|
|
162
|
-
: data.str;
|
|
176
|
+
return format === "hex" ? data.hex : data.str;
|
|
163
177
|
}
|
|
164
178
|
function encode_rune_label(label) {
|
|
165
179
|
const str = label.toUpperCase();
|
|
166
180
|
let big = _0n;
|
|
167
181
|
for (const char of str) {
|
|
168
|
-
if (char >=
|
|
169
|
-
big = big * _26n + BigInt(char.charCodeAt(0) - (
|
|
182
|
+
if (char >= "A" && char <= "Z") {
|
|
183
|
+
big = big * _26n + BigInt(char.charCodeAt(0) - ("A".charCodeAt(0) - 1));
|
|
170
184
|
}
|
|
171
185
|
else {
|
|
172
|
-
|
|
186
|
+
throw new ValidationError(`invalid character in rune label: '${char}' (only A-Z allowed)`, "label");
|
|
173
187
|
}
|
|
174
188
|
}
|
|
175
189
|
big = big - _1n;
|
|
176
190
|
return Buff.big(big).reverse().hex;
|
|
177
191
|
}
|
|
178
192
|
function decode_rune_label(label) {
|
|
179
|
-
|
|
193
|
+
const normalized = normalize_script_word(label);
|
|
194
|
+
let big = Buff.bytes(normalized).reverse().big;
|
|
180
195
|
big = big + _1n;
|
|
181
|
-
let result =
|
|
196
|
+
let result = "";
|
|
182
197
|
while (big > _0n) {
|
|
183
198
|
const mod = big % _26n;
|
|
184
199
|
if (mod === _0n) {
|
|
185
|
-
result =
|
|
200
|
+
result = `Z${result}`;
|
|
186
201
|
big = big / _26n - _1n;
|
|
187
202
|
}
|
|
188
203
|
else {
|
|
189
|
-
const charCode = Number(mod) +
|
|
204
|
+
const charCode = Number(mod) + "A".charCodeAt(0) - 1;
|
|
190
205
|
result = String.fromCharCode(charCode) + result;
|
|
191
206
|
big = big / _26n;
|
|
192
207
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SequenceConfig, SequenceData } from
|
|
1
|
+
import type { SequenceConfig, SequenceData } from "../../types/index.js";
|
|
2
2
|
export declare namespace SequenceField {
|
|
3
3
|
const encode: typeof encode_sequence;
|
|
4
4
|
const decode: typeof decode_sequence;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { ValidationError } from "../../error.js";
|
|
1
2
|
const TIMELOCK_DISABLE = 0x80000000;
|
|
2
3
|
const TIMELOCK_TYPE = 0x00400000;
|
|
3
|
-
const TIMELOCK_VALUE_MASK =
|
|
4
|
-
const TIMELOCK_VALUE_MAX =
|
|
4
|
+
const TIMELOCK_VALUE_MASK = 0x0000ffff;
|
|
5
|
+
const TIMELOCK_VALUE_MAX = 0xffff;
|
|
5
6
|
const TIMELOCK_GRANULARITY = 512;
|
|
6
7
|
export var SequenceField;
|
|
7
8
|
(function (SequenceField) {
|
|
@@ -9,15 +10,15 @@ export var SequenceField;
|
|
|
9
10
|
SequenceField.decode = decode_sequence;
|
|
10
11
|
})(SequenceField || (SequenceField = {}));
|
|
11
12
|
export function encode_sequence(data) {
|
|
12
|
-
if (data.mode ===
|
|
13
|
+
if (data.mode === "height") {
|
|
13
14
|
const height = parse_height(data.height);
|
|
14
15
|
return (height & TIMELOCK_VALUE_MASK) >>> 0;
|
|
15
16
|
}
|
|
16
|
-
if (data.mode ===
|
|
17
|
+
if (data.mode === "stamp") {
|
|
17
18
|
const stamp = parse_stamp(data.stamp);
|
|
18
19
|
return (TIMELOCK_TYPE | (stamp & TIMELOCK_VALUE_MASK)) >>> 0;
|
|
19
20
|
}
|
|
20
|
-
throw new
|
|
21
|
+
throw new ValidationError(`invalid timelock mode: "${data.mode}". Valid modes are "height" or "stamp"`);
|
|
21
22
|
}
|
|
22
23
|
export function decode_sequence(sequence) {
|
|
23
24
|
const seq = parse_sequence(sequence);
|
|
@@ -26,40 +27,41 @@ export function decode_sequence(sequence) {
|
|
|
26
27
|
const value = seq & TIMELOCK_VALUE_MASK;
|
|
27
28
|
if (seq & TIMELOCK_TYPE) {
|
|
28
29
|
const stamp = value * TIMELOCK_GRANULARITY;
|
|
29
|
-
if (stamp >
|
|
30
|
-
throw new
|
|
30
|
+
if (stamp > 0xffffffff) {
|
|
31
|
+
throw new ValidationError(`decoded timestamp ${stamp} exceeds 32-bit limit (max: ${0xffffffff})`);
|
|
31
32
|
}
|
|
32
|
-
return { mode:
|
|
33
|
+
return { mode: "stamp", stamp };
|
|
33
34
|
}
|
|
34
35
|
else {
|
|
35
36
|
if (value > TIMELOCK_VALUE_MAX) {
|
|
36
|
-
throw new
|
|
37
|
+
throw new ValidationError(`decoded height ${value} exceeds maximum (${TIMELOCK_VALUE_MAX})`);
|
|
37
38
|
}
|
|
38
|
-
return { mode:
|
|
39
|
+
return { mode: "height", height: value };
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
function parse_sequence(sequence) {
|
|
42
|
-
const seq =
|
|
43
|
-
|
|
44
|
-
:
|
|
45
|
-
if (!Number.isInteger(seq) || seq < 0 || seq > 0xFFFFFFFF) {
|
|
46
|
-
throw new Error(`invalid sequence value: ${seq}`);
|
|
43
|
+
const seq = typeof sequence === "string" ? parseInt(sequence, 16) : sequence;
|
|
44
|
+
if (!Number.isInteger(seq) || seq < 0 || seq > 0xffffffff) {
|
|
45
|
+
throw new ValidationError(`invalid sequence value: ${seq}. Must be an integer between 0 and 0xffffffff`);
|
|
47
46
|
}
|
|
48
47
|
return seq;
|
|
49
48
|
}
|
|
50
49
|
function parse_stamp(stamp) {
|
|
51
50
|
if (stamp === undefined || !Number.isInteger(stamp)) {
|
|
52
|
-
throw new
|
|
51
|
+
throw new ValidationError(`timestamp must be an integer, got: ${stamp}`);
|
|
53
52
|
}
|
|
54
53
|
const ts = Math.floor(stamp / TIMELOCK_GRANULARITY);
|
|
55
54
|
if (!Number.isInteger(ts) || ts < 0 || ts > TIMELOCK_VALUE_MAX) {
|
|
56
|
-
throw new
|
|
55
|
+
throw new ValidationError(`timelock value must be an integer between 0 and ${TIMELOCK_VALUE_MAX} (in 512-second increments)`);
|
|
57
56
|
}
|
|
58
57
|
return ts;
|
|
59
58
|
}
|
|
60
59
|
function parse_height(height) {
|
|
61
|
-
if (height === undefined ||
|
|
62
|
-
|
|
60
|
+
if (height === undefined ||
|
|
61
|
+
!Number.isInteger(height) ||
|
|
62
|
+
height < 0 ||
|
|
63
|
+
height > TIMELOCK_VALUE_MAX) {
|
|
64
|
+
throw new ValidationError(`heightlock value must be an integer between 0 and ${TIMELOCK_VALUE_MAX}, got: ${height}`);
|
|
63
65
|
}
|
|
64
66
|
return height;
|
|
65
67
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Bytes } from
|
|
2
|
-
import type { ScriptInfo } from
|
|
1
|
+
import { type Bytes } from "@vbyte/buff";
|
|
2
|
+
import type { ScriptInfo } from "../../types/script.js";
|
|
3
3
|
export declare function parse_script(script: Bytes): ScriptInfo;
|
|
4
4
|
export declare function decode_script(script: Bytes): string[];
|
|
5
5
|
export declare function is_valid_script(script: string | Uint8Array): boolean;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { Buff, Stream } from
|
|
2
|
-
import {
|
|
1
|
+
import { Buff, Stream } from "@vbyte/buff";
|
|
2
|
+
import { DecodingError } from "../../error.js";
|
|
3
|
+
import { get_op_code, get_op_type, is_valid_op } from "./words.js";
|
|
3
4
|
export function parse_script(script) {
|
|
4
5
|
const bytes = Buff.bytes(script);
|
|
5
6
|
return {
|
|
6
7
|
asm: decode_script(bytes),
|
|
7
|
-
hex: bytes.hex
|
|
8
|
+
hex: bytes.hex,
|
|
8
9
|
};
|
|
9
10
|
}
|
|
10
11
|
export function decode_script(script) {
|
|
@@ -20,33 +21,68 @@ export function decode_script(script) {
|
|
|
20
21
|
word_type = get_op_type(word);
|
|
21
22
|
count++;
|
|
22
23
|
switch (word_type) {
|
|
23
|
-
case
|
|
24
|
-
|
|
24
|
+
case "varint":
|
|
25
|
+
try {
|
|
26
|
+
stack.push(stream.read(word).hex);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
throw new DecodingError(`Malformed script: varint push at position ${count - 1} requires ${word} bytes but stream exhausted`, count - 1);
|
|
30
|
+
}
|
|
25
31
|
count += word;
|
|
26
32
|
break;
|
|
27
|
-
case
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
case "pushdata1":
|
|
34
|
+
try {
|
|
35
|
+
word_size = stream.read(1).reverse().num;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
throw new DecodingError(`Malformed script: PUSHDATA1 at position ${count - 1} missing size byte`, count - 1);
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
stack.push(stream.read(word_size).hex);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
throw new DecodingError(`Malformed script: PUSHDATA1 at position ${count - 1} requires ${word_size} bytes but stream exhausted`, count - 1);
|
|
45
|
+
}
|
|
30
46
|
count += word_size + 1;
|
|
31
47
|
break;
|
|
32
|
-
case
|
|
33
|
-
|
|
34
|
-
|
|
48
|
+
case "pushdata2":
|
|
49
|
+
try {
|
|
50
|
+
word_size = stream.read(2).reverse().num;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
throw new DecodingError(`Malformed script: PUSHDATA2 at position ${count - 1} missing size bytes`, count - 1);
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
stack.push(stream.read(word_size).hex);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
throw new DecodingError(`Malformed script: PUSHDATA2 at position ${count - 1} requires ${word_size} bytes but stream exhausted`, count - 1);
|
|
60
|
+
}
|
|
35
61
|
count += word_size + 2;
|
|
36
62
|
break;
|
|
37
|
-
case
|
|
38
|
-
|
|
39
|
-
|
|
63
|
+
case "pushdata4":
|
|
64
|
+
try {
|
|
65
|
+
word_size = stream.read(4).reverse().num;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
throw new DecodingError(`Malformed script: PUSHDATA4 at position ${count - 1} missing size bytes`, count - 1);
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
stack.push(stream.read(word_size).hex);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
throw new DecodingError(`Malformed script: PUSHDATA4 at position ${count - 1} requires ${word_size} bytes but stream exhausted`, count - 1);
|
|
75
|
+
}
|
|
40
76
|
count += word_size + 4;
|
|
41
77
|
break;
|
|
42
|
-
case
|
|
78
|
+
case "opcode":
|
|
43
79
|
if (!is_valid_op(word)) {
|
|
44
|
-
throw new
|
|
80
|
+
throw new DecodingError(`Invalid OPCODE: ${word}`, count - 1);
|
|
45
81
|
}
|
|
46
82
|
stack.push(get_op_code(word));
|
|
47
83
|
break;
|
|
48
84
|
default:
|
|
49
|
-
throw new
|
|
85
|
+
throw new DecodingError(`Word type undefined: ${word}`, count - 1);
|
|
50
86
|
}
|
|
51
87
|
}
|
|
52
88
|
return stack;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Buff } from
|
|
1
|
+
import { Buff } from "@vbyte/buff";
|
|
2
2
|
export declare function encode_script(words: (string | number | Uint8Array)[], varint?: boolean): Buff;
|
|
3
3
|
export declare function encode_script_word(word: string | number | Uint8Array): Uint8Array;
|
|
4
4
|
export declare function split_script_word(word: Uint8Array): Buff[];
|