@vbyte/btc-dev 2.0.0 → 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 +33 -0
- package/LICENSE +21 -121
- package/README.md +36 -227
- package/dist/error.d.ts +11 -0
- package/dist/error.js +20 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/address/api.js +3 -2
- package/dist/lib/address/encode.js +6 -4
- package/dist/lib/address/p2pkh.js +4 -4
- package/dist/lib/address/p2sh.js +3 -3
- package/dist/lib/address/p2tr.js +3 -3
- package/dist/lib/address/p2wpkh.js +4 -4
- package/dist/lib/address/p2wsh.js +3 -3
- package/dist/lib/address/util.js +3 -1
- package/dist/lib/meta/locktime.js +3 -2
- package/dist/lib/meta/ref.js +4 -3
- package/dist/lib/meta/scribe.js +26 -6
- package/dist/lib/meta/sequence.js +8 -7
- package/dist/lib/script/decode.js +10 -9
- package/dist/lib/script/encode.js +4 -3
- package/dist/lib/script/words.js +4 -3
- package/dist/lib/sighash/segwit.js +9 -6
- package/dist/lib/sighash/taproot.js +7 -4
- package/dist/lib/sighash/util.js +4 -3
- package/dist/lib/signer/sign.js +7 -6
- package/dist/lib/signer/verify.js +8 -9
- package/dist/lib/taproot/cblock.js +3 -2
- package/dist/lib/taproot/encode.d.ts +1 -1
- package/dist/lib/taproot/encode.js +4 -3
- package/dist/lib/taproot/parse.js +9 -7
- package/dist/lib/taproot/tree.js +3 -2
- package/dist/lib/tx/create.js +1 -1
- package/dist/lib/tx/decode.js +13 -11
- package/dist/lib/tx/encode.js +1 -1
- package/dist/lib/tx/parse.js +3 -3
- package/dist/lib/tx/size.js +2 -4
- package/dist/lib/tx/util.js +2 -3
- package/dist/lib/tx/validate.js +36 -8
- package/dist/lib/witness/util.js +1 -1
- package/dist/main.cjs +1127 -1160
- package/dist/main.cjs.map +1 -1
- package/dist/module.mjs +1125 -1161
- package/dist/module.mjs.map +1 -1
- package/dist/package.json +13 -11
- package/dist/script.js +10 -12
- package/dist/script.js.map +1 -1
- 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 +13 -11
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Buff, Stream } from "@vbyte/buff";
|
|
2
|
-
import { Assert
|
|
2
|
+
import { Assert } from "@vbyte/util";
|
|
3
|
+
import { ECC } from "@vbyte/crypto";
|
|
4
|
+
import { DecodingError, ValidationError } from "../../error.js";
|
|
3
5
|
import { parse_witness } from "../../lib/witness/parse.js";
|
|
4
6
|
import { encode_tapbranch, encode_tapscript, encode_taptweak, } from "./encode.js";
|
|
5
7
|
export function parse_taproot_witness(witness) {
|
|
@@ -14,8 +16,8 @@ export function parse_taproot_witness(witness) {
|
|
|
14
16
|
}
|
|
15
17
|
const tweak = encode_taptweak(cblk.int_key, branch);
|
|
16
18
|
const tapkey = ECC.tweak_pubkey(cblk.int_key, tweak, "bip340");
|
|
17
|
-
params.map((e) => Buff.bytes(e).hex);
|
|
18
|
-
return { cblock: cblk, params, script, tapkey: tapkey.hex, tweak: tweak.hex };
|
|
19
|
+
const hexParams = params.map((e) => Buff.bytes(e).hex);
|
|
20
|
+
return { cblock: cblk, params: hexParams, script, tapkey: tapkey.hex, tweak: tweak.hex };
|
|
19
21
|
}
|
|
20
22
|
export function parse_cblock(cblock) {
|
|
21
23
|
const buffer = new Stream(cblock);
|
|
@@ -27,19 +29,19 @@ export function parse_cblock(cblock) {
|
|
|
27
29
|
path.push(buffer.read(32).hex);
|
|
28
30
|
}
|
|
29
31
|
if (buffer.size !== 0) {
|
|
30
|
-
throw new
|
|
32
|
+
throw new DecodingError(`control block has ${buffer.size} extra bytes. Expected: 33 + (32 * path_length) bytes`);
|
|
31
33
|
}
|
|
32
34
|
return { int_key, path, parity, version };
|
|
33
35
|
}
|
|
34
36
|
export function parse_cblock_parity(cbits) {
|
|
35
|
-
return cbits % 2 === 0 ? [cbits
|
|
37
|
+
return cbits % 2 === 0 ? [cbits, 0x02] : [cbits - 1, 0x03];
|
|
36
38
|
}
|
|
37
39
|
export function parse_pubkey_parity(pubkey) {
|
|
38
|
-
Assert.
|
|
40
|
+
Assert.ok(Buff.bytes(pubkey).length === 33, "invalid pubkey size");
|
|
39
41
|
const [parity] = Buff.bytes(pubkey);
|
|
40
42
|
if (parity === 0x02)
|
|
41
43
|
return 0;
|
|
42
44
|
if (parity === 0x03)
|
|
43
45
|
return 1;
|
|
44
|
-
throw new
|
|
46
|
+
throw new ValidationError(`invalid pubkey parity prefix: 0x${parity.toString(16)}. Expected 0x02 (even) or 0x03 (odd)`);
|
|
45
47
|
}
|
package/dist/lib/taproot/tree.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Buff } from "@vbyte/buff";
|
|
2
|
+
import { ValidationError } from "../../error.js";
|
|
2
3
|
import { encode_tapbranch } from "./encode.js";
|
|
3
4
|
const MAX_TAPROOT_DEPTH = 128;
|
|
4
5
|
export function get_merkle_root(leaves) {
|
|
@@ -6,12 +7,12 @@ export function get_merkle_root(leaves) {
|
|
|
6
7
|
}
|
|
7
8
|
export function merkleize(taptree, target, path = [], depth = 0) {
|
|
8
9
|
if (depth > MAX_TAPROOT_DEPTH) {
|
|
9
|
-
throw new
|
|
10
|
+
throw new ValidationError(`Taproot tree depth ${depth} exceeds maximum ${MAX_TAPROOT_DEPTH}`, "depth");
|
|
10
11
|
}
|
|
11
12
|
const leaves = [];
|
|
12
13
|
const tree = [];
|
|
13
14
|
if (taptree.length < 1) {
|
|
14
|
-
throw new
|
|
15
|
+
throw new ValidationError("Taproot tree cannot be empty", "taptree");
|
|
15
16
|
}
|
|
16
17
|
for (let i = 0; i < taptree.length; i++) {
|
|
17
18
|
const bytes = taptree[i];
|
package/dist/lib/tx/create.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Assert } from "@vbyte/
|
|
1
|
+
import { Assert } from "@vbyte/util";
|
|
2
2
|
import { COINBASE, DEFAULT } from "../../const.js";
|
|
3
3
|
import { normalize_prevout, normalize_sequence, normalize_value, } from "./util.js";
|
|
4
4
|
import { assert_tx_template, assert_vin_template, assert_vout_template, } from "./validate.js";
|
package/dist/lib/tx/decode.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Stream } from "@vbyte/buff";
|
|
2
|
-
import { Assert } from "@vbyte/
|
|
2
|
+
import { Assert } from "@vbyte/util";
|
|
3
3
|
import { COINBASE, MAX_VARINT_SIZE } from "../../const.js";
|
|
4
|
+
import { DecodingError } from "../../error.js";
|
|
4
5
|
const MAX_TX_SIZE = 4_000_000;
|
|
5
6
|
const MAX_TX_ELEMENTS = 100_000;
|
|
6
7
|
export function decode_tx(txdata, use_segwit = true) {
|
|
7
|
-
Assert.
|
|
8
|
+
Assert.ok(typeof txdata === "string" || txdata instanceof Uint8Array, "txdata must be hex or bytes");
|
|
8
9
|
const txSize = typeof txdata === "string" ? txdata.length / 2 : txdata.length;
|
|
9
10
|
if (txSize > MAX_TX_SIZE) {
|
|
10
|
-
throw new
|
|
11
|
+
throw new DecodingError(`Transaction size ${txSize} exceeds maximum ${MAX_TX_SIZE} bytes`);
|
|
11
12
|
}
|
|
12
13
|
const stream = new Stream(txdata);
|
|
13
14
|
const version = read_version(stream);
|
|
@@ -34,7 +35,7 @@ function check_witness_flag(stream) {
|
|
|
34
35
|
return true;
|
|
35
36
|
}
|
|
36
37
|
else {
|
|
37
|
-
throw new
|
|
38
|
+
throw new DecodingError(`Invalid witness flag: ${flag}`, 1);
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
return false;
|
|
@@ -43,7 +44,7 @@ function read_inputs(stream) {
|
|
|
43
44
|
const inputs = [];
|
|
44
45
|
const vinCount = stream.read_varint();
|
|
45
46
|
if (vinCount > MAX_TX_ELEMENTS) {
|
|
46
|
-
throw new
|
|
47
|
+
throw new DecodingError(`Input count ${vinCount} exceeds maximum ${MAX_TX_ELEMENTS}`);
|
|
47
48
|
}
|
|
48
49
|
for (let i = 0; i < vinCount; i++) {
|
|
49
50
|
const txinput = read_vin(stream);
|
|
@@ -84,14 +85,15 @@ function read_outputs(stream) {
|
|
|
84
85
|
const outputs = [];
|
|
85
86
|
const vcount = stream.read_varint();
|
|
86
87
|
if (vcount > MAX_TX_ELEMENTS) {
|
|
87
|
-
throw new
|
|
88
|
+
throw new DecodingError(`Output count ${vcount} exceeds maximum ${MAX_TX_ELEMENTS}`);
|
|
88
89
|
}
|
|
89
90
|
for (let i = 0; i < vcount; i++) {
|
|
90
91
|
try {
|
|
91
92
|
outputs.push(read_vout(stream));
|
|
92
93
|
}
|
|
93
|
-
catch (
|
|
94
|
-
|
|
94
|
+
catch (error) {
|
|
95
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
96
|
+
throw new DecodingError(`Failed to decode output at index ${i}: ${message}`);
|
|
95
97
|
}
|
|
96
98
|
}
|
|
97
99
|
return outputs;
|
|
@@ -106,12 +108,12 @@ function read_witness(stream) {
|
|
|
106
108
|
const stack = [];
|
|
107
109
|
const count = stream.read_varint();
|
|
108
110
|
if (count > MAX_TX_ELEMENTS) {
|
|
109
|
-
throw new
|
|
111
|
+
throw new DecodingError(`Witness element count ${count} exceeds maximum ${MAX_TX_ELEMENTS}`);
|
|
110
112
|
}
|
|
111
113
|
for (let i = 0; i < count; i++) {
|
|
112
114
|
const element = read_payload(stream);
|
|
113
115
|
if (element === null) {
|
|
114
|
-
throw new
|
|
116
|
+
throw new DecodingError(`Failed to decode witness element at index ${i}`);
|
|
115
117
|
}
|
|
116
118
|
stack.push(element);
|
|
117
119
|
}
|
|
@@ -120,7 +122,7 @@ function read_witness(stream) {
|
|
|
120
122
|
export function read_payload(stream) {
|
|
121
123
|
const size = stream.read_varint("le");
|
|
122
124
|
if (size > MAX_VARINT_SIZE) {
|
|
123
|
-
throw new
|
|
125
|
+
throw new DecodingError(`Payload size ${size} exceeds maximum ${MAX_VARINT_SIZE}`);
|
|
124
126
|
}
|
|
125
127
|
return size > 0 ? stream.read(size).hex : null;
|
|
126
128
|
}
|
package/dist/lib/tx/encode.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Buff } from "@vbyte/buff";
|
|
2
|
-
import { Assert } from "@vbyte/
|
|
2
|
+
import { Assert } from "@vbyte/util";
|
|
3
3
|
import { COINBASE } from "../../const.js";
|
|
4
4
|
import { assert_tx_data } from "./validate.js";
|
|
5
5
|
export function encode_tx(txdata, use_segwit = true) {
|
package/dist/lib/tx/parse.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Assert } from "@vbyte/
|
|
1
|
+
import { Assert } from "@vbyte/util";
|
|
2
2
|
import { create_tx, create_tx_output } from "./create.js";
|
|
3
3
|
import { decode_tx } from "./decode.js";
|
|
4
4
|
import { assert_tx_template } from "./validate.js";
|
|
@@ -31,14 +31,14 @@ export function serialize_tx(txdata) {
|
|
|
31
31
|
if (e.prevout !== null) {
|
|
32
32
|
vin.push({
|
|
33
33
|
script_pk: e.prevout.script_pk,
|
|
34
|
-
value:
|
|
34
|
+
value: String(e.prevout.value),
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
for (const e of tx.vout) {
|
|
39
39
|
vout.push({
|
|
40
40
|
script_pk: e.script_pk,
|
|
41
|
-
value:
|
|
41
|
+
value: String(e.value),
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
return { version, locktime, vin, vout };
|
package/dist/lib/tx/size.js
CHANGED
|
@@ -4,16 +4,14 @@ import { parse_tx } from "./parse.js";
|
|
|
4
4
|
const WIT_FLAG_BYTES = 2;
|
|
5
5
|
export function get_vsize(bytes) {
|
|
6
6
|
const weight = Buff.bytes(bytes).length;
|
|
7
|
-
|
|
8
|
-
return Math.ceil(weight / 4) + remain;
|
|
7
|
+
return Math.ceil(weight / 4);
|
|
9
8
|
}
|
|
10
9
|
export function get_txsize(txdata) {
|
|
11
10
|
const json = parse_tx(txdata);
|
|
12
11
|
const base = encode_tx(json, false).length;
|
|
13
12
|
const total = encode_tx(json, true).length;
|
|
14
13
|
const weight = base * 3 + total;
|
|
15
|
-
const
|
|
16
|
-
const vsize = Math.ceil(weight / 4) + remain;
|
|
14
|
+
const vsize = Math.ceil(weight / 4);
|
|
17
15
|
return { base, total, vsize, weight };
|
|
18
16
|
}
|
|
19
17
|
export function get_vin_size(vin) {
|
package/dist/lib/tx/util.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Buff } from "@vbyte/buff";
|
|
2
|
-
import { Test } from "@vbyte/
|
|
3
|
-
import {
|
|
4
|
-
import { hash256 } from "@vbyte/micro-lib/hash";
|
|
2
|
+
import { Assert, Test } from "@vbyte/util";
|
|
3
|
+
import { hash256 } from "@vbyte/crypto/hash";
|
|
5
4
|
import { DEFAULT } from "../../const.js";
|
|
6
5
|
import { decode_tx } from "./decode.js";
|
|
7
6
|
import { encode_tx } from "./encode.js";
|
package/dist/lib/tx/validate.js
CHANGED
|
@@ -1,28 +1,56 @@
|
|
|
1
1
|
import * as Schema from "../../schema/index.js";
|
|
2
|
+
import { ValidationError } from "../../error.js";
|
|
3
|
+
function format_zod_error(error) {
|
|
4
|
+
const issues = error.issues.map((issue) => {
|
|
5
|
+
const path = issue.path.length > 0 ? `${issue.path.join(".")}: ` : "";
|
|
6
|
+
return `${path}${issue.message}`;
|
|
7
|
+
});
|
|
8
|
+
return issues.join("; ");
|
|
9
|
+
}
|
|
2
10
|
export function assert_tx_template(txdata) {
|
|
3
|
-
Schema.tx.tx_template.
|
|
11
|
+
const result = Schema.tx.tx_template.safeParse(txdata);
|
|
12
|
+
if (!result.success) {
|
|
13
|
+
throw new ValidationError(`invalid transaction template: ${format_zod_error(result.error)}`);
|
|
14
|
+
}
|
|
4
15
|
}
|
|
5
16
|
export function assert_has_prevouts(vin) {
|
|
6
|
-
|
|
7
|
-
|
|
17
|
+
const missingIdx = vin.findIndex((txin) => txin.prevout === null || txin.prevout === undefined);
|
|
18
|
+
if (missingIdx !== -1) {
|
|
19
|
+
throw new ValidationError(`transaction input at index ${missingIdx} is missing prevout data. ` +
|
|
20
|
+
`Prevout (previous output) is required for signing`);
|
|
8
21
|
}
|
|
9
22
|
}
|
|
10
23
|
export function assert_tx_data(txdata) {
|
|
11
|
-
Schema.tx.tx_data.
|
|
24
|
+
const result = Schema.tx.tx_data.safeParse(txdata);
|
|
25
|
+
if (!result.success) {
|
|
26
|
+
throw new ValidationError(`invalid transaction data: ${format_zod_error(result.error)}`);
|
|
27
|
+
}
|
|
12
28
|
}
|
|
13
29
|
export function assert_tx_spend_data(txdata) {
|
|
14
30
|
assert_tx_data(txdata);
|
|
15
31
|
assert_has_prevouts(txdata.vin);
|
|
16
32
|
}
|
|
17
33
|
export function assert_tx_input(tx_input) {
|
|
18
|
-
Schema.tx.tx_input.
|
|
34
|
+
const result = Schema.tx.tx_input.safeParse(tx_input);
|
|
35
|
+
if (!result.success) {
|
|
36
|
+
throw new ValidationError(`invalid transaction input: ${format_zod_error(result.error)}`);
|
|
37
|
+
}
|
|
19
38
|
}
|
|
20
39
|
export function assert_tx_output(tx_output) {
|
|
21
|
-
Schema.tx.tx_output.
|
|
40
|
+
const result = Schema.tx.tx_output.safeParse(tx_output);
|
|
41
|
+
if (!result.success) {
|
|
42
|
+
throw new ValidationError(`invalid transaction output: ${format_zod_error(result.error)}`);
|
|
43
|
+
}
|
|
22
44
|
}
|
|
23
45
|
export function assert_vin_template(vin) {
|
|
24
|
-
Schema.tx.vin_template.
|
|
46
|
+
const result = Schema.tx.vin_template.safeParse(vin);
|
|
47
|
+
if (!result.success) {
|
|
48
|
+
throw new ValidationError(`invalid input template: ${format_zod_error(result.error)}`);
|
|
49
|
+
}
|
|
25
50
|
}
|
|
26
51
|
export function assert_vout_template(vout) {
|
|
27
|
-
Schema.tx.vout_template.
|
|
52
|
+
const result = Schema.tx.vout_template.safeParse(vout);
|
|
53
|
+
if (!result.success) {
|
|
54
|
+
throw new ValidationError(`invalid output template: ${format_zod_error(result.error)}`);
|
|
55
|
+
}
|
|
28
56
|
}
|
package/dist/lib/witness/util.js
CHANGED