@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.
Files changed (181) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/LICENSE +21 -121
  3. package/README.md +69 -3
  4. package/dist/const.d.ts +3 -0
  5. package/dist/const.js +23 -22
  6. package/dist/error.d.ts +11 -0
  7. package/dist/error.js +20 -0
  8. package/dist/index.d.ts +12 -11
  9. package/dist/index.js +11 -10
  10. package/dist/lib/address/api.d.ts +2 -2
  11. package/dist/lib/address/api.js +13 -12
  12. package/dist/lib/address/encode.d.ts +1 -1
  13. package/dist/lib/address/encode.js +26 -24
  14. package/dist/lib/address/index.d.ts +6 -6
  15. package/dist/lib/address/index.js +6 -6
  16. package/dist/lib/address/p2pkh.d.ts +2 -2
  17. package/dist/lib/address/p2pkh.js +15 -15
  18. package/dist/lib/address/p2sh.d.ts +2 -2
  19. package/dist/lib/address/p2sh.js +14 -14
  20. package/dist/lib/address/p2tr.d.ts +2 -2
  21. package/dist/lib/address/p2tr.js +14 -14
  22. package/dist/lib/address/p2wpkh.d.ts +2 -2
  23. package/dist/lib/address/p2wpkh.js +15 -15
  24. package/dist/lib/address/p2wsh.d.ts +2 -2
  25. package/dist/lib/address/p2wsh.js +14 -14
  26. package/dist/lib/address/script.d.ts +1 -1
  27. package/dist/lib/address/script.js +16 -16
  28. package/dist/lib/address/util.d.ts +1 -1
  29. package/dist/lib/address/util.js +24 -22
  30. package/dist/lib/meta/index.d.ts +4 -4
  31. package/dist/lib/meta/index.js +4 -4
  32. package/dist/lib/meta/locktime.d.ts +1 -1
  33. package/dist/lib/meta/locktime.js +13 -12
  34. package/dist/lib/meta/ref.js +13 -9
  35. package/dist/lib/meta/scribe.d.ts +2 -2
  36. package/dist/lib/meta/scribe.js +71 -56
  37. package/dist/lib/meta/sequence.d.ts +1 -1
  38. package/dist/lib/meta/sequence.js +21 -19
  39. package/dist/lib/script/decode.d.ts +2 -2
  40. package/dist/lib/script/decode.js +53 -17
  41. package/dist/lib/script/encode.d.ts +1 -1
  42. package/dist/lib/script/encode.js +21 -16
  43. package/dist/lib/script/index.d.ts +5 -13
  44. package/dist/lib/script/index.js +5 -14
  45. package/dist/lib/script/lock.d.ts +2 -2
  46. package/dist/lib/script/lock.js +15 -12
  47. package/dist/lib/script/util.js +4 -4
  48. package/dist/lib/script/words.js +131 -130
  49. package/dist/lib/sighash/index.d.ts +3 -3
  50. package/dist/lib/sighash/index.js +3 -3
  51. package/dist/lib/sighash/segwit.d.ts +2 -2
  52. package/dist/lib/sighash/segwit.js +18 -14
  53. package/dist/lib/sighash/taproot.d.ts +2 -2
  54. package/dist/lib/sighash/taproot.js +27 -23
  55. package/dist/lib/sighash/util.d.ts +2 -2
  56. package/dist/lib/sighash/util.js +8 -7
  57. package/dist/lib/signer/index.d.ts +2 -2
  58. package/dist/lib/signer/index.js +2 -2
  59. package/dist/lib/signer/sign.d.ts +1 -1
  60. package/dist/lib/signer/sign.js +43 -7
  61. package/dist/lib/signer/verify.d.ts +17 -3
  62. package/dist/lib/signer/verify.js +232 -3
  63. package/dist/lib/taproot/cblock.d.ts +1 -1
  64. package/dist/lib/taproot/cblock.js +16 -17
  65. package/dist/lib/taproot/encode.d.ts +1 -1
  66. package/dist/lib/taproot/encode.js +9 -8
  67. package/dist/lib/taproot/index.d.ts +4 -4
  68. package/dist/lib/taproot/index.js +4 -4
  69. package/dist/lib/taproot/parse.d.ts +1 -1
  70. package/dist/lib/taproot/parse.js +15 -15
  71. package/dist/lib/taproot/tree.d.ts +2 -2
  72. package/dist/lib/taproot/tree.js +12 -7
  73. package/dist/lib/tx/create.d.ts +1 -1
  74. package/dist/lib/tx/create.js +28 -12
  75. package/dist/lib/tx/decode.d.ts +2 -2
  76. package/dist/lib/tx/decode.js +52 -17
  77. package/dist/lib/tx/encode.d.ts +2 -2
  78. package/dist/lib/tx/encode.js +13 -16
  79. package/dist/lib/tx/index.d.ts +7 -7
  80. package/dist/lib/tx/index.js +7 -7
  81. package/dist/lib/tx/parse.d.ts +1 -1
  82. package/dist/lib/tx/parse.js +9 -9
  83. package/dist/lib/tx/size.d.ts +2 -2
  84. package/dist/lib/tx/size.js +9 -11
  85. package/dist/lib/tx/util.d.ts +2 -2
  86. package/dist/lib/tx/util.js +22 -20
  87. package/dist/lib/tx/validate.d.ts +1 -1
  88. package/dist/lib/tx/validate.js +37 -9
  89. package/dist/lib/witness/index.d.ts +2 -2
  90. package/dist/lib/witness/index.js +2 -2
  91. package/dist/lib/witness/parse.d.ts +2 -2
  92. package/dist/lib/witness/parse.js +24 -23
  93. package/dist/lib/witness/util.d.ts +2 -2
  94. package/dist/lib/witness/util.js +5 -5
  95. package/dist/main.cjs +3305 -2035
  96. package/dist/main.cjs.map +1 -1
  97. package/dist/module.mjs +3303 -2036
  98. package/dist/module.mjs.map +1 -1
  99. package/dist/package.json +24 -19
  100. package/dist/schema/base.d.ts +1 -1
  101. package/dist/schema/base.js +17 -13
  102. package/dist/schema/index.d.ts +2 -2
  103. package/dist/schema/index.js +2 -2
  104. package/dist/schema/taproot.d.ts +1 -1
  105. package/dist/schema/taproot.js +2 -2
  106. package/dist/schema/tx.d.ts +1 -1
  107. package/dist/schema/tx.js +4 -4
  108. package/dist/script.js +10 -12
  109. package/dist/script.js.map +1 -1
  110. package/dist/types/address.d.ts +4 -4
  111. package/dist/types/index.d.ts +8 -8
  112. package/dist/types/index.js +8 -8
  113. package/dist/types/meta.d.ts +4 -4
  114. package/dist/types/psbt.d.ts +2 -2
  115. package/dist/types/script.d.ts +2 -2
  116. package/dist/types/sighash.d.ts +2 -2
  117. package/dist/types/witness.d.ts +5 -5
  118. package/docs/API.md +1145 -0
  119. package/docs/CONVENTIONS.md +316 -0
  120. package/docs/FAQ.md +396 -0
  121. package/docs/GUIDE.md +1102 -0
  122. package/package.json +24 -19
  123. package/src/const.ts +0 -61
  124. package/src/index.ts +0 -13
  125. package/src/lib/address/api.ts +0 -50
  126. package/src/lib/address/encode.ts +0 -183
  127. package/src/lib/address/index.ts +0 -7
  128. package/src/lib/address/p2pkh.ts +0 -94
  129. package/src/lib/address/p2sh.ts +0 -96
  130. package/src/lib/address/p2tr.ts +0 -91
  131. package/src/lib/address/p2wpkh.ts +0 -94
  132. package/src/lib/address/p2wsh.ts +0 -92
  133. package/src/lib/address/script.ts +0 -63
  134. package/src/lib/address/util.ts +0 -87
  135. package/src/lib/meta/index.ts +0 -4
  136. package/src/lib/meta/locktime.ts +0 -57
  137. package/src/lib/meta/ref.ts +0 -107
  138. package/src/lib/meta/scribe.ts +0 -256
  139. package/src/lib/meta/sequence.ts +0 -146
  140. package/src/lib/script/decode.ts +0 -85
  141. package/src/lib/script/encode.ts +0 -129
  142. package/src/lib/script/index.ts +0 -20
  143. package/src/lib/script/lock.ts +0 -73
  144. package/src/lib/script/util.ts +0 -78
  145. package/src/lib/script/words.ts +0 -182
  146. package/src/lib/sighash/index.ts +0 -3
  147. package/src/lib/sighash/segwit.ts +0 -152
  148. package/src/lib/sighash/taproot.ts +0 -206
  149. package/src/lib/sighash/util.ts +0 -51
  150. package/src/lib/signer/index.ts +0 -2
  151. package/src/lib/signer/sign.ts +0 -39
  152. package/src/lib/signer/verify.ts +0 -88
  153. package/src/lib/taproot/cblock.ts +0 -96
  154. package/src/lib/taproot/encode.ts +0 -49
  155. package/src/lib/taproot/index.ts +0 -4
  156. package/src/lib/taproot/parse.ts +0 -65
  157. package/src/lib/taproot/tree.ts +0 -94
  158. package/src/lib/tx/create.ts +0 -90
  159. package/src/lib/tx/decode.ts +0 -123
  160. package/src/lib/tx/encode.ts +0 -155
  161. package/src/lib/tx/index.ts +0 -7
  162. package/src/lib/tx/parse.ts +0 -69
  163. package/src/lib/tx/size.ts +0 -68
  164. package/src/lib/tx/util.ts +0 -111
  165. package/src/lib/tx/validate.ts +0 -49
  166. package/src/lib/witness/index.ts +0 -2
  167. package/src/lib/witness/parse.ts +0 -127
  168. package/src/lib/witness/util.ts +0 -18
  169. package/src/schema/base.ts +0 -57
  170. package/src/schema/index.ts +0 -2
  171. package/src/schema/taproot.ts +0 -12
  172. package/src/schema/tx.ts +0 -48
  173. package/src/types/address.ts +0 -35
  174. package/src/types/index.ts +0 -8
  175. package/src/types/meta.ts +0 -48
  176. package/src/types/psbt.ts +0 -15
  177. package/src/types/script.ts +0 -18
  178. package/src/types/sighash.ts +0 -16
  179. package/src/types/taproot.ts +0 -41
  180. package/src/types/txdata.ts +0 -85
  181. package/src/types/witness.ts +0 -42
@@ -1,4 +1,4 @@
1
- export * from './locktime.js';
2
- export * from './ref.js';
3
- export * from './scribe.js';
4
- export * from './sequence.js';
1
+ export * from "./locktime.js";
2
+ export * from "./ref.js";
3
+ export * from "./scribe.js";
4
+ export * from "./sequence.js";
@@ -1,4 +1,4 @@
1
- import type { LocktimeData } from '../../types/index.js';
1
+ import type { LocktimeData } from "../../types/index.js";
2
2
  export declare namespace LocktimeField {
3
3
  const encode: typeof encode_locktime;
4
4
  const decode: typeof decode_locktime;
@@ -1,4 +1,5 @@
1
- import { Assert } from '@vbyte/micro-lib';
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 'timelock':
11
- Assert.ok(locktime.stamp >= LOCKTIME_THRESHOLD, 'Invalid timestamp');
11
+ case "timelock":
12
+ Assert.ok(locktime.stamp >= LOCKTIME_THRESHOLD, "Invalid timestamp");
12
13
  return locktime.stamp;
13
- case 'heightlock':
14
- Assert.ok(locktime.height > 0, 'height must be greater than 0');
15
- Assert.ok(locktime.height < LOCKTIME_THRESHOLD, 'invalid block height');
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 Error('Invalid locktime type');
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: 'heightlock',
28
- height: locktime
28
+ type: "heightlock",
29
+ height: locktime,
29
30
  };
30
31
  }
31
32
  else {
32
33
  return {
33
- type: 'timelock',
34
- stamp: locktime
34
+ type: "timelock",
35
+ stamp: locktime,
35
36
  };
36
37
  }
37
38
  }
@@ -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('i');
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 Error(`invalid inscription id: ${inscription_id}`);
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 { block_height: parseInt(block_height), block_index: parseInt(block_index) };
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 Error(`invalid rune id: ${rune_id}`);
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 Error(`invalid outpoint: ${outpoint}`);
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 '@vbyte/buff';
2
- import type { InscriptionData } from '../../types/index.js';
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;
@@ -1,7 +1,8 @@
1
- import { Buff, Stream } from '@vbyte/buff';
2
- import { Assert } from '@vbyte/micro-lib';
3
- import { encode_script } from '../../lib/script/encode.js';
4
- import { decode_script } from '../../lib/script/decode.js';
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
- let asm = ['OP_0', 'OP_IF', '6f7264'];
22
- if (typeof data.delegate === 'string') {
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('OP_11', id);
25
+ asm.push("OP_11", id);
25
26
  }
26
- if (typeof data.ref === 'string') {
27
- asm.push('OP_WITHIN', data.ref);
27
+ if (typeof data.ref === "string") {
28
+ asm.push("OP_WITHIN", data.ref);
28
29
  }
29
- if (typeof data.parent === 'string') {
30
+ if (typeof data.parent === "string") {
30
31
  const id = encode_id(data.parent);
31
- asm.push('OP_3', id);
32
+ asm.push("OP_3", id);
32
33
  }
33
- if (typeof data.opcode === 'number') {
34
+ if (typeof data.opcode === "number") {
34
35
  const code = encode_pointer(data.opcode);
35
- asm.push('OP_NOP', code);
36
+ asm.push("OP_NOP", code);
36
37
  }
37
- if (typeof data.pointer === 'number') {
38
+ if (typeof data.pointer === "number") {
38
39
  const ptr = encode_pointer(data.pointer);
39
- asm.push('OP_2', ptr);
40
+ asm.push("OP_2", ptr);
40
41
  }
41
- if (typeof data.rune === 'string') {
42
+ if (typeof data.rune === "string") {
42
43
  const label = encode_rune_label(data.rune);
43
- asm.push('OP_13', label);
44
+ asm.push("OP_13", label);
44
45
  }
45
- if (typeof data.mimetype === 'string') {
46
+ if (typeof data.mimetype === "string") {
46
47
  const label = encode_label(data.mimetype);
47
- asm.push('OP_1', label);
48
+ asm.push("OP_1", label);
48
49
  }
49
- if (typeof data.content === 'string') {
50
+ if (typeof data.content === "string") {
50
51
  const chunks = encode_content(data.content);
51
- asm.push('OP_0', ...chunks);
52
+ asm.push("OP_0", ...chunks);
52
53
  }
53
- asm.push('OP_ENDIF');
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.findIndex(e => e === 'OP_0');
59
- Assert.ok(start_idx !== -1, 'inscription envelope not found');
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(words[idx + 1] === 'OP_IF', 'OP_IF missing from envelope');
63
- Assert.ok(words[idx + 2] === '6f7264', 'magic bytes missing from envelope');
64
- const stop_idx = words.findIndex(e => e === 'OP_ENDIF');
65
- Assert.ok(stop_idx !== -1, 'inscription envelope missing END_IF statement');
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 += stop_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 'OP_1':
78
+ case "OP_1":
77
79
  record.mimetype = decode_label(envelope[i + 1]);
78
80
  i += 1;
79
81
  break;
80
- case 'OP_2':
82
+ case "OP_2":
81
83
  record.pointer = decode_pointer(envelope[i + 1]);
82
84
  i += 1;
83
85
  break;
84
- case 'OP_3':
86
+ case "OP_3":
85
87
  record.parent = decode_id(envelope[i + 1]);
86
88
  i += 1;
87
89
  break;
88
- case 'OP_11':
90
+ case "OP_11":
89
91
  record.delegate = decode_id(envelope[i + 1]);
90
92
  i += 1;
91
93
  break;
92
- case 'OP_13':
94
+ case "OP_13":
93
95
  record.rune = decode_rune_label(envelope[i + 1]);
94
96
  i += 1;
95
97
  break;
96
- case 'OP_WITHIN':
98
+ case "OP_WITHIN":
97
99
  record.ref = decode_bytes(envelope[i + 1]);
98
100
  i += 1;
99
101
  break;
100
- case 'OP_NOP':
102
+ case "OP_NOP":
101
103
  record.opcode = decode_pointer(envelope[i + 1]);
102
104
  i += 1;
103
105
  break;
104
- case 'OP_0':
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('i'), 'identifier must include an index');
116
- const parts = identifier.split('i');
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 (idx !== 0) ? txid + Buff.num(idx).hex : txid;
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 + 'i' + String(idx);
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 = 'hex') {
174
+ function decode_content(chunks, format = "hex") {
159
175
  const data = Buff.join(chunks);
160
- return (format === 'hex')
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 >= 'A' && char <= 'Z') {
169
- big = big * _26n + BigInt(char.charCodeAt(0) - ('A'.charCodeAt(0) - 1));
182
+ if (char >= "A" && char <= "Z") {
183
+ big = big * _26n + BigInt(char.charCodeAt(0) - ("A".charCodeAt(0) - 1));
170
184
  }
171
185
  else {
172
- continue;
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
- let big = Buff.bytes(label).reverse().big;
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 = 'Z' + result;
200
+ result = `Z${result}`;
186
201
  big = big / _26n - _1n;
187
202
  }
188
203
  else {
189
- const charCode = Number(mod) + 'A'.charCodeAt(0) - 1;
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 '../../types/index.js';
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 = 0x0000FFFF;
4
- const TIMELOCK_VALUE_MAX = 0xFFFF;
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 === 'height') {
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 === 'stamp') {
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 Error('invalid timelock mode: ' + data.mode);
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 > 0xFFFFFFFF) {
30
- throw new Error('Decoded timestamp exceeds 32-bit limit');
30
+ if (stamp > 0xffffffff) {
31
+ throw new ValidationError(`decoded timestamp ${stamp} exceeds 32-bit limit (max: ${0xffffffff})`);
31
32
  }
32
- return { mode: 'stamp', stamp };
33
+ return { mode: "stamp", stamp };
33
34
  }
34
35
  else {
35
36
  if (value > TIMELOCK_VALUE_MAX) {
36
- throw new Error('Decoded height exceeds maximum');
37
+ throw new ValidationError(`decoded height ${value} exceeds maximum (${TIMELOCK_VALUE_MAX})`);
37
38
  }
38
- return { mode: 'height', height: value };
39
+ return { mode: "height", height: value };
39
40
  }
40
41
  }
41
42
  function parse_sequence(sequence) {
42
- const seq = (typeof sequence === 'string')
43
- ? parseInt(sequence, 16)
44
- : sequence;
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 Error(`timestamp must be a number`);
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 Error(`timelock value must be an integer between 0 and ${TIMELOCK_VALUE_MAX} (in 512-second increments)`);
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 || !Number.isInteger(height) || height < 0 || height > TIMELOCK_VALUE_MAX) {
62
- throw new Error(`Heightlock value must be an integer between 0 and ${TIMELOCK_VALUE_MAX}`);
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 '@vbyte/buff';
2
- import type { ScriptInfo } from '../../types/script.js';
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 '@vbyte/buff';
2
- import { get_op_code, get_op_type, is_valid_op } from './words.js';
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 'varint':
24
- stack.push(stream.read(word).hex);
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 'pushdata1':
28
- word_size = stream.read(1).reverse().num;
29
- stack.push(stream.read(word_size).hex);
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 'pushdata2':
33
- word_size = stream.read(2).reverse().num;
34
- stack.push(stream.read(word_size).hex);
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 'pushdata4':
38
- word_size = stream.read(4).reverse().num;
39
- stack.push(stream.read(word_size).hex);
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 'opcode':
78
+ case "opcode":
43
79
  if (!is_valid_op(word)) {
44
- throw new Error(`Invalid OPCODE: ${word}`);
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 Error(`Word type undefined: ${word}`);
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 '@vbyte/buff';
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[];