@vbyte/btc-dev 1.0.15 → 1.1.1
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/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/lib/meta/locktime.d.ts +1 -1
- package/dist/lib/meta/locktime.js +5 -5
- package/dist/lib/meta/ref.d.ts +1 -1
- package/dist/lib/meta/ref.js +6 -6
- package/dist/lib/meta/scribe.d.ts +5 -3
- package/dist/lib/meta/scribe.js +21 -18
- package/dist/lib/meta/sequence.d.ts +1 -1
- package/dist/lib/meta/sequence.js +5 -5
- package/dist/lib/script/decode.d.ts +2 -1
- package/dist/lib/script/encode.d.ts +1 -1
- package/dist/lib/script/encode.js +3 -3
- package/dist/lib/taproot/cblock.js +1 -0
- package/dist/main.cjs +100 -4036
- package/dist/main.cjs.map +1 -1
- package/dist/module.mjs +99 -4034
- package/dist/module.mjs.map +1 -1
- package/dist/package.json +5 -9
- package/dist/schema/tx.d.ts +12 -12
- package/dist/script.js +8 -8
- package/dist/script.js.map +1 -1
- package/dist/types/taproot.d.ts +1 -0
- package/package.json +5 -9
- package/src/index.ts +0 -1
- package/src/lib/meta/locktime.ts +1 -1
- package/src/lib/meta/ref.ts +1 -1
- package/src/lib/meta/scribe.ts +29 -24
- package/src/lib/meta/sequence.ts +1 -1
- package/src/lib/script/decode.ts +2 -2
- package/src/lib/script/encode.ts +4 -5
- package/src/lib/taproot/cblock.ts +1 -0
- package/src/types/taproot.ts +1 -0
- package/dist/lib/psbt/encoder.d.ts +0 -5
- package/dist/lib/psbt/encoder.js +0 -21
- package/dist/lib/psbt/index.d.ts +0 -4
- package/dist/lib/psbt/index.js +0 -4
- package/dist/lib/psbt/meta.d.ts +0 -3
- package/dist/lib/psbt/meta.js +0 -11
- package/dist/lib/psbt/util.d.ts +0 -5
- package/dist/lib/psbt/util.js +0 -44
- package/dist/lib/psbt/validate.d.ts +0 -2
- package/dist/lib/psbt/validate.js +0 -11
- package/src/lib/psbt/encoder.ts +0 -24
- package/src/lib/psbt/index.ts +0 -4
- package/src/lib/psbt/meta.ts +0 -15
- package/src/lib/psbt/util.ts +0 -62
- package/src/lib/psbt/validate.ts +0 -18
package/dist/main.cjs
CHANGED
|
@@ -787,7 +787,7 @@ const crypto$1 = typeof globalThis === 'object' && 'crypto' in globalThis ? glob
|
|
|
787
787
|
// Makes the utils un-importable in browsers without a bundler.
|
|
788
788
|
// Once node.js 18 is deprecated (2025-04-30), we can just drop the import.
|
|
789
789
|
/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
|
|
790
|
-
function isBytes$
|
|
790
|
+
function isBytes$1(a) {
|
|
791
791
|
return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');
|
|
792
792
|
}
|
|
793
793
|
/** Asserts something is positive integer. */
|
|
@@ -797,7 +797,7 @@ function anumber$1(n) {
|
|
|
797
797
|
}
|
|
798
798
|
/** Asserts something is Uint8Array. */
|
|
799
799
|
function abytes$1(b, ...lengths) {
|
|
800
|
-
if (!isBytes$
|
|
800
|
+
if (!isBytes$1(b))
|
|
801
801
|
throw new Error('Uint8Array expected');
|
|
802
802
|
if (lengths.length > 0 && !lengths.includes(b.length))
|
|
803
803
|
throw new Error('Uint8Array expected of length ' + lengths + ', got length=' + b.length);
|
|
@@ -831,7 +831,7 @@ function clean(...arrays) {
|
|
|
831
831
|
}
|
|
832
832
|
}
|
|
833
833
|
/** Create DataView of an array for easy byte-level manipulation. */
|
|
834
|
-
function createView
|
|
834
|
+
function createView(arr) {
|
|
835
835
|
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
836
836
|
}
|
|
837
837
|
/** The rotate right (circular right shift) operation for uint32 */
|
|
@@ -843,7 +843,7 @@ function rotl(word, shift) {
|
|
|
843
843
|
return (word << shift) | ((word >>> (32 - shift)) >>> 0);
|
|
844
844
|
}
|
|
845
845
|
// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex
|
|
846
|
-
const hasHexBuiltin
|
|
846
|
+
const hasHexBuiltin = /* @__PURE__ */ (() =>
|
|
847
847
|
// @ts-ignore
|
|
848
848
|
typeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')();
|
|
849
849
|
// Array where index 0xf0 (240) is mapped to string 'f0'
|
|
@@ -855,7 +855,7 @@ const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(1
|
|
|
855
855
|
function bytesToHex(bytes) {
|
|
856
856
|
abytes$1(bytes);
|
|
857
857
|
// @ts-ignore
|
|
858
|
-
if (hasHexBuiltin
|
|
858
|
+
if (hasHexBuiltin)
|
|
859
859
|
return bytes.toHex();
|
|
860
860
|
// pre-caching improves the speed 6x
|
|
861
861
|
let hex = '';
|
|
@@ -883,7 +883,7 @@ function hexToBytes(hex) {
|
|
|
883
883
|
if (typeof hex !== 'string')
|
|
884
884
|
throw new Error('hex string expected, got ' + typeof hex);
|
|
885
885
|
// @ts-ignore
|
|
886
|
-
if (hasHexBuiltin
|
|
886
|
+
if (hasHexBuiltin)
|
|
887
887
|
return Uint8Array.fromHex(hex);
|
|
888
888
|
const hl = hex.length;
|
|
889
889
|
const al = hl / 2;
|
|
@@ -922,7 +922,7 @@ function toBytes(data) {
|
|
|
922
922
|
return data;
|
|
923
923
|
}
|
|
924
924
|
/** Copies several Uint8Arrays into one. */
|
|
925
|
-
function concatBytes
|
|
925
|
+
function concatBytes(...arrays) {
|
|
926
926
|
let sum = 0;
|
|
927
927
|
for (let i = 0; i < arrays.length; i++) {
|
|
928
928
|
const a = arrays[i];
|
|
@@ -1002,7 +1002,7 @@ class HashMD extends Hash {
|
|
|
1002
1002
|
this.padOffset = padOffset;
|
|
1003
1003
|
this.isLE = isLE;
|
|
1004
1004
|
this.buffer = new Uint8Array(blockLen);
|
|
1005
|
-
this.view = createView
|
|
1005
|
+
this.view = createView(this.buffer);
|
|
1006
1006
|
}
|
|
1007
1007
|
update(data) {
|
|
1008
1008
|
aexists(this);
|
|
@@ -1014,7 +1014,7 @@ class HashMD extends Hash {
|
|
|
1014
1014
|
const take = Math.min(blockLen - this.pos, len - pos);
|
|
1015
1015
|
// Fast path: we have at least one block in input, cast it to view and process
|
|
1016
1016
|
if (take === blockLen) {
|
|
1017
|
-
const dataView = createView
|
|
1017
|
+
const dataView = createView(data);
|
|
1018
1018
|
for (; blockLen <= len - pos; pos += blockLen)
|
|
1019
1019
|
this.process(dataView, pos);
|
|
1020
1020
|
continue;
|
|
@@ -1057,7 +1057,7 @@ class HashMD extends Hash {
|
|
|
1057
1057
|
// So we just write lowest 64 bits of that value.
|
|
1058
1058
|
setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);
|
|
1059
1059
|
this.process(view, 0);
|
|
1060
|
-
const oview = createView
|
|
1060
|
+
const oview = createView(out);
|
|
1061
1061
|
const len = this.outputLen;
|
|
1062
1062
|
// NOTE: we do division by 4 later, which should be fused in single op with modulo by JIT
|
|
1063
1063
|
if (len % 4)
|
|
@@ -1348,7 +1348,7 @@ function ensureBytes(title, hex, expectedLength) {
|
|
|
1348
1348
|
throw new Error(title + ' must be hex string or Uint8Array, cause: ' + e);
|
|
1349
1349
|
}
|
|
1350
1350
|
}
|
|
1351
|
-
else if (isBytes$
|
|
1351
|
+
else if (isBytes$1(hex)) {
|
|
1352
1352
|
// Uint8Array.from() instead of hash.slice() because node.js Buffer
|
|
1353
1353
|
// is instance of Uint8Array, and its slice() creates **mutable** copy
|
|
1354
1354
|
res = Uint8Array.from(hex);
|
|
@@ -1453,7 +1453,7 @@ function createHmacDrbg(hashLen, qByteLen, hmacFn) {
|
|
|
1453
1453
|
out.push(sl);
|
|
1454
1454
|
len += v.length;
|
|
1455
1455
|
}
|
|
1456
|
-
return concatBytes
|
|
1456
|
+
return concatBytes(...out);
|
|
1457
1457
|
};
|
|
1458
1458
|
const genUntil = (seed, pred) => {
|
|
1459
1459
|
reset();
|
|
@@ -2523,10 +2523,10 @@ function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
2523
2523
|
if (isCompressed) {
|
|
2524
2524
|
assertCompressionIsSupported();
|
|
2525
2525
|
const hasEvenY = !Fp.isOdd(y);
|
|
2526
|
-
return concatBytes
|
|
2526
|
+
return concatBytes(pprefix(hasEvenY), bx);
|
|
2527
2527
|
}
|
|
2528
2528
|
else {
|
|
2529
|
-
return concatBytes
|
|
2529
|
+
return concatBytes(Uint8Array.of(0x04), bx, Fp.toBytes(y));
|
|
2530
2530
|
}
|
|
2531
2531
|
}
|
|
2532
2532
|
function pointFromBytes(bytes) {
|
|
@@ -2973,7 +2973,7 @@ function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
|
2973
2973
|
});
|
|
2974
2974
|
const randomBytes_ = ecdsaOpts.randomBytes || randomBytes;
|
|
2975
2975
|
const hmac_ = ecdsaOpts.hmac ||
|
|
2976
|
-
((key, ...msgs) => hmac(ecdsaOpts.hash, key, concatBytes
|
|
2976
|
+
((key, ...msgs) => hmac(ecdsaOpts.hash, key, concatBytes(...msgs)));
|
|
2977
2977
|
const { Fp, Fn } = Point;
|
|
2978
2978
|
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
2979
2979
|
function isBiggerThanHalfOrder(number) {
|
|
@@ -3041,7 +3041,7 @@ function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
|
3041
3041
|
if (!Fp.isValid(radj))
|
|
3042
3042
|
throw new Error('recovery id 2 or 3 invalid');
|
|
3043
3043
|
const x = Fp.toBytes(radj);
|
|
3044
|
-
const R = Point.fromHex(concatBytes
|
|
3044
|
+
const R = Point.fromHex(concatBytes(pprefix((rec & 1) === 0), x));
|
|
3045
3045
|
const ir = Fn.inv(radj); // r^-1
|
|
3046
3046
|
const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
|
|
3047
3047
|
const u1 = Fn.create(-h * ir); // -hr^-1
|
|
@@ -3062,7 +3062,7 @@ function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
|
3062
3062
|
}
|
|
3063
3063
|
toBytes(format) {
|
|
3064
3064
|
if (format === 'compact')
|
|
3065
|
-
return concatBytes
|
|
3065
|
+
return concatBytes(Fn.toBytes(this.r), Fn.toBytes(this.s));
|
|
3066
3066
|
if (format === 'der')
|
|
3067
3067
|
return hexToBytes(DER.hexFromSig(this));
|
|
3068
3068
|
throw new Error('invalid format');
|
|
@@ -3210,7 +3210,7 @@ function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
|
3210
3210
|
const e = ent === true ? randomBytes_(Fp.BYTES) : ent; // generate random bytes OR pass as-is
|
|
3211
3211
|
seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
|
|
3212
3212
|
}
|
|
3213
|
-
const seed = concatBytes
|
|
3213
|
+
const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
|
|
3214
3214
|
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
|
3215
3215
|
// Converts signature params into point w r/s, checks result for validity.
|
|
3216
3216
|
// Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
|
|
@@ -3287,7 +3287,7 @@ function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
|
3287
3287
|
throw new Error('options.strict was renamed to lowS');
|
|
3288
3288
|
if (format !== undefined && !['compact', 'der', 'js'].includes(format))
|
|
3289
3289
|
throw new Error('format must be "compact", "der" or "js"');
|
|
3290
|
-
const isHex = typeof sg === 'string' || isBytes$
|
|
3290
|
+
const isHex = typeof sg === 'string' || isBytes$1(sg);
|
|
3291
3291
|
const isObj = !isHex &&
|
|
3292
3292
|
!format &&
|
|
3293
3293
|
typeof sg === 'object' &&
|
|
@@ -3536,22 +3536,22 @@ function taggedHash(tag, ...messages) {
|
|
|
3536
3536
|
let tagP = TAGGED_HASH_PREFIXES[tag];
|
|
3537
3537
|
if (tagP === undefined) {
|
|
3538
3538
|
const tagH = sha256$1(Uint8Array.from(tag, (c) => c.charCodeAt(0)));
|
|
3539
|
-
tagP = concatBytes
|
|
3539
|
+
tagP = concatBytes(tagH, tagH);
|
|
3540
3540
|
TAGGED_HASH_PREFIXES[tag] = tagP;
|
|
3541
3541
|
}
|
|
3542
|
-
return sha256$1(concatBytes
|
|
3542
|
+
return sha256$1(concatBytes(tagP, ...messages));
|
|
3543
3543
|
}
|
|
3544
3544
|
// ECDSA compact points are 33-byte. Schnorr is 32: we strip first byte 0x02 or 0x03
|
|
3545
3545
|
const pointToBytes = (point) => point.toBytes(true).slice(1);
|
|
3546
3546
|
const numTo32b = (n) => numberToBytesBE(n, 32);
|
|
3547
3547
|
const modP = (x) => mod(x, secp256k1_CURVE.p);
|
|
3548
3548
|
const modN = (x) => mod(x, secp256k1_CURVE.n);
|
|
3549
|
-
const Point
|
|
3549
|
+
const Point = /* @__PURE__ */ (() => secp256k1.Point)();
|
|
3550
3550
|
const hasEven = (y) => y % _2n === _0n$1;
|
|
3551
3551
|
// Calculate point, scalar and bytes
|
|
3552
3552
|
function schnorrGetExtPubKey(priv) {
|
|
3553
3553
|
let d_ = secp256k1.utils.normPrivateKeyToScalar(priv); // same method executed in fromPrivateKey
|
|
3554
|
-
let p = Point
|
|
3554
|
+
let p = Point.fromPrivateKey(d_); // P = d'⋅G; 0 < d' < n check is done inside
|
|
3555
3555
|
const scalar = hasEven(p.y) ? d_ : modN(-d_);
|
|
3556
3556
|
return { scalar: scalar, bytes: pointToBytes(p) };
|
|
3557
3557
|
}
|
|
@@ -3566,7 +3566,7 @@ function lift_x(x) {
|
|
|
3566
3566
|
let y = sqrtMod(c); // Let y = c^(p+1)/4 mod p.
|
|
3567
3567
|
if (!hasEven(y))
|
|
3568
3568
|
y = modP(-y); // Return the unique point P such that x(P) = x and
|
|
3569
|
-
const p = Point
|
|
3569
|
+
const p = Point.fromAffine({ x, y }); // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise.
|
|
3570
3570
|
p.assertValidity();
|
|
3571
3571
|
return p;
|
|
3572
3572
|
}
|
|
@@ -3624,7 +3624,7 @@ function schnorrVerify(signature, message, publicKey) {
|
|
|
3624
3624
|
return false;
|
|
3625
3625
|
const e = challenge(numTo32b(r), pointToBytes(P), m); // int(challenge(bytes(r)||bytes(P)||m))%n
|
|
3626
3626
|
// R = s⋅G - e⋅P, where -eP == (n-e)P
|
|
3627
|
-
const R = Point
|
|
3627
|
+
const R = Point.BASE.multiplyUnsafe(s).add(P.multiplyUnsafe(modN(-e)));
|
|
3628
3628
|
const { x, y } = R.toAffine();
|
|
3629
3629
|
// Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r.
|
|
3630
3630
|
if (R.is0() || !hasEven(y) || x !== r)
|
|
@@ -3843,7 +3843,7 @@ class RIPEMD160 extends HashMD {
|
|
|
3843
3843
|
*/
|
|
3844
3844
|
const ripemd160 = /* @__PURE__ */ createHasher(() => new RIPEMD160());
|
|
3845
3845
|
|
|
3846
|
-
function hash160
|
|
3846
|
+
function hash160(...input) {
|
|
3847
3847
|
const buffer = Buff.join(input);
|
|
3848
3848
|
const digest = ripemd160(sha256$1(buffer));
|
|
3849
3849
|
return new Buff(digest);
|
|
@@ -7770,7 +7770,7 @@ float.refine((e) => {
|
|
|
7770
7770
|
const parts = String(e).split('.').at(1);
|
|
7771
7771
|
return parts !== undefined && parts.length <= 2;
|
|
7772
7772
|
});
|
|
7773
|
-
const hex
|
|
7773
|
+
const hex = stringType()
|
|
7774
7774
|
.regex(/^[0-9a-fA-F]*$/)
|
|
7775
7775
|
.refine(e => e.length % 2 === 0);
|
|
7776
7776
|
const literal = unionType([
|
|
@@ -7781,11 +7781,11 @@ u8a.refine((e) => e.length === 20);
|
|
|
7781
7781
|
const u8a32 = u8a.refine((e) => e.length === 32);
|
|
7782
7782
|
const u8a33 = u8a.refine((e) => e.length === 33);
|
|
7783
7783
|
const u8a64 = u8a.refine((e) => e.length === 64);
|
|
7784
|
-
hex
|
|
7785
|
-
const hex32 = hex
|
|
7786
|
-
const hex33 = hex
|
|
7787
|
-
const hex64 = hex
|
|
7788
|
-
unionType([hex
|
|
7784
|
+
hex.refine((e) => e.length === 40);
|
|
7785
|
+
const hex32 = hex.refine((e) => e.length === 64);
|
|
7786
|
+
const hex33 = hex.refine((e) => e.length === 66);
|
|
7787
|
+
const hex64 = hex.refine((e) => e.length === 128);
|
|
7788
|
+
unionType([hex, u8a]);
|
|
7789
7789
|
const byte32 = unionType([hex32, u8a32]);
|
|
7790
7790
|
unionType([hex33, u8a33]);
|
|
7791
7791
|
unionType([hex64, u8a64]);
|
|
@@ -7795,12 +7795,12 @@ stringType().regex(/^[a-zA-Z0-9\-_]+={0,2}$/);
|
|
|
7795
7795
|
stringType().regex(/^[a-z]+1[023456789acdefghjklmnpqrstuvwxyz]+$/);
|
|
7796
7796
|
|
|
7797
7797
|
/*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
7798
|
-
function isBytes
|
|
7798
|
+
function isBytes(a) {
|
|
7799
7799
|
return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');
|
|
7800
7800
|
}
|
|
7801
7801
|
/** Asserts something is Uint8Array. */
|
|
7802
7802
|
function abytes(b, ...lengths) {
|
|
7803
|
-
if (!isBytes
|
|
7803
|
+
if (!isBytes(b))
|
|
7804
7804
|
throw new Error('Uint8Array expected');
|
|
7805
7805
|
if (lengths.length > 0 && !lengths.includes(b.length))
|
|
7806
7806
|
throw new Error('Uint8Array expected of length ' + lengths + ', got length=' + b.length);
|
|
@@ -7935,13 +7935,6 @@ function padding(bits, chr = '=') {
|
|
|
7935
7935
|
},
|
|
7936
7936
|
};
|
|
7937
7937
|
}
|
|
7938
|
-
/**
|
|
7939
|
-
* @__NO_SIDE_EFFECTS__
|
|
7940
|
-
*/
|
|
7941
|
-
function normalize(fn) {
|
|
7942
|
-
afn(fn);
|
|
7943
|
-
return { encode: (from) => from, decode: (to) => fn(to) };
|
|
7944
|
-
}
|
|
7945
7938
|
/**
|
|
7946
7939
|
* Slow: O(n^2) time complexity
|
|
7947
7940
|
*/
|
|
@@ -8053,7 +8046,7 @@ function radix(num) {
|
|
|
8053
8046
|
const _256 = 2 ** 8;
|
|
8054
8047
|
return {
|
|
8055
8048
|
encode: (bytes) => {
|
|
8056
|
-
if (!isBytes
|
|
8049
|
+
if (!isBytes(bytes))
|
|
8057
8050
|
throw new Error('radix.encode input should be Uint8Array');
|
|
8058
8051
|
return convertRadix(Array.from(bytes), _256, num);
|
|
8059
8052
|
},
|
|
@@ -8076,7 +8069,7 @@ function radix2(bits, revPadding = false) {
|
|
|
8076
8069
|
throw new Error('radix2: carry overflow');
|
|
8077
8070
|
return {
|
|
8078
8071
|
encode: (bytes) => {
|
|
8079
|
-
if (!isBytes
|
|
8072
|
+
if (!isBytes(bytes))
|
|
8080
8073
|
throw new Error('radix2.encode input should be Uint8Array');
|
|
8081
8074
|
return convertRadix2(Array.from(bytes), 8, bits, !revPadding);
|
|
8082
8075
|
},
|
|
@@ -8100,7 +8093,7 @@ function checksum(len, fn) {
|
|
|
8100
8093
|
afn(fn);
|
|
8101
8094
|
return {
|
|
8102
8095
|
encode(data) {
|
|
8103
|
-
if (!isBytes
|
|
8096
|
+
if (!isBytes(data))
|
|
8104
8097
|
throw new Error('checksum.encode: input should be Uint8Array');
|
|
8105
8098
|
const sum = fn(data).slice(0, len);
|
|
8106
8099
|
const res = new Uint8Array(data.length + len);
|
|
@@ -8109,7 +8102,7 @@ function checksum(len, fn) {
|
|
|
8109
8102
|
return res;
|
|
8110
8103
|
},
|
|
8111
8104
|
decode(data) {
|
|
8112
|
-
if (!isBytes
|
|
8105
|
+
if (!isBytes(data))
|
|
8113
8106
|
throw new Error('checksum.decode: input should be Uint8Array');
|
|
8114
8107
|
const payload = data.slice(0, -len);
|
|
8115
8108
|
const oldChecksum = data.slice(-len);
|
|
@@ -8222,7 +8215,7 @@ function genBech32(encoding) {
|
|
|
8222
8215
|
const fromWordsUnsafe = unsafeWrapper(fromWords);
|
|
8223
8216
|
function encode(prefix, words, limit = 90) {
|
|
8224
8217
|
astr('bech32.encode prefix', prefix);
|
|
8225
|
-
if (isBytes
|
|
8218
|
+
if (isBytes(words))
|
|
8226
8219
|
words = Array.from(words);
|
|
8227
8220
|
anumArr('bech32.encode', words);
|
|
8228
8221
|
const plen = prefix.length;
|
|
@@ -8289,42 +8282,6 @@ const bech32 = genBech32('bech32');
|
|
|
8289
8282
|
* https://github.com/paulmillr/scure-btc-signer.
|
|
8290
8283
|
*/
|
|
8291
8284
|
const bech32m = genBech32('bech32m');
|
|
8292
|
-
/**
|
|
8293
|
-
* UTF-8-to-byte decoder. Uses built-in TextDecoder / TextEncoder.
|
|
8294
|
-
* @example
|
|
8295
|
-
* ```js
|
|
8296
|
-
* const b = utf8.decode("hey"); // => new Uint8Array([ 104, 101, 121 ])
|
|
8297
|
-
* const str = utf8.encode(b); // "hey"
|
|
8298
|
-
* ```
|
|
8299
|
-
*/
|
|
8300
|
-
const utf8 = {
|
|
8301
|
-
encode: (data) => new TextDecoder().decode(data),
|
|
8302
|
-
decode: (str) => new TextEncoder().encode(str),
|
|
8303
|
-
};
|
|
8304
|
-
// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex
|
|
8305
|
-
// prettier-ignore
|
|
8306
|
-
const hasHexBuiltin = /* @__PURE__ */ (() => typeof Uint8Array.from([]).toHex === 'function' &&
|
|
8307
|
-
typeof Uint8Array.fromHex === 'function')();
|
|
8308
|
-
// prettier-ignore
|
|
8309
|
-
const hexBuiltin = {
|
|
8310
|
-
encode(data) { abytes(data); return data.toHex(); },
|
|
8311
|
-
decode(s) { astr('hex', s); return Uint8Array.fromHex(s); },
|
|
8312
|
-
};
|
|
8313
|
-
/**
|
|
8314
|
-
* hex string decoder. Uses built-in function, when available.
|
|
8315
|
-
* @example
|
|
8316
|
-
* ```js
|
|
8317
|
-
* const b = hex.decode("0102ff"); // => new Uint8Array([ 1, 2, 255 ])
|
|
8318
|
-
* const str = hex.encode(b); // "0102ff"
|
|
8319
|
-
* ```
|
|
8320
|
-
*/
|
|
8321
|
-
const hex = hasHexBuiltin
|
|
8322
|
-
? hexBuiltin
|
|
8323
|
-
: chain(radix2(4), alphabet('0123456789abcdef'), join(''), normalize((s) => {
|
|
8324
|
-
if (typeof s !== 'string' || s.length % 2 !== 0)
|
|
8325
|
-
throw new TypeError(`hex.decode: expected string, got ${typeof s} with length ${s.length}`);
|
|
8326
|
-
return s.toLowerCase();
|
|
8327
|
-
}));
|
|
8328
8285
|
|
|
8329
8286
|
var B58chk;
|
|
8330
8287
|
(function (B58chk) {
|
|
@@ -8588,7 +8545,7 @@ var P2PKH;
|
|
|
8588
8545
|
})(P2PKH || (P2PKH = {}));
|
|
8589
8546
|
function create_p2pkh_address(script, network = 'main') {
|
|
8590
8547
|
const bytes = Buff.bytes(script);
|
|
8591
|
-
const hash = hash160
|
|
8548
|
+
const hash = hash160(bytes);
|
|
8592
8549
|
return encode_p2pkh_address(hash, network);
|
|
8593
8550
|
}
|
|
8594
8551
|
function encode_p2pkh_address(pk_hash, network = 'main') {
|
|
@@ -8617,7 +8574,7 @@ var P2SH;
|
|
|
8617
8574
|
})(P2SH || (P2SH = {}));
|
|
8618
8575
|
function create_p2sh_address(script, network = 'main') {
|
|
8619
8576
|
const bytes = Buff.bytes(script);
|
|
8620
|
-
const hash = hash160
|
|
8577
|
+
const hash = hash160(bytes);
|
|
8621
8578
|
return encode_p2sh_address(hash, network);
|
|
8622
8579
|
}
|
|
8623
8580
|
function encode_p2sh_address(script_hash, network = 'main') {
|
|
@@ -8647,7 +8604,7 @@ var P2WPKH;
|
|
|
8647
8604
|
function create_p2wpkh_address(pubkey, network = 'main') {
|
|
8648
8605
|
const bytes = Buff.bytes(pubkey);
|
|
8649
8606
|
Assert.size(bytes, 33, `invalid payload size: ${bytes.length} !== 33`);
|
|
8650
|
-
const hash = hash160
|
|
8607
|
+
const hash = hash160(bytes);
|
|
8651
8608
|
return encode_p2wpkh_address(hash, network);
|
|
8652
8609
|
}
|
|
8653
8610
|
function encode_p2wpkh_address(pk_hash, network = 'main') {
|
|
@@ -8729,7 +8686,7 @@ var AddressTool;
|
|
|
8729
8686
|
AddressTool.parse = parse_address;
|
|
8730
8687
|
})(AddressTool || (AddressTool = {}));
|
|
8731
8688
|
|
|
8732
|
-
var index$
|
|
8689
|
+
var index$8 = /*#__PURE__*/Object.freeze({
|
|
8733
8690
|
__proto__: null,
|
|
8734
8691
|
get AddressTool () { return AddressTool; },
|
|
8735
8692
|
get P2PKH () { return P2PKH; },
|
|
@@ -8741,11 +8698,11 @@ var index$9 = /*#__PURE__*/Object.freeze({
|
|
|
8741
8698
|
});
|
|
8742
8699
|
|
|
8743
8700
|
const LOCKTIME_THRESHOLD = 500000000;
|
|
8744
|
-
var
|
|
8745
|
-
(function (
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
})(
|
|
8701
|
+
var LocktimeField;
|
|
8702
|
+
(function (LocktimeField) {
|
|
8703
|
+
LocktimeField.encode = encode_locktime;
|
|
8704
|
+
LocktimeField.decode = decode_locktime;
|
|
8705
|
+
})(LocktimeField || (LocktimeField = {}));
|
|
8749
8706
|
function encode_locktime(locktime) {
|
|
8750
8707
|
switch (locktime.type) {
|
|
8751
8708
|
case 'timelock':
|
|
@@ -8777,27 +8734,27 @@ function decode_locktime(locktime) {
|
|
|
8777
8734
|
}
|
|
8778
8735
|
}
|
|
8779
8736
|
|
|
8780
|
-
var
|
|
8781
|
-
(function (
|
|
8782
|
-
|
|
8737
|
+
var RefPointer;
|
|
8738
|
+
(function (RefPointer) {
|
|
8739
|
+
RefPointer.outpoint = {
|
|
8783
8740
|
encode: encode_outpoint,
|
|
8784
8741
|
decode: decode_outpoint,
|
|
8785
8742
|
verify: verify_outpoint,
|
|
8786
8743
|
assert: assert_outpoint,
|
|
8787
8744
|
};
|
|
8788
|
-
|
|
8745
|
+
RefPointer.record_id = {
|
|
8789
8746
|
encode: encode_inscription_id,
|
|
8790
8747
|
decode: decode_inscription_id,
|
|
8791
8748
|
verify: verify_inscription_id,
|
|
8792
8749
|
assert: assert_inscription_id,
|
|
8793
8750
|
};
|
|
8794
|
-
|
|
8751
|
+
RefPointer.rune_id = {
|
|
8795
8752
|
encode: encode_rune_id,
|
|
8796
8753
|
decode: decode_rune_id,
|
|
8797
8754
|
verify: verify_rune_id,
|
|
8798
8755
|
assert: assert_rune_id,
|
|
8799
8756
|
};
|
|
8800
|
-
})(
|
|
8757
|
+
})(RefPointer || (RefPointer = {}));
|
|
8801
8758
|
function encode_inscription_id(txid, order = 0) {
|
|
8802
8759
|
return `${txid}i${order}`;
|
|
8803
8760
|
}
|
|
@@ -9015,15 +8972,15 @@ function is_valid_op(word) {
|
|
|
9015
8972
|
const MAX_WORD_SIZE = 520;
|
|
9016
8973
|
function encode_script(words, varint = false) {
|
|
9017
8974
|
if (words.length === 0)
|
|
9018
|
-
return
|
|
8975
|
+
return Buff.num(0, 1);
|
|
9019
8976
|
const bytes = [];
|
|
9020
8977
|
for (const word of words) {
|
|
9021
8978
|
bytes.push(encode_script_word(word));
|
|
9022
8979
|
}
|
|
9023
8980
|
const buffer = Buff.join(bytes);
|
|
9024
8981
|
return (varint)
|
|
9025
|
-
? buffer.prepend(Buff.varint(buffer.length, 'le'))
|
|
9026
|
-
: buffer
|
|
8982
|
+
? buffer.prepend(Buff.varint(buffer.length, 'le'))
|
|
8983
|
+
: buffer;
|
|
9027
8984
|
}
|
|
9028
8985
|
function encode_script_word(word) {
|
|
9029
8986
|
let buff;
|
|
@@ -9148,17 +9105,17 @@ function is_valid_script(script) {
|
|
|
9148
9105
|
const _0n = BigInt(0);
|
|
9149
9106
|
const _1n = BigInt(1);
|
|
9150
9107
|
const _26n = BigInt(26);
|
|
9151
|
-
var
|
|
9152
|
-
(function (
|
|
9153
|
-
|
|
9154
|
-
|
|
9155
|
-
})(
|
|
9108
|
+
var InscriptionUtil;
|
|
9109
|
+
(function (InscriptionUtil) {
|
|
9110
|
+
InscriptionUtil.encode = encode_inscription;
|
|
9111
|
+
InscriptionUtil.decode = decode_inscription;
|
|
9112
|
+
})(InscriptionUtil || (InscriptionUtil = {}));
|
|
9156
9113
|
function decode_inscription(script) {
|
|
9157
9114
|
const envelopes = parse_envelopes(script);
|
|
9158
9115
|
return envelopes.map(parse_record);
|
|
9159
9116
|
}
|
|
9160
9117
|
function encode_inscription(data) {
|
|
9161
|
-
return data.map(create_envelope)
|
|
9118
|
+
return Buff.join(data.map(create_envelope));
|
|
9162
9119
|
}
|
|
9163
9120
|
function create_envelope(data) {
|
|
9164
9121
|
let asm = ['OP_0', 'OP_IF', '6f7264'];
|
|
@@ -9237,7 +9194,7 @@ function parse_record(envelope) {
|
|
|
9237
9194
|
i += 1;
|
|
9238
9195
|
break;
|
|
9239
9196
|
case 'OP_WITHIN':
|
|
9240
|
-
record.ref = envelope[i + 1];
|
|
9197
|
+
record.ref = decode_bytes(envelope[i + 1]);
|
|
9241
9198
|
i += 1;
|
|
9242
9199
|
break;
|
|
9243
9200
|
case 'OP_NOP':
|
|
@@ -9251,6 +9208,9 @@ function parse_record(envelope) {
|
|
|
9251
9208
|
}
|
|
9252
9209
|
return record;
|
|
9253
9210
|
}
|
|
9211
|
+
function decode_bytes(bytes) {
|
|
9212
|
+
return Buff.bytes(bytes).hex;
|
|
9213
|
+
}
|
|
9254
9214
|
function encode_id(identifier) {
|
|
9255
9215
|
Assert.ok(identifier.includes('i'), 'identifier must include an index');
|
|
9256
9216
|
const parts = identifier.split('i');
|
|
@@ -9259,8 +9219,8 @@ function encode_id(identifier) {
|
|
|
9259
9219
|
const txid = bytes.reverse().hex;
|
|
9260
9220
|
return (idx !== 0) ? txid + Buff.num(idx).hex : txid;
|
|
9261
9221
|
}
|
|
9262
|
-
function decode_id(
|
|
9263
|
-
const bytes = Buff.
|
|
9222
|
+
function decode_id(identifier) {
|
|
9223
|
+
const bytes = Buff.bytes(identifier);
|
|
9264
9224
|
const idx = bytes.at(-1) ?? 0;
|
|
9265
9225
|
const txid = bytes.slice(0, -1).reverse().hex;
|
|
9266
9226
|
return txid + 'i' + String(idx);
|
|
@@ -9268,14 +9228,14 @@ function decode_id(hexstr) {
|
|
|
9268
9228
|
function encode_pointer(pointer) {
|
|
9269
9229
|
return Buff.num(pointer).reverse().hex;
|
|
9270
9230
|
}
|
|
9271
|
-
function decode_pointer(
|
|
9272
|
-
return Buff.
|
|
9231
|
+
function decode_pointer(bytes) {
|
|
9232
|
+
return Buff.bytes(bytes).reverse().num;
|
|
9273
9233
|
}
|
|
9274
9234
|
function encode_label(label) {
|
|
9275
9235
|
return Buff.str(label).hex;
|
|
9276
9236
|
}
|
|
9277
|
-
function decode_label(
|
|
9278
|
-
return Buff.
|
|
9237
|
+
function decode_label(label) {
|
|
9238
|
+
return Buff.bytes(label).str;
|
|
9279
9239
|
}
|
|
9280
9240
|
function encode_content(content) {
|
|
9281
9241
|
const bytes = Buff.is_hex(content)
|
|
@@ -9295,9 +9255,9 @@ function encode_content(content) {
|
|
|
9295
9255
|
}
|
|
9296
9256
|
return chunks;
|
|
9297
9257
|
}
|
|
9298
|
-
function decode_content(
|
|
9299
|
-
const data = Buff.join(
|
|
9300
|
-
return (
|
|
9258
|
+
function decode_content(chunks, format = 'hex') {
|
|
9259
|
+
const data = Buff.join(chunks);
|
|
9260
|
+
return (format === 'hex')
|
|
9301
9261
|
? data.hex
|
|
9302
9262
|
: data.str;
|
|
9303
9263
|
}
|
|
@@ -9315,8 +9275,8 @@ function encode_rune_label(label) {
|
|
|
9315
9275
|
big = big - _1n;
|
|
9316
9276
|
return Buff.big(big).reverse().hex;
|
|
9317
9277
|
}
|
|
9318
|
-
function decode_rune_label(
|
|
9319
|
-
let big = Buff.
|
|
9278
|
+
function decode_rune_label(label) {
|
|
9279
|
+
let big = Buff.bytes(label).reverse().big;
|
|
9320
9280
|
big = big + _1n;
|
|
9321
9281
|
let result = '';
|
|
9322
9282
|
while (big > _0n) {
|
|
@@ -9339,11 +9299,11 @@ const TIMELOCK_TYPE = 0x00400000;
|
|
|
9339
9299
|
const TIMELOCK_VALUE_MASK = 0x0000FFFF;
|
|
9340
9300
|
const TIMELOCK_VALUE_MAX = 0xFFFF;
|
|
9341
9301
|
const TIMELOCK_GRANULARITY = 512;
|
|
9342
|
-
var
|
|
9343
|
-
(function (
|
|
9344
|
-
|
|
9345
|
-
|
|
9346
|
-
})(
|
|
9302
|
+
var SequenceField;
|
|
9303
|
+
(function (SequenceField) {
|
|
9304
|
+
SequenceField.encode = encode_sequence;
|
|
9305
|
+
SequenceField.decode = decode_sequence;
|
|
9306
|
+
})(SequenceField || (SequenceField = {}));
|
|
9347
9307
|
function encode_sequence(data) {
|
|
9348
9308
|
if (data.mode === 'height') {
|
|
9349
9309
|
const height = parse_height(data.height);
|
|
@@ -9400,12 +9360,12 @@ function parse_height(height) {
|
|
|
9400
9360
|
return height;
|
|
9401
9361
|
}
|
|
9402
9362
|
|
|
9403
|
-
var index$
|
|
9363
|
+
var index$7 = /*#__PURE__*/Object.freeze({
|
|
9404
9364
|
__proto__: null,
|
|
9405
|
-
get
|
|
9406
|
-
get
|
|
9407
|
-
get
|
|
9408
|
-
get
|
|
9365
|
+
get InscriptionUtil () { return InscriptionUtil; },
|
|
9366
|
+
get LocktimeField () { return LocktimeField; },
|
|
9367
|
+
get RefPointer () { return RefPointer; },
|
|
9368
|
+
get SequenceField () { return SequenceField; },
|
|
9409
9369
|
decode_inscription: decode_inscription,
|
|
9410
9370
|
decode_locktime: decode_locktime,
|
|
9411
9371
|
decode_sequence: decode_sequence,
|
|
@@ -9414,3902 +9374,6 @@ var index$8 = /*#__PURE__*/Object.freeze({
|
|
|
9414
9374
|
encode_sequence: encode_sequence
|
|
9415
9375
|
});
|
|
9416
9376
|
|
|
9417
|
-
/**
|
|
9418
|
-
* Define complex binary structures using composable primitives.
|
|
9419
|
-
* Main ideas:
|
|
9420
|
-
* - Encode / decode can be chained, same as in `scure-base`
|
|
9421
|
-
* - A complex structure can be created from an array and struct of primitive types
|
|
9422
|
-
* - Strings / bytes are arrays with specific optimizations: we can just read bytes directly
|
|
9423
|
-
* without creating plain array first and reading each byte separately.
|
|
9424
|
-
* - Types are inferred from definition
|
|
9425
|
-
* @module
|
|
9426
|
-
* @example
|
|
9427
|
-
* import * as P from 'micro-packed';
|
|
9428
|
-
* const s = P.struct({
|
|
9429
|
-
* field1: P.U32BE, // 32-bit unsigned big-endian integer
|
|
9430
|
-
* field2: P.string(P.U8), // String with U8 length prefix
|
|
9431
|
-
* field3: P.bytes(32), // 32 bytes
|
|
9432
|
-
* field4: P.array(P.U16BE, P.struct({ // Array of structs with U16BE length
|
|
9433
|
-
* subField1: P.U64BE, // 64-bit unsigned big-endian integer
|
|
9434
|
-
* subField2: P.string(10) // 10-byte string
|
|
9435
|
-
* }))
|
|
9436
|
-
* });
|
|
9437
|
-
*/
|
|
9438
|
-
// TODO: remove dependency on scure-base & inline?
|
|
9439
|
-
/*
|
|
9440
|
-
Exports can be groupped like this:
|
|
9441
|
-
|
|
9442
|
-
- Primitive types: P.bytes, P.string, P.hex, P.constant, P.pointer
|
|
9443
|
-
- Complex types: P.array, P.struct, P.tuple, P.map, P.tag, P.mappedTag
|
|
9444
|
-
- Padding, prefix, magic: P.padLeft, P.padRight, P.prefix, P.magic, P.magicBytes
|
|
9445
|
-
- Flags: P.flag, P.flagged, P.optional
|
|
9446
|
-
- Wrappers: P.apply, P.wrap, P.lazy
|
|
9447
|
-
- Bit fiddling: P.bits, P.bitset
|
|
9448
|
-
- utils: P.validate, coders.decimal
|
|
9449
|
-
- Debugger
|
|
9450
|
-
*/
|
|
9451
|
-
/** Shortcut to zero-length (empty) byte array */
|
|
9452
|
-
const EMPTY = /* @__PURE__ */ new Uint8Array();
|
|
9453
|
-
/** Shortcut to one-element (element is 0) byte array */
|
|
9454
|
-
const NULL = /* @__PURE__ */ new Uint8Array([0]);
|
|
9455
|
-
/** Checks if two Uint8Arrays are equal. Not constant-time. */
|
|
9456
|
-
function equalBytes$1(a, b) {
|
|
9457
|
-
if (a.length !== b.length)
|
|
9458
|
-
return false;
|
|
9459
|
-
for (let i = 0; i < a.length; i++)
|
|
9460
|
-
if (a[i] !== b[i])
|
|
9461
|
-
return false;
|
|
9462
|
-
return true;
|
|
9463
|
-
}
|
|
9464
|
-
/** Checks if the given value is a Uint8Array. */
|
|
9465
|
-
function isBytes$1(a) {
|
|
9466
|
-
return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');
|
|
9467
|
-
}
|
|
9468
|
-
/**
|
|
9469
|
-
* Concatenates multiple Uint8Arrays.
|
|
9470
|
-
* Engines limit functions to 65K+ arguments.
|
|
9471
|
-
* @param arrays Array of Uint8Array elements
|
|
9472
|
-
* @returns Concatenated Uint8Array
|
|
9473
|
-
*/
|
|
9474
|
-
function concatBytes$1(...arrays) {
|
|
9475
|
-
let sum = 0;
|
|
9476
|
-
for (let i = 0; i < arrays.length; i++) {
|
|
9477
|
-
const a = arrays[i];
|
|
9478
|
-
if (!isBytes$1(a))
|
|
9479
|
-
throw new Error('Uint8Array expected');
|
|
9480
|
-
sum += a.length;
|
|
9481
|
-
}
|
|
9482
|
-
const res = new Uint8Array(sum);
|
|
9483
|
-
for (let i = 0, pad = 0; i < arrays.length; i++) {
|
|
9484
|
-
const a = arrays[i];
|
|
9485
|
-
res.set(a, pad);
|
|
9486
|
-
pad += a.length;
|
|
9487
|
-
}
|
|
9488
|
-
return res;
|
|
9489
|
-
}
|
|
9490
|
-
/**
|
|
9491
|
-
* Creates DataView from Uint8Array
|
|
9492
|
-
* @param arr - bytes
|
|
9493
|
-
* @returns DataView
|
|
9494
|
-
*/
|
|
9495
|
-
const createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
9496
|
-
/**
|
|
9497
|
-
* Checks if the provided value is a plain object, not created from any class or special constructor.
|
|
9498
|
-
* Array, Uint8Array and others are not plain objects.
|
|
9499
|
-
* @param obj - The value to be checked.
|
|
9500
|
-
*/
|
|
9501
|
-
function isPlainObject(obj) {
|
|
9502
|
-
return Object.prototype.toString.call(obj) === '[object Object]';
|
|
9503
|
-
}
|
|
9504
|
-
function isNum(num) {
|
|
9505
|
-
return Number.isSafeInteger(num);
|
|
9506
|
-
}
|
|
9507
|
-
const utils = {
|
|
9508
|
-
equalBytes: equalBytes$1,
|
|
9509
|
-
isBytes: isBytes$1,
|
|
9510
|
-
concatBytes: concatBytes$1};
|
|
9511
|
-
// NOTE: we can't have terminator separate function, since it won't know about boundaries
|
|
9512
|
-
// E.g. array of U16LE ([1,2,3]) would be [1, 0, 2, 0, 3, 0]
|
|
9513
|
-
// But terminator will find array at index '1', which happens to be inside of an element itself
|
|
9514
|
-
/**
|
|
9515
|
-
* Can be:
|
|
9516
|
-
* - Dynamic (CoderType)
|
|
9517
|
-
* - Fixed (number)
|
|
9518
|
-
* - Terminated (usually zero): Uint8Array with terminator
|
|
9519
|
-
* - Field path to field with length (string)
|
|
9520
|
-
* - Infinity (null) - decodes until end of buffer
|
|
9521
|
-
* Used in:
|
|
9522
|
-
* - bytes (string, prefix is implementation of bytes)
|
|
9523
|
-
* - array
|
|
9524
|
-
*/
|
|
9525
|
-
const lengthCoder = (len) => {
|
|
9526
|
-
if (len !== null && typeof len !== 'string' && !isCoder(len) && !isBytes$1(len) && !isNum(len)) {
|
|
9527
|
-
throw new Error(`lengthCoder: expected null | number | Uint8Array | CoderType, got ${len} (${typeof len})`);
|
|
9528
|
-
}
|
|
9529
|
-
return {
|
|
9530
|
-
encodeStream(w, value) {
|
|
9531
|
-
if (len === null)
|
|
9532
|
-
return;
|
|
9533
|
-
if (isCoder(len))
|
|
9534
|
-
return len.encodeStream(w, value);
|
|
9535
|
-
let byteLen;
|
|
9536
|
-
if (typeof len === 'number')
|
|
9537
|
-
byteLen = len;
|
|
9538
|
-
else if (typeof len === 'string')
|
|
9539
|
-
byteLen = Path.resolve(w.stack, len);
|
|
9540
|
-
if (typeof byteLen === 'bigint')
|
|
9541
|
-
byteLen = Number(byteLen);
|
|
9542
|
-
if (byteLen === undefined || byteLen !== value)
|
|
9543
|
-
throw w.err(`Wrong length: ${byteLen} len=${len} exp=${value} (${typeof value})`);
|
|
9544
|
-
},
|
|
9545
|
-
decodeStream(r) {
|
|
9546
|
-
let byteLen;
|
|
9547
|
-
if (isCoder(len))
|
|
9548
|
-
byteLen = Number(len.decodeStream(r));
|
|
9549
|
-
else if (typeof len === 'number')
|
|
9550
|
-
byteLen = len;
|
|
9551
|
-
else if (typeof len === 'string')
|
|
9552
|
-
byteLen = Path.resolve(r.stack, len);
|
|
9553
|
-
if (typeof byteLen === 'bigint')
|
|
9554
|
-
byteLen = Number(byteLen);
|
|
9555
|
-
if (typeof byteLen !== 'number')
|
|
9556
|
-
throw r.err(`Wrong length: ${byteLen}`);
|
|
9557
|
-
return byteLen;
|
|
9558
|
-
},
|
|
9559
|
-
};
|
|
9560
|
-
};
|
|
9561
|
-
/**
|
|
9562
|
-
* Small bitset structure to store position of ranges that have been read.
|
|
9563
|
-
* Can be more efficient when internal trees are utilized at the cost of complexity.
|
|
9564
|
-
* Needs `O(N/8)` memory for parsing.
|
|
9565
|
-
* Purpose: if there are pointers in parsed structure,
|
|
9566
|
-
* they can cause read of two distinct ranges:
|
|
9567
|
-
* [0-32, 64-128], which means 'pos' is not enough to handle them
|
|
9568
|
-
*/
|
|
9569
|
-
const Bitset = {
|
|
9570
|
-
BITS: 32,
|
|
9571
|
-
FULL_MASK: -1 >>> 0, // 1<<32 will overflow
|
|
9572
|
-
len: (len) => Math.ceil(len / 32),
|
|
9573
|
-
create: (len) => new Uint32Array(Bitset.len(len)),
|
|
9574
|
-
clean: (bs) => bs.fill(0),
|
|
9575
|
-
debug: (bs) => Array.from(bs).map((i) => (i >>> 0).toString(2).padStart(32, '0')),
|
|
9576
|
-
checkLen: (bs, len) => {
|
|
9577
|
-
if (Bitset.len(len) === bs.length)
|
|
9578
|
-
return;
|
|
9579
|
-
throw new Error(`wrong length=${bs.length}. Expected: ${Bitset.len(len)}`);
|
|
9580
|
-
},
|
|
9581
|
-
chunkLen: (bsLen, pos, len) => {
|
|
9582
|
-
if (pos < 0)
|
|
9583
|
-
throw new Error(`wrong pos=${pos}`);
|
|
9584
|
-
if (pos + len > bsLen)
|
|
9585
|
-
throw new Error(`wrong range=${pos}/${len} of ${bsLen}`);
|
|
9586
|
-
},
|
|
9587
|
-
set: (bs, chunk, value, allowRewrite = true) => {
|
|
9588
|
-
if (!allowRewrite && (bs[chunk] & value) !== 0)
|
|
9589
|
-
return false;
|
|
9590
|
-
bs[chunk] |= value;
|
|
9591
|
-
return true;
|
|
9592
|
-
},
|
|
9593
|
-
pos: (pos, i) => ({
|
|
9594
|
-
chunk: Math.floor((pos + i) / 32),
|
|
9595
|
-
mask: 1 << (32 - ((pos + i) % 32) - 1),
|
|
9596
|
-
}),
|
|
9597
|
-
indices: (bs, len, invert = false) => {
|
|
9598
|
-
Bitset.checkLen(bs, len);
|
|
9599
|
-
const { FULL_MASK, BITS } = Bitset;
|
|
9600
|
-
const left = BITS - (len % BITS);
|
|
9601
|
-
const lastMask = left ? (FULL_MASK >>> left) << left : FULL_MASK;
|
|
9602
|
-
const res = [];
|
|
9603
|
-
for (let i = 0; i < bs.length; i++) {
|
|
9604
|
-
let c = bs[i];
|
|
9605
|
-
if (invert)
|
|
9606
|
-
c = ~c; // allows to gen unset elements
|
|
9607
|
-
// apply mask to last element, so we won't iterate non-existent items
|
|
9608
|
-
if (i === bs.length - 1)
|
|
9609
|
-
c &= lastMask;
|
|
9610
|
-
if (c === 0)
|
|
9611
|
-
continue; // fast-path
|
|
9612
|
-
for (let j = 0; j < BITS; j++) {
|
|
9613
|
-
const m = 1 << (BITS - j - 1);
|
|
9614
|
-
if (c & m)
|
|
9615
|
-
res.push(i * BITS + j);
|
|
9616
|
-
}
|
|
9617
|
-
}
|
|
9618
|
-
return res;
|
|
9619
|
-
},
|
|
9620
|
-
range: (arr) => {
|
|
9621
|
-
const res = [];
|
|
9622
|
-
let cur;
|
|
9623
|
-
for (const i of arr) {
|
|
9624
|
-
if (cur === undefined || i !== cur.pos + cur.length)
|
|
9625
|
-
res.push((cur = { pos: i, length: 1 }));
|
|
9626
|
-
else
|
|
9627
|
-
cur.length += 1;
|
|
9628
|
-
}
|
|
9629
|
-
return res;
|
|
9630
|
-
},
|
|
9631
|
-
rangeDebug: (bs, len, invert = false) => `[${Bitset.range(Bitset.indices(bs, len, invert))
|
|
9632
|
-
.map((i) => `(${i.pos}/${i.length})`)
|
|
9633
|
-
.join(', ')}]`,
|
|
9634
|
-
setRange: (bs, bsLen, pos, len, allowRewrite = true) => {
|
|
9635
|
-
Bitset.chunkLen(bsLen, pos, len);
|
|
9636
|
-
const { FULL_MASK, BITS } = Bitset;
|
|
9637
|
-
// Try to set range with maximum efficiency:
|
|
9638
|
-
// - first chunk is always '0000[1111]' (only right ones)
|
|
9639
|
-
// - middle chunks are set to '[1111 1111]' (all ones)
|
|
9640
|
-
// - last chunk is always '[1111]0000' (only left ones)
|
|
9641
|
-
// - max operations: (N/32) + 2 (first and last)
|
|
9642
|
-
const first = pos % BITS ? Math.floor(pos / BITS) : undefined;
|
|
9643
|
-
const lastPos = pos + len;
|
|
9644
|
-
const last = lastPos % BITS ? Math.floor(lastPos / BITS) : undefined;
|
|
9645
|
-
// special case, whole range inside single chunk
|
|
9646
|
-
if (first !== undefined && first === last)
|
|
9647
|
-
return Bitset.set(bs, first, (FULL_MASK >>> (BITS - len)) << (BITS - len - pos), allowRewrite);
|
|
9648
|
-
if (first !== undefined) {
|
|
9649
|
-
if (!Bitset.set(bs, first, FULL_MASK >>> pos % BITS, allowRewrite))
|
|
9650
|
-
return false; // first chunk
|
|
9651
|
-
}
|
|
9652
|
-
// middle chunks
|
|
9653
|
-
const start = first !== undefined ? first + 1 : pos / BITS;
|
|
9654
|
-
const end = last !== undefined ? last : lastPos / BITS;
|
|
9655
|
-
for (let i = start; i < end; i++)
|
|
9656
|
-
if (!Bitset.set(bs, i, FULL_MASK, allowRewrite))
|
|
9657
|
-
return false;
|
|
9658
|
-
if (last !== undefined && first !== last)
|
|
9659
|
-
if (!Bitset.set(bs, last, FULL_MASK << (BITS - (lastPos % BITS)), allowRewrite))
|
|
9660
|
-
return false; // last chunk
|
|
9661
|
-
return true;
|
|
9662
|
-
},
|
|
9663
|
-
};
|
|
9664
|
-
const Path = {
|
|
9665
|
-
/**
|
|
9666
|
-
* Internal method for handling stack of paths (debug, errors, dynamic fields via path)
|
|
9667
|
-
* This is looks ugly (callback), but allows us to force stack cleaning by construction (.pop always after function).
|
|
9668
|
-
* Also, this makes impossible:
|
|
9669
|
-
* - pushing field when stack is empty
|
|
9670
|
-
* - pushing field inside of field (real bug)
|
|
9671
|
-
* NOTE: we don't want to do '.pop' on error!
|
|
9672
|
-
*/
|
|
9673
|
-
pushObj: (stack, obj, objFn) => {
|
|
9674
|
-
const last = { obj };
|
|
9675
|
-
stack.push(last);
|
|
9676
|
-
objFn((field, fieldFn) => {
|
|
9677
|
-
last.field = field;
|
|
9678
|
-
fieldFn();
|
|
9679
|
-
last.field = undefined;
|
|
9680
|
-
});
|
|
9681
|
-
stack.pop();
|
|
9682
|
-
},
|
|
9683
|
-
path: (stack) => {
|
|
9684
|
-
const res = [];
|
|
9685
|
-
for (const i of stack)
|
|
9686
|
-
if (i.field !== undefined)
|
|
9687
|
-
res.push(i.field);
|
|
9688
|
-
return res.join('/');
|
|
9689
|
-
},
|
|
9690
|
-
err: (name, stack, msg) => {
|
|
9691
|
-
const err = new Error(`${name}(${Path.path(stack)}): ${typeof msg === 'string' ? msg : msg.message}`);
|
|
9692
|
-
if (msg instanceof Error && msg.stack)
|
|
9693
|
-
err.stack = msg.stack;
|
|
9694
|
-
return err;
|
|
9695
|
-
},
|
|
9696
|
-
resolve: (stack, path) => {
|
|
9697
|
-
const parts = path.split('/');
|
|
9698
|
-
const objPath = stack.map((i) => i.obj);
|
|
9699
|
-
let i = 0;
|
|
9700
|
-
for (; i < parts.length; i++) {
|
|
9701
|
-
if (parts[i] === '..')
|
|
9702
|
-
objPath.pop();
|
|
9703
|
-
else
|
|
9704
|
-
break;
|
|
9705
|
-
}
|
|
9706
|
-
let cur = objPath.pop();
|
|
9707
|
-
for (; i < parts.length; i++) {
|
|
9708
|
-
if (!cur || cur[parts[i]] === undefined)
|
|
9709
|
-
return undefined;
|
|
9710
|
-
cur = cur[parts[i]];
|
|
9711
|
-
}
|
|
9712
|
-
return cur;
|
|
9713
|
-
},
|
|
9714
|
-
};
|
|
9715
|
-
/**
|
|
9716
|
-
* Internal structure. Reader class for reading from a byte array.
|
|
9717
|
-
* `stack` is internal: for debugger and logging
|
|
9718
|
-
* @class Reader
|
|
9719
|
-
*/
|
|
9720
|
-
class _Reader {
|
|
9721
|
-
constructor(data, opts = {}, stack = [], parent = undefined, parentOffset = 0) {
|
|
9722
|
-
this.pos = 0;
|
|
9723
|
-
this.bitBuf = 0;
|
|
9724
|
-
this.bitPos = 0;
|
|
9725
|
-
this.data = data;
|
|
9726
|
-
this.opts = opts;
|
|
9727
|
-
this.stack = stack;
|
|
9728
|
-
this.parent = parent;
|
|
9729
|
-
this.parentOffset = parentOffset;
|
|
9730
|
-
this.view = createView(data);
|
|
9731
|
-
}
|
|
9732
|
-
/** Internal method for pointers. */
|
|
9733
|
-
_enablePointers() {
|
|
9734
|
-
if (this.parent)
|
|
9735
|
-
return this.parent._enablePointers();
|
|
9736
|
-
if (this.bs)
|
|
9737
|
-
return;
|
|
9738
|
-
this.bs = Bitset.create(this.data.length);
|
|
9739
|
-
Bitset.setRange(this.bs, this.data.length, 0, this.pos, this.opts.allowMultipleReads);
|
|
9740
|
-
}
|
|
9741
|
-
markBytesBS(pos, len) {
|
|
9742
|
-
if (this.parent)
|
|
9743
|
-
return this.parent.markBytesBS(this.parentOffset + pos, len);
|
|
9744
|
-
if (!len)
|
|
9745
|
-
return true;
|
|
9746
|
-
if (!this.bs)
|
|
9747
|
-
return true;
|
|
9748
|
-
return Bitset.setRange(this.bs, this.data.length, pos, len, false);
|
|
9749
|
-
}
|
|
9750
|
-
markBytes(len) {
|
|
9751
|
-
const pos = this.pos;
|
|
9752
|
-
this.pos += len;
|
|
9753
|
-
const res = this.markBytesBS(pos, len);
|
|
9754
|
-
if (!this.opts.allowMultipleReads && !res)
|
|
9755
|
-
throw this.err(`multiple read pos=${this.pos} len=${len}`);
|
|
9756
|
-
return res;
|
|
9757
|
-
}
|
|
9758
|
-
pushObj(obj, objFn) {
|
|
9759
|
-
return Path.pushObj(this.stack, obj, objFn);
|
|
9760
|
-
}
|
|
9761
|
-
readView(n, fn) {
|
|
9762
|
-
if (!Number.isFinite(n))
|
|
9763
|
-
throw this.err(`readView: wrong length=${n}`);
|
|
9764
|
-
if (this.pos + n > this.data.length)
|
|
9765
|
-
throw this.err('readView: Unexpected end of buffer');
|
|
9766
|
-
const res = fn(this.view, this.pos);
|
|
9767
|
-
this.markBytes(n);
|
|
9768
|
-
return res;
|
|
9769
|
-
}
|
|
9770
|
-
// read bytes by absolute offset
|
|
9771
|
-
absBytes(n) {
|
|
9772
|
-
if (n > this.data.length)
|
|
9773
|
-
throw new Error('Unexpected end of buffer');
|
|
9774
|
-
return this.data.subarray(n);
|
|
9775
|
-
}
|
|
9776
|
-
finish() {
|
|
9777
|
-
if (this.opts.allowUnreadBytes)
|
|
9778
|
-
return;
|
|
9779
|
-
if (this.bitPos) {
|
|
9780
|
-
throw this.err(`${this.bitPos} bits left after unpack: ${hex.encode(this.data.slice(this.pos))}`);
|
|
9781
|
-
}
|
|
9782
|
-
if (this.bs && !this.parent) {
|
|
9783
|
-
const notRead = Bitset.indices(this.bs, this.data.length, true);
|
|
9784
|
-
if (notRead.length) {
|
|
9785
|
-
const formatted = Bitset.range(notRead)
|
|
9786
|
-
.map(({ pos, length }) => `(${pos}/${length})[${hex.encode(this.data.subarray(pos, pos + length))}]`)
|
|
9787
|
-
.join(', ');
|
|
9788
|
-
throw this.err(`unread byte ranges: ${formatted} (total=${this.data.length})`);
|
|
9789
|
-
}
|
|
9790
|
-
else
|
|
9791
|
-
return; // all bytes read, everything is ok
|
|
9792
|
-
}
|
|
9793
|
-
// Default: no pointers enabled
|
|
9794
|
-
if (!this.isEnd()) {
|
|
9795
|
-
throw this.err(`${this.leftBytes} bytes ${this.bitPos} bits left after unpack: ${hex.encode(this.data.slice(this.pos))}`);
|
|
9796
|
-
}
|
|
9797
|
-
}
|
|
9798
|
-
// User methods
|
|
9799
|
-
err(msg) {
|
|
9800
|
-
return Path.err('Reader', this.stack, msg);
|
|
9801
|
-
}
|
|
9802
|
-
offsetReader(n) {
|
|
9803
|
-
if (n > this.data.length)
|
|
9804
|
-
throw this.err('offsetReader: Unexpected end of buffer');
|
|
9805
|
-
return new _Reader(this.absBytes(n), this.opts, this.stack, this, n);
|
|
9806
|
-
}
|
|
9807
|
-
bytes(n, peek = false) {
|
|
9808
|
-
if (this.bitPos)
|
|
9809
|
-
throw this.err('readBytes: bitPos not empty');
|
|
9810
|
-
if (!Number.isFinite(n))
|
|
9811
|
-
throw this.err(`readBytes: wrong length=${n}`);
|
|
9812
|
-
if (this.pos + n > this.data.length)
|
|
9813
|
-
throw this.err('readBytes: Unexpected end of buffer');
|
|
9814
|
-
const slice = this.data.subarray(this.pos, this.pos + n);
|
|
9815
|
-
if (!peek)
|
|
9816
|
-
this.markBytes(n);
|
|
9817
|
-
return slice;
|
|
9818
|
-
}
|
|
9819
|
-
byte(peek = false) {
|
|
9820
|
-
if (this.bitPos)
|
|
9821
|
-
throw this.err('readByte: bitPos not empty');
|
|
9822
|
-
if (this.pos + 1 > this.data.length)
|
|
9823
|
-
throw this.err('readBytes: Unexpected end of buffer');
|
|
9824
|
-
const data = this.data[this.pos];
|
|
9825
|
-
if (!peek)
|
|
9826
|
-
this.markBytes(1);
|
|
9827
|
-
return data;
|
|
9828
|
-
}
|
|
9829
|
-
get leftBytes() {
|
|
9830
|
-
return this.data.length - this.pos;
|
|
9831
|
-
}
|
|
9832
|
-
get totalBytes() {
|
|
9833
|
-
return this.data.length;
|
|
9834
|
-
}
|
|
9835
|
-
isEnd() {
|
|
9836
|
-
return this.pos >= this.data.length && !this.bitPos;
|
|
9837
|
-
}
|
|
9838
|
-
// bits are read in BE mode (left to right): (0b1000_0000).readBits(1) == 1
|
|
9839
|
-
bits(bits) {
|
|
9840
|
-
if (bits > 32)
|
|
9841
|
-
throw this.err('BitReader: cannot read more than 32 bits in single call');
|
|
9842
|
-
let out = 0;
|
|
9843
|
-
while (bits) {
|
|
9844
|
-
if (!this.bitPos) {
|
|
9845
|
-
this.bitBuf = this.byte();
|
|
9846
|
-
this.bitPos = 8;
|
|
9847
|
-
}
|
|
9848
|
-
const take = Math.min(bits, this.bitPos);
|
|
9849
|
-
this.bitPos -= take;
|
|
9850
|
-
out = (out << take) | ((this.bitBuf >> this.bitPos) & (2 ** take - 1));
|
|
9851
|
-
this.bitBuf &= 2 ** this.bitPos - 1;
|
|
9852
|
-
bits -= take;
|
|
9853
|
-
}
|
|
9854
|
-
// Fix signed integers
|
|
9855
|
-
return out >>> 0;
|
|
9856
|
-
}
|
|
9857
|
-
find(needle, pos = this.pos) {
|
|
9858
|
-
if (!isBytes$1(needle))
|
|
9859
|
-
throw this.err(`find: needle is not bytes! ${needle}`);
|
|
9860
|
-
if (this.bitPos)
|
|
9861
|
-
throw this.err('findByte: bitPos not empty');
|
|
9862
|
-
if (!needle.length)
|
|
9863
|
-
throw this.err(`find: needle is empty`);
|
|
9864
|
-
// indexOf should be faster than full equalBytes check
|
|
9865
|
-
for (let idx = pos; (idx = this.data.indexOf(needle[0], idx)) !== -1; idx++) {
|
|
9866
|
-
if (idx === -1)
|
|
9867
|
-
return;
|
|
9868
|
-
const leftBytes = this.data.length - idx;
|
|
9869
|
-
if (leftBytes < needle.length)
|
|
9870
|
-
return;
|
|
9871
|
-
if (equalBytes$1(needle, this.data.subarray(idx, idx + needle.length)))
|
|
9872
|
-
return idx;
|
|
9873
|
-
}
|
|
9874
|
-
return;
|
|
9875
|
-
}
|
|
9876
|
-
}
|
|
9877
|
-
/**
|
|
9878
|
-
* Internal structure. Writer class for writing to a byte array.
|
|
9879
|
-
* The `stack` argument of constructor is internal, for debugging and logs.
|
|
9880
|
-
* @class Writer
|
|
9881
|
-
*/
|
|
9882
|
-
class _Writer {
|
|
9883
|
-
constructor(stack = []) {
|
|
9884
|
-
this.pos = 0;
|
|
9885
|
-
// We could have a single buffer here and re-alloc it with
|
|
9886
|
-
// x1.5-2 size each time it full, but it will be slower:
|
|
9887
|
-
// basic/encode bench: 395ns -> 560ns
|
|
9888
|
-
this.buffers = [];
|
|
9889
|
-
this.ptrs = [];
|
|
9890
|
-
this.bitBuf = 0;
|
|
9891
|
-
this.bitPos = 0;
|
|
9892
|
-
this.viewBuf = new Uint8Array(8);
|
|
9893
|
-
this.finished = false;
|
|
9894
|
-
this.stack = stack;
|
|
9895
|
-
this.view = createView(this.viewBuf);
|
|
9896
|
-
}
|
|
9897
|
-
pushObj(obj, objFn) {
|
|
9898
|
-
return Path.pushObj(this.stack, obj, objFn);
|
|
9899
|
-
}
|
|
9900
|
-
writeView(len, fn) {
|
|
9901
|
-
if (this.finished)
|
|
9902
|
-
throw this.err('buffer: finished');
|
|
9903
|
-
if (!isNum(len) || len > 8)
|
|
9904
|
-
throw new Error(`wrong writeView length=${len}`);
|
|
9905
|
-
fn(this.view);
|
|
9906
|
-
this.bytes(this.viewBuf.slice(0, len));
|
|
9907
|
-
this.viewBuf.fill(0);
|
|
9908
|
-
}
|
|
9909
|
-
// User methods
|
|
9910
|
-
err(msg) {
|
|
9911
|
-
if (this.finished)
|
|
9912
|
-
throw this.err('buffer: finished');
|
|
9913
|
-
return Path.err('Reader', this.stack, msg);
|
|
9914
|
-
}
|
|
9915
|
-
bytes(b) {
|
|
9916
|
-
if (this.finished)
|
|
9917
|
-
throw this.err('buffer: finished');
|
|
9918
|
-
if (this.bitPos)
|
|
9919
|
-
throw this.err('writeBytes: ends with non-empty bit buffer');
|
|
9920
|
-
this.buffers.push(b);
|
|
9921
|
-
this.pos += b.length;
|
|
9922
|
-
}
|
|
9923
|
-
byte(b) {
|
|
9924
|
-
if (this.finished)
|
|
9925
|
-
throw this.err('buffer: finished');
|
|
9926
|
-
if (this.bitPos)
|
|
9927
|
-
throw this.err('writeByte: ends with non-empty bit buffer');
|
|
9928
|
-
this.buffers.push(new Uint8Array([b]));
|
|
9929
|
-
this.pos++;
|
|
9930
|
-
}
|
|
9931
|
-
finish(clean = true) {
|
|
9932
|
-
if (this.finished)
|
|
9933
|
-
throw this.err('buffer: finished');
|
|
9934
|
-
if (this.bitPos)
|
|
9935
|
-
throw this.err('buffer: ends with non-empty bit buffer');
|
|
9936
|
-
// Can't use concatBytes, because it limits amount of arguments (65K).
|
|
9937
|
-
const buffers = this.buffers.concat(this.ptrs.map((i) => i.buffer));
|
|
9938
|
-
const sum = buffers.map((b) => b.length).reduce((a, b) => a + b, 0);
|
|
9939
|
-
const buf = new Uint8Array(sum);
|
|
9940
|
-
for (let i = 0, pad = 0; i < buffers.length; i++) {
|
|
9941
|
-
const a = buffers[i];
|
|
9942
|
-
buf.set(a, pad);
|
|
9943
|
-
pad += a.length;
|
|
9944
|
-
}
|
|
9945
|
-
for (let pos = this.pos, i = 0; i < this.ptrs.length; i++) {
|
|
9946
|
-
const ptr = this.ptrs[i];
|
|
9947
|
-
buf.set(ptr.ptr.encode(pos), ptr.pos);
|
|
9948
|
-
pos += ptr.buffer.length;
|
|
9949
|
-
}
|
|
9950
|
-
// Cleanup
|
|
9951
|
-
if (clean) {
|
|
9952
|
-
// We cannot cleanup buffers here, since it can be static user provided buffer.
|
|
9953
|
-
// Only '.byte' and '.bits' create buffer which we can safely clean.
|
|
9954
|
-
// for (const b of this.buffers) b.fill(0);
|
|
9955
|
-
this.buffers = [];
|
|
9956
|
-
for (const p of this.ptrs)
|
|
9957
|
-
p.buffer.fill(0);
|
|
9958
|
-
this.ptrs = [];
|
|
9959
|
-
this.finished = true;
|
|
9960
|
-
this.bitBuf = 0;
|
|
9961
|
-
}
|
|
9962
|
-
return buf;
|
|
9963
|
-
}
|
|
9964
|
-
bits(value, bits) {
|
|
9965
|
-
if (bits > 32)
|
|
9966
|
-
throw this.err('writeBits: cannot write more than 32 bits in single call');
|
|
9967
|
-
if (value >= 2 ** bits)
|
|
9968
|
-
throw this.err(`writeBits: value (${value}) >= 2**bits (${bits})`);
|
|
9969
|
-
while (bits) {
|
|
9970
|
-
const take = Math.min(bits, 8 - this.bitPos);
|
|
9971
|
-
this.bitBuf = (this.bitBuf << take) | (value >> (bits - take));
|
|
9972
|
-
this.bitPos += take;
|
|
9973
|
-
bits -= take;
|
|
9974
|
-
value &= 2 ** bits - 1;
|
|
9975
|
-
if (this.bitPos === 8) {
|
|
9976
|
-
this.bitPos = 0;
|
|
9977
|
-
this.buffers.push(new Uint8Array([this.bitBuf]));
|
|
9978
|
-
this.pos++;
|
|
9979
|
-
}
|
|
9980
|
-
}
|
|
9981
|
-
}
|
|
9982
|
-
}
|
|
9983
|
-
// Immutable LE<->BE
|
|
9984
|
-
const swapEndianness = (b) => Uint8Array.from(b).reverse();
|
|
9985
|
-
/** Internal function for checking bit bounds of bigint in signed/unsinged form */
|
|
9986
|
-
function checkBounds(value, bits, signed) {
|
|
9987
|
-
if (signed) {
|
|
9988
|
-
// [-(2**(32-1)), 2**(32-1)-1]
|
|
9989
|
-
const signBit = 2n ** (bits - 1n);
|
|
9990
|
-
if (value < -signBit || value >= signBit)
|
|
9991
|
-
throw new Error(`value out of signed bounds. Expected ${-signBit} <= ${value} < ${signBit}`);
|
|
9992
|
-
}
|
|
9993
|
-
else {
|
|
9994
|
-
// [0, 2**32-1]
|
|
9995
|
-
if (0n > value || value >= 2n ** bits)
|
|
9996
|
-
throw new Error(`value out of unsigned bounds. Expected 0 <= ${value} < ${2n ** bits}`);
|
|
9997
|
-
}
|
|
9998
|
-
}
|
|
9999
|
-
function _wrap(inner) {
|
|
10000
|
-
return {
|
|
10001
|
-
// NOTE: we cannot export validate here, since it is likely mistake.
|
|
10002
|
-
encodeStream: inner.encodeStream,
|
|
10003
|
-
decodeStream: inner.decodeStream,
|
|
10004
|
-
size: inner.size,
|
|
10005
|
-
encode: (value) => {
|
|
10006
|
-
const w = new _Writer();
|
|
10007
|
-
inner.encodeStream(w, value);
|
|
10008
|
-
return w.finish();
|
|
10009
|
-
},
|
|
10010
|
-
decode: (data, opts = {}) => {
|
|
10011
|
-
const r = new _Reader(data, opts);
|
|
10012
|
-
const res = inner.decodeStream(r);
|
|
10013
|
-
r.finish();
|
|
10014
|
-
return res;
|
|
10015
|
-
},
|
|
10016
|
-
};
|
|
10017
|
-
}
|
|
10018
|
-
/**
|
|
10019
|
-
* Validates a value before encoding and after decoding using a provided function.
|
|
10020
|
-
* @param inner - The inner CoderType.
|
|
10021
|
-
* @param fn - The validation function.
|
|
10022
|
-
* @returns CoderType which check value with validation function.
|
|
10023
|
-
* @example
|
|
10024
|
-
* const val = (n: number) => {
|
|
10025
|
-
* if (n > 10) throw new Error(`${n} > 10`);
|
|
10026
|
-
* return n;
|
|
10027
|
-
* };
|
|
10028
|
-
*
|
|
10029
|
-
* const RangedInt = P.validate(P.U32LE, val); // Will check if value is <= 10 during encoding and decoding
|
|
10030
|
-
*/
|
|
10031
|
-
function validate(inner, fn) {
|
|
10032
|
-
if (!isCoder(inner))
|
|
10033
|
-
throw new Error(`validate: invalid inner value ${inner}`);
|
|
10034
|
-
if (typeof fn !== 'function')
|
|
10035
|
-
throw new Error('validate: fn should be function');
|
|
10036
|
-
return _wrap({
|
|
10037
|
-
size: inner.size,
|
|
10038
|
-
encodeStream: (w, value) => {
|
|
10039
|
-
let res;
|
|
10040
|
-
try {
|
|
10041
|
-
res = fn(value);
|
|
10042
|
-
}
|
|
10043
|
-
catch (e) {
|
|
10044
|
-
throw w.err(e);
|
|
10045
|
-
}
|
|
10046
|
-
inner.encodeStream(w, res);
|
|
10047
|
-
},
|
|
10048
|
-
decodeStream: (r) => {
|
|
10049
|
-
const res = inner.decodeStream(r);
|
|
10050
|
-
try {
|
|
10051
|
-
return fn(res);
|
|
10052
|
-
}
|
|
10053
|
-
catch (e) {
|
|
10054
|
-
throw r.err(e);
|
|
10055
|
-
}
|
|
10056
|
-
},
|
|
10057
|
-
});
|
|
10058
|
-
}
|
|
10059
|
-
/**
|
|
10060
|
-
* Wraps a stream encoder into a generic encoder and optionally validation function
|
|
10061
|
-
* @param {inner} inner BytesCoderStream & { validate?: Validate<T> }.
|
|
10062
|
-
* @returns The wrapped CoderType.
|
|
10063
|
-
* @example
|
|
10064
|
-
* const U8 = P.wrap({
|
|
10065
|
-
* encodeStream: (w: Writer, value: number) => w.byte(value),
|
|
10066
|
-
* decodeStream: (r: Reader): number => r.byte()
|
|
10067
|
-
* });
|
|
10068
|
-
* const checkedU8 = P.wrap({
|
|
10069
|
-
* encodeStream: (w: Writer, value: number) => w.byte(value),
|
|
10070
|
-
* decodeStream: (r: Reader): number => r.byte()
|
|
10071
|
-
* validate: (n: number) => {
|
|
10072
|
-
* if (n > 10) throw new Error(`${n} > 10`);
|
|
10073
|
-
* return n;
|
|
10074
|
-
* }
|
|
10075
|
-
* });
|
|
10076
|
-
*/
|
|
10077
|
-
const wrap = (inner) => {
|
|
10078
|
-
const res = _wrap(inner);
|
|
10079
|
-
return inner.validate ? validate(res, inner.validate) : res;
|
|
10080
|
-
};
|
|
10081
|
-
const isBaseCoder = (elm) => isPlainObject(elm) && typeof elm.decode === 'function' && typeof elm.encode === 'function';
|
|
10082
|
-
/**
|
|
10083
|
-
* Checks if the given value is a CoderType.
|
|
10084
|
-
* @param elm - The value to check.
|
|
10085
|
-
* @returns True if the value is a CoderType, false otherwise.
|
|
10086
|
-
*/
|
|
10087
|
-
function isCoder(elm) {
|
|
10088
|
-
return (isPlainObject(elm) &&
|
|
10089
|
-
isBaseCoder(elm) &&
|
|
10090
|
-
typeof elm.encodeStream === 'function' &&
|
|
10091
|
-
typeof elm.decodeStream === 'function' &&
|
|
10092
|
-
(elm.size === undefined || isNum(elm.size)));
|
|
10093
|
-
}
|
|
10094
|
-
// Coders (like in @scure/base) for common operations
|
|
10095
|
-
/**
|
|
10096
|
-
* Base coder for working with dictionaries (records, objects, key-value map)
|
|
10097
|
-
* Dictionary is dynamic type like: `[key: string, value: any][]`
|
|
10098
|
-
* @returns base coder that encodes/decodes between arrays of key-value tuples and dictionaries.
|
|
10099
|
-
* @example
|
|
10100
|
-
* const dict: P.CoderType<Record<string, number>> = P.apply(
|
|
10101
|
-
* P.array(P.U16BE, P.tuple([P.cstring, P.U32LE] as const)),
|
|
10102
|
-
* P.coders.dict()
|
|
10103
|
-
* );
|
|
10104
|
-
*/
|
|
10105
|
-
function dict() {
|
|
10106
|
-
return {
|
|
10107
|
-
encode: (from) => {
|
|
10108
|
-
if (!Array.isArray(from))
|
|
10109
|
-
throw new Error('array expected');
|
|
10110
|
-
const to = {};
|
|
10111
|
-
for (const item of from) {
|
|
10112
|
-
if (!Array.isArray(item) || item.length !== 2)
|
|
10113
|
-
throw new Error(`array of two elements expected`);
|
|
10114
|
-
const name = item[0];
|
|
10115
|
-
const value = item[1];
|
|
10116
|
-
if (to[name] !== undefined)
|
|
10117
|
-
throw new Error(`key(${name}) appears twice in struct`);
|
|
10118
|
-
to[name] = value;
|
|
10119
|
-
}
|
|
10120
|
-
return to;
|
|
10121
|
-
},
|
|
10122
|
-
decode: (to) => {
|
|
10123
|
-
if (!isPlainObject(to))
|
|
10124
|
-
throw new Error(`expected plain object, got ${to}`);
|
|
10125
|
-
return Object.entries(to);
|
|
10126
|
-
},
|
|
10127
|
-
};
|
|
10128
|
-
}
|
|
10129
|
-
/**
|
|
10130
|
-
* Safely converts bigint to number.
|
|
10131
|
-
* Sometimes pointers / tags use u64 or other big numbers which cannot be represented by number,
|
|
10132
|
-
* but we still can use them since real value will be smaller than u32
|
|
10133
|
-
*/
|
|
10134
|
-
const numberBigint = {
|
|
10135
|
-
encode: (from) => {
|
|
10136
|
-
if (typeof from !== 'bigint')
|
|
10137
|
-
throw new Error(`expected bigint, got ${typeof from}`);
|
|
10138
|
-
if (from > BigInt(Number.MAX_SAFE_INTEGER))
|
|
10139
|
-
throw new Error(`element bigger than MAX_SAFE_INTEGER=${from}`);
|
|
10140
|
-
return Number(from);
|
|
10141
|
-
},
|
|
10142
|
-
decode: (to) => {
|
|
10143
|
-
if (!isNum(to))
|
|
10144
|
-
throw new Error('element is not a safe integer');
|
|
10145
|
-
return BigInt(to);
|
|
10146
|
-
},
|
|
10147
|
-
};
|
|
10148
|
-
/**
|
|
10149
|
-
* Base coder for working with TypeScript enums.
|
|
10150
|
-
* @param e - TypeScript enum.
|
|
10151
|
-
* @returns base coder that encodes/decodes between numbers and enum keys.
|
|
10152
|
-
* @example
|
|
10153
|
-
* enum Color { Red, Green, Blue }
|
|
10154
|
-
* const colorCoder = P.coders.tsEnum(Color);
|
|
10155
|
-
* colorCoder.encode(Color.Red); // 'Red'
|
|
10156
|
-
* colorCoder.decode('Green'); // 1
|
|
10157
|
-
*/
|
|
10158
|
-
function tsEnum(e) {
|
|
10159
|
-
if (!isPlainObject(e))
|
|
10160
|
-
throw new Error('plain object expected');
|
|
10161
|
-
return {
|
|
10162
|
-
encode: (from) => {
|
|
10163
|
-
if (!isNum(from) || !(from in e))
|
|
10164
|
-
throw new Error(`wrong value ${from}`);
|
|
10165
|
-
return e[from];
|
|
10166
|
-
},
|
|
10167
|
-
decode: (to) => {
|
|
10168
|
-
if (typeof to !== 'string')
|
|
10169
|
-
throw new Error(`wrong value ${typeof to}`);
|
|
10170
|
-
return e[to];
|
|
10171
|
-
},
|
|
10172
|
-
};
|
|
10173
|
-
}
|
|
10174
|
-
/**
|
|
10175
|
-
* Base coder for working with decimal numbers.
|
|
10176
|
-
* @param precision - Number of decimal places.
|
|
10177
|
-
* @param round - Round fraction part if bigger than precision (throws error by default)
|
|
10178
|
-
* @returns base coder that encodes/decodes between bigints and decimal strings.
|
|
10179
|
-
* @example
|
|
10180
|
-
* const decimal8 = P.coders.decimal(8);
|
|
10181
|
-
* decimal8.encode(630880845n); // '6.30880845'
|
|
10182
|
-
* decimal8.decode('6.30880845'); // 630880845n
|
|
10183
|
-
*/
|
|
10184
|
-
function decimal(precision, round = false) {
|
|
10185
|
-
if (!isNum(precision))
|
|
10186
|
-
throw new Error(`decimal/precision: wrong value ${precision}`);
|
|
10187
|
-
if (typeof round !== 'boolean')
|
|
10188
|
-
throw new Error(`decimal/round: expected boolean, got ${typeof round}`);
|
|
10189
|
-
const decimalMask = 10n ** BigInt(precision);
|
|
10190
|
-
return {
|
|
10191
|
-
encode: (from) => {
|
|
10192
|
-
if (typeof from !== 'bigint')
|
|
10193
|
-
throw new Error(`expected bigint, got ${typeof from}`);
|
|
10194
|
-
let s = (from < 0n ? -from : from).toString(10);
|
|
10195
|
-
let sep = s.length - precision;
|
|
10196
|
-
if (sep < 0) {
|
|
10197
|
-
s = s.padStart(s.length - sep, '0');
|
|
10198
|
-
sep = 0;
|
|
10199
|
-
}
|
|
10200
|
-
let i = s.length - 1;
|
|
10201
|
-
for (; i >= sep && s[i] === '0'; i--)
|
|
10202
|
-
;
|
|
10203
|
-
let int = s.slice(0, sep);
|
|
10204
|
-
let frac = s.slice(sep, i + 1);
|
|
10205
|
-
if (!int)
|
|
10206
|
-
int = '0';
|
|
10207
|
-
if (from < 0n)
|
|
10208
|
-
int = '-' + int;
|
|
10209
|
-
if (!frac)
|
|
10210
|
-
return int;
|
|
10211
|
-
return `${int}.${frac}`;
|
|
10212
|
-
},
|
|
10213
|
-
decode: (to) => {
|
|
10214
|
-
if (typeof to !== 'string')
|
|
10215
|
-
throw new Error(`expected string, got ${typeof to}`);
|
|
10216
|
-
if (to === '-0')
|
|
10217
|
-
throw new Error(`negative zero is not allowed`);
|
|
10218
|
-
let neg = false;
|
|
10219
|
-
if (to.startsWith('-')) {
|
|
10220
|
-
neg = true;
|
|
10221
|
-
to = to.slice(1);
|
|
10222
|
-
}
|
|
10223
|
-
if (!/^(0|[1-9]\d*)(\.\d+)?$/.test(to))
|
|
10224
|
-
throw new Error(`wrong string value=${to}`);
|
|
10225
|
-
let sep = to.indexOf('.');
|
|
10226
|
-
sep = sep === -1 ? to.length : sep;
|
|
10227
|
-
// split by separator and strip trailing zeros from fraction. always returns [string, string] (.split doesn't).
|
|
10228
|
-
const intS = to.slice(0, sep);
|
|
10229
|
-
const fracS = to.slice(sep + 1).replace(/0+$/, '');
|
|
10230
|
-
const int = BigInt(intS) * decimalMask;
|
|
10231
|
-
if (!round && fracS.length > precision) {
|
|
10232
|
-
throw new Error(`fractional part cannot be represented with this precision (num=${to}, prec=${precision})`);
|
|
10233
|
-
}
|
|
10234
|
-
const fracLen = Math.min(fracS.length, precision);
|
|
10235
|
-
const frac = BigInt(fracS.slice(0, fracLen)) * 10n ** BigInt(precision - fracLen);
|
|
10236
|
-
const value = int + frac;
|
|
10237
|
-
return neg ? -value : value;
|
|
10238
|
-
},
|
|
10239
|
-
};
|
|
10240
|
-
}
|
|
10241
|
-
/**
|
|
10242
|
-
* Combines multiple coders into a single coder, allowing conditional encoding/decoding based on input.
|
|
10243
|
-
* Acts as a parser combinator, splitting complex conditional coders into smaller parts.
|
|
10244
|
-
*
|
|
10245
|
-
* `encode = [Ae, Be]; decode = [Ad, Bd]`
|
|
10246
|
-
* ->
|
|
10247
|
-
* `match([{encode: Ae, decode: Ad}, {encode: Be; decode: Bd}])`
|
|
10248
|
-
*
|
|
10249
|
-
* @param lst - Array of coders to match.
|
|
10250
|
-
* @returns Combined coder for conditional encoding/decoding.
|
|
10251
|
-
*/
|
|
10252
|
-
function match(lst) {
|
|
10253
|
-
if (!Array.isArray(lst))
|
|
10254
|
-
throw new Error(`expected array, got ${typeof lst}`);
|
|
10255
|
-
for (const i of lst)
|
|
10256
|
-
if (!isBaseCoder(i))
|
|
10257
|
-
throw new Error(`wrong base coder ${i}`);
|
|
10258
|
-
return {
|
|
10259
|
-
encode: (from) => {
|
|
10260
|
-
for (const c of lst) {
|
|
10261
|
-
const elm = c.encode(from);
|
|
10262
|
-
if (elm !== undefined)
|
|
10263
|
-
return elm;
|
|
10264
|
-
}
|
|
10265
|
-
throw new Error(`match/encode: cannot find match in ${from}`);
|
|
10266
|
-
},
|
|
10267
|
-
decode: (to) => {
|
|
10268
|
-
for (const c of lst) {
|
|
10269
|
-
const elm = c.decode(to);
|
|
10270
|
-
if (elm !== undefined)
|
|
10271
|
-
return elm;
|
|
10272
|
-
}
|
|
10273
|
-
throw new Error(`match/decode: cannot find match in ${to}`);
|
|
10274
|
-
},
|
|
10275
|
-
};
|
|
10276
|
-
}
|
|
10277
|
-
/** Reverses direction of coder */
|
|
10278
|
-
const reverse = (coder) => {
|
|
10279
|
-
if (!isBaseCoder(coder))
|
|
10280
|
-
throw new Error('BaseCoder expected');
|
|
10281
|
-
return { encode: coder.decode, decode: coder.encode };
|
|
10282
|
-
};
|
|
10283
|
-
const coders = { dict, numberBigint, tsEnum, decimal, match, reverse };
|
|
10284
|
-
/**
|
|
10285
|
-
* CoderType for working with bigint values.
|
|
10286
|
-
* Unsized bigint values should be wrapped in a container (e.g., bytes or string).
|
|
10287
|
-
*
|
|
10288
|
-
* `0n = new Uint8Array([])`
|
|
10289
|
-
*
|
|
10290
|
-
* `1n = new Uint8Array([1n])`
|
|
10291
|
-
*
|
|
10292
|
-
* Please open issue, if you need different behavior for zero.
|
|
10293
|
-
*
|
|
10294
|
-
* @param size - Size of the bigint in bytes.
|
|
10295
|
-
* @param le - Whether to use little-endian byte order.
|
|
10296
|
-
* @param signed - Whether the bigint is signed.
|
|
10297
|
-
* @param sized - Whether the bigint should have a fixed size.
|
|
10298
|
-
* @returns CoderType representing the bigint value.
|
|
10299
|
-
* @example
|
|
10300
|
-
* const U512BE = P.bigint(64, false, true, true); // Define a CoderType for a 512-bit unsigned big-endian integer
|
|
10301
|
-
*/
|
|
10302
|
-
const bigint = (size, le = false, signed = false, sized = true) => {
|
|
10303
|
-
if (!isNum(size))
|
|
10304
|
-
throw new Error(`bigint/size: wrong value ${size}`);
|
|
10305
|
-
if (typeof le !== 'boolean')
|
|
10306
|
-
throw new Error(`bigint/le: expected boolean, got ${typeof le}`);
|
|
10307
|
-
if (typeof signed !== 'boolean')
|
|
10308
|
-
throw new Error(`bigint/signed: expected boolean, got ${typeof signed}`);
|
|
10309
|
-
if (typeof sized !== 'boolean')
|
|
10310
|
-
throw new Error(`bigint/sized: expected boolean, got ${typeof sized}`);
|
|
10311
|
-
const bLen = BigInt(size);
|
|
10312
|
-
const signBit = 2n ** (8n * bLen - 1n);
|
|
10313
|
-
return wrap({
|
|
10314
|
-
size: sized ? size : undefined,
|
|
10315
|
-
encodeStream: (w, value) => {
|
|
10316
|
-
if (signed && value < 0)
|
|
10317
|
-
value = value | signBit;
|
|
10318
|
-
const b = [];
|
|
10319
|
-
for (let i = 0; i < size; i++) {
|
|
10320
|
-
b.push(Number(value & 255n));
|
|
10321
|
-
value >>= 8n;
|
|
10322
|
-
}
|
|
10323
|
-
let res = new Uint8Array(b).reverse();
|
|
10324
|
-
if (!sized) {
|
|
10325
|
-
let pos = 0;
|
|
10326
|
-
for (pos = 0; pos < res.length; pos++)
|
|
10327
|
-
if (res[pos] !== 0)
|
|
10328
|
-
break;
|
|
10329
|
-
res = res.subarray(pos); // remove leading zeros
|
|
10330
|
-
}
|
|
10331
|
-
w.bytes(le ? res.reverse() : res);
|
|
10332
|
-
},
|
|
10333
|
-
decodeStream: (r) => {
|
|
10334
|
-
// TODO: for le we can read until first zero?
|
|
10335
|
-
const value = r.bytes(sized ? size : Math.min(size, r.leftBytes));
|
|
10336
|
-
const b = le ? value : swapEndianness(value);
|
|
10337
|
-
let res = 0n;
|
|
10338
|
-
for (let i = 0; i < b.length; i++)
|
|
10339
|
-
res |= BigInt(b[i]) << (8n * BigInt(i));
|
|
10340
|
-
if (signed && res & signBit)
|
|
10341
|
-
res = (res ^ signBit) - signBit;
|
|
10342
|
-
return res;
|
|
10343
|
-
},
|
|
10344
|
-
validate: (value) => {
|
|
10345
|
-
if (typeof value !== 'bigint')
|
|
10346
|
-
throw new Error(`bigint: invalid value: ${value}`);
|
|
10347
|
-
checkBounds(value, 8n * bLen, !!signed);
|
|
10348
|
-
return value;
|
|
10349
|
-
},
|
|
10350
|
-
});
|
|
10351
|
-
};
|
|
10352
|
-
/** Unsigned 256-bit big-endian integer CoderType. */
|
|
10353
|
-
const U256BE = /* @__PURE__ */ bigint(32, false);
|
|
10354
|
-
/** Unsigned 64-bit little-endian integer CoderType. */
|
|
10355
|
-
const U64LE = /* @__PURE__ */ bigint(8, true);
|
|
10356
|
-
/** Signed 64-bit little-endian integer CoderType. */
|
|
10357
|
-
const I64LE = /* @__PURE__ */ bigint(8, true, true);
|
|
10358
|
-
const view = (len, opts) => wrap({
|
|
10359
|
-
size: len,
|
|
10360
|
-
encodeStream: (w, value) => w.writeView(len, (view) => opts.write(view, value)),
|
|
10361
|
-
decodeStream: (r) => r.readView(len, opts.read),
|
|
10362
|
-
validate: (value) => {
|
|
10363
|
-
if (typeof value !== 'number')
|
|
10364
|
-
throw new Error(`viewCoder: expected number, got ${typeof value}`);
|
|
10365
|
-
if (opts.validate)
|
|
10366
|
-
opts.validate(value);
|
|
10367
|
-
return value;
|
|
10368
|
-
},
|
|
10369
|
-
});
|
|
10370
|
-
const intView = (len, signed, opts) => {
|
|
10371
|
-
const bits = len * 8;
|
|
10372
|
-
const signBit = 2 ** (bits - 1);
|
|
10373
|
-
// Inlined checkBounds for integer
|
|
10374
|
-
const validateSigned = (value) => {
|
|
10375
|
-
if (!isNum(value))
|
|
10376
|
-
throw new Error(`sintView: value is not safe integer: ${value}`);
|
|
10377
|
-
if (value < -signBit || value >= signBit) {
|
|
10378
|
-
throw new Error(`sintView: value out of bounds. Expected ${-signBit} <= ${value} < ${signBit}`);
|
|
10379
|
-
}
|
|
10380
|
-
};
|
|
10381
|
-
const maxVal = 2 ** bits;
|
|
10382
|
-
const validateUnsigned = (value) => {
|
|
10383
|
-
if (!isNum(value))
|
|
10384
|
-
throw new Error(`uintView: value is not safe integer: ${value}`);
|
|
10385
|
-
if (0 > value || value >= maxVal) {
|
|
10386
|
-
throw new Error(`uintView: value out of bounds. Expected 0 <= ${value} < ${maxVal}`);
|
|
10387
|
-
}
|
|
10388
|
-
};
|
|
10389
|
-
return view(len, {
|
|
10390
|
-
write: opts.write,
|
|
10391
|
-
read: opts.read,
|
|
10392
|
-
validate: signed ? validateSigned : validateUnsigned,
|
|
10393
|
-
});
|
|
10394
|
-
};
|
|
10395
|
-
/** Unsigned 32-bit little-endian integer CoderType. */
|
|
10396
|
-
const U32LE = /* @__PURE__ */ intView(4, false, {
|
|
10397
|
-
read: (view, pos) => view.getUint32(pos, true),
|
|
10398
|
-
write: (view, value) => view.setUint32(0, value, true),
|
|
10399
|
-
});
|
|
10400
|
-
/** Unsigned 32-bit big-endian integer CoderType. */
|
|
10401
|
-
const U32BE = /* @__PURE__ */ intView(4, false, {
|
|
10402
|
-
read: (view, pos) => view.getUint32(pos, false),
|
|
10403
|
-
write: (view, value) => view.setUint32(0, value, false),
|
|
10404
|
-
});
|
|
10405
|
-
/** Signed 32-bit little-endian integer CoderType. */
|
|
10406
|
-
const I32LE = /* @__PURE__ */ intView(4, true, {
|
|
10407
|
-
read: (view, pos) => view.getInt32(pos, true),
|
|
10408
|
-
write: (view, value) => view.setInt32(0, value, true),
|
|
10409
|
-
});
|
|
10410
|
-
/** Unsigned 16-bit little-endian integer CoderType. */
|
|
10411
|
-
const U16LE = /* @__PURE__ */ intView(2, false, {
|
|
10412
|
-
read: (view, pos) => view.getUint16(pos, true),
|
|
10413
|
-
write: (view, value) => view.setUint16(0, value, true),
|
|
10414
|
-
});
|
|
10415
|
-
/** Unsigned 8-bit integer CoderType. */
|
|
10416
|
-
const U8 = /* @__PURE__ */ intView(1, false, {
|
|
10417
|
-
read: (view, pos) => view.getUint8(pos),
|
|
10418
|
-
write: (view, value) => view.setUint8(0, value),
|
|
10419
|
-
});
|
|
10420
|
-
/**
|
|
10421
|
-
* Bytes CoderType with a specified length and endianness.
|
|
10422
|
-
* The bytes can have:
|
|
10423
|
-
* - Dynamic size (prefixed with a length CoderType like U16BE)
|
|
10424
|
-
* - Fixed size (specified by a number)
|
|
10425
|
-
* - Unknown size (null, will parse until end of buffer)
|
|
10426
|
-
* - Zero-terminated (terminator can be any Uint8Array)
|
|
10427
|
-
* @param len - CoderType, number, Uint8Array (terminator) or null
|
|
10428
|
-
* @param le - Whether to use little-endian byte order.
|
|
10429
|
-
* @returns CoderType representing the bytes.
|
|
10430
|
-
* @example
|
|
10431
|
-
* // Dynamic size bytes (prefixed with P.U16BE number of bytes length)
|
|
10432
|
-
* const dynamicBytes = P.bytes(P.U16BE, false);
|
|
10433
|
-
* const fixedBytes = P.bytes(32, false); // Fixed size bytes
|
|
10434
|
-
* const unknownBytes = P.bytes(null, false); // Unknown size bytes, will parse until end of buffer
|
|
10435
|
-
* const zeroTerminatedBytes = P.bytes(new Uint8Array([0]), false); // Zero-terminated bytes
|
|
10436
|
-
*/
|
|
10437
|
-
const createBytes = (len, le = false) => {
|
|
10438
|
-
if (typeof le !== 'boolean')
|
|
10439
|
-
throw new Error(`bytes/le: expected boolean, got ${typeof le}`);
|
|
10440
|
-
const _length = lengthCoder(len);
|
|
10441
|
-
const _isb = isBytes$1(len);
|
|
10442
|
-
return wrap({
|
|
10443
|
-
size: typeof len === 'number' ? len : undefined,
|
|
10444
|
-
encodeStream: (w, value) => {
|
|
10445
|
-
if (!_isb)
|
|
10446
|
-
_length.encodeStream(w, value.length);
|
|
10447
|
-
w.bytes(le ? swapEndianness(value) : value);
|
|
10448
|
-
if (_isb)
|
|
10449
|
-
w.bytes(len);
|
|
10450
|
-
},
|
|
10451
|
-
decodeStream: (r) => {
|
|
10452
|
-
let bytes;
|
|
10453
|
-
if (_isb) {
|
|
10454
|
-
const tPos = r.find(len);
|
|
10455
|
-
if (!tPos)
|
|
10456
|
-
throw r.err(`bytes: cannot find terminator`);
|
|
10457
|
-
bytes = r.bytes(tPos - r.pos);
|
|
10458
|
-
r.bytes(len.length);
|
|
10459
|
-
}
|
|
10460
|
-
else {
|
|
10461
|
-
bytes = r.bytes(len === null ? r.leftBytes : _length.decodeStream(r));
|
|
10462
|
-
}
|
|
10463
|
-
return le ? swapEndianness(bytes) : bytes;
|
|
10464
|
-
},
|
|
10465
|
-
validate: (value) => {
|
|
10466
|
-
if (!isBytes$1(value))
|
|
10467
|
-
throw new Error(`bytes: invalid value ${value}`);
|
|
10468
|
-
return value;
|
|
10469
|
-
},
|
|
10470
|
-
});
|
|
10471
|
-
};
|
|
10472
|
-
/**
|
|
10473
|
-
* Prefix-encoded value using a length prefix and an inner CoderType.
|
|
10474
|
-
* The prefix can have:
|
|
10475
|
-
* - Dynamic size (prefixed with a length CoderType like U16BE)
|
|
10476
|
-
* - Fixed size (specified by a number)
|
|
10477
|
-
* - Unknown size (null, will parse until end of buffer)
|
|
10478
|
-
* - Zero-terminated (terminator can be any Uint8Array)
|
|
10479
|
-
* @param len - Length CoderType (dynamic size), number (fixed size), Uint8Array (for terminator), or null (will parse until end of buffer)
|
|
10480
|
-
* @param inner - CoderType for the actual value to be prefix-encoded.
|
|
10481
|
-
* @returns CoderType representing the prefix-encoded value.
|
|
10482
|
-
* @example
|
|
10483
|
-
* const dynamicPrefix = P.prefix(P.U16BE, P.bytes(null)); // Dynamic size prefix (prefixed with P.U16BE number of bytes length)
|
|
10484
|
-
* const fixedPrefix = P.prefix(10, P.bytes(null)); // Fixed size prefix (always 10 bytes)
|
|
10485
|
-
*/
|
|
10486
|
-
function prefix(len, inner) {
|
|
10487
|
-
if (!isCoder(inner))
|
|
10488
|
-
throw new Error(`prefix: invalid inner value ${inner}`);
|
|
10489
|
-
return apply(createBytes(len), reverse(inner));
|
|
10490
|
-
}
|
|
10491
|
-
/**
|
|
10492
|
-
* String CoderType with a specified length and endianness.
|
|
10493
|
-
* The string can be:
|
|
10494
|
-
* - Dynamic size (prefixed with a length CoderType like U16BE)
|
|
10495
|
-
* - Fixed size (specified by a number)
|
|
10496
|
-
* - Unknown size (null, will parse until end of buffer)
|
|
10497
|
-
* - Zero-terminated (terminator can be any Uint8Array)
|
|
10498
|
-
* @param len - Length CoderType (dynamic size), number (fixed size), Uint8Array (for terminator), or null (will parse until end of buffer)
|
|
10499
|
-
* @param le - Whether to use little-endian byte order.
|
|
10500
|
-
* @returns CoderType representing the string.
|
|
10501
|
-
* @example
|
|
10502
|
-
* const dynamicString = P.string(P.U16BE, false); // Dynamic size string (prefixed with P.U16BE number of string length)
|
|
10503
|
-
* const fixedString = P.string(10, false); // Fixed size string
|
|
10504
|
-
* const unknownString = P.string(null, false); // Unknown size string, will parse until end of buffer
|
|
10505
|
-
* const nullTerminatedString = P.cstring; // NUL-terminated string
|
|
10506
|
-
* const _cstring = P.string(new Uint8Array([0])); // Same thing
|
|
10507
|
-
*/
|
|
10508
|
-
const string = (len, le = false) => validate(apply(createBytes(len, le), utf8), (value) => {
|
|
10509
|
-
// TextEncoder/TextDecoder will fail on non-string, but we create more readable errors earlier
|
|
10510
|
-
if (typeof value !== 'string')
|
|
10511
|
-
throw new Error(`expected string, got ${typeof value}`);
|
|
10512
|
-
return value;
|
|
10513
|
-
});
|
|
10514
|
-
/**
|
|
10515
|
-
* Hexadecimal string CoderType with a specified length, endianness, and optional 0x prefix.
|
|
10516
|
-
* @param len - Length CoderType (dynamic size), number (fixed size), Uint8Array (for terminator), or null (will parse until end of buffer)
|
|
10517
|
-
* @param le - Whether to use little-endian byte order.
|
|
10518
|
-
* @param withZero - Whether to include the 0x prefix.
|
|
10519
|
-
* @returns CoderType representing the hexadecimal string.
|
|
10520
|
-
* @example
|
|
10521
|
-
* const dynamicHex = P.hex(P.U16BE, {isLE: false, with0x: true}); // Hex string with 0x prefix and U16BE length
|
|
10522
|
-
* const fixedHex = P.hex(32, {isLE: false, with0x: false}); // Fixed-length 32-byte hex string without 0x prefix
|
|
10523
|
-
*/
|
|
10524
|
-
const createHex = (len, options = { isLE: false, with0x: false }) => {
|
|
10525
|
-
let inner = apply(createBytes(len, options.isLE), hex);
|
|
10526
|
-
const prefix = options.with0x;
|
|
10527
|
-
if (typeof prefix !== 'boolean')
|
|
10528
|
-
throw new Error(`hex/with0x: expected boolean, got ${typeof prefix}`);
|
|
10529
|
-
if (prefix) {
|
|
10530
|
-
inner = apply(inner, {
|
|
10531
|
-
encode: (value) => `0x${value}`,
|
|
10532
|
-
decode: (value) => {
|
|
10533
|
-
if (!value.startsWith('0x'))
|
|
10534
|
-
throw new Error('hex(with0x=true).encode input should start with 0x');
|
|
10535
|
-
return value.slice(2);
|
|
10536
|
-
},
|
|
10537
|
-
});
|
|
10538
|
-
}
|
|
10539
|
-
return inner;
|
|
10540
|
-
};
|
|
10541
|
-
/**
|
|
10542
|
-
* Applies a base coder to a CoderType.
|
|
10543
|
-
* @param inner - The inner CoderType.
|
|
10544
|
-
* @param b - The base coder to apply.
|
|
10545
|
-
* @returns CoderType representing the transformed value.
|
|
10546
|
-
* @example
|
|
10547
|
-
* import { hex } from '@scure/base';
|
|
10548
|
-
* const hex = P.apply(P.bytes(32), hex); // will decode bytes into a hex string
|
|
10549
|
-
*/
|
|
10550
|
-
function apply(inner, base) {
|
|
10551
|
-
if (!isCoder(inner))
|
|
10552
|
-
throw new Error(`apply: invalid inner value ${inner}`);
|
|
10553
|
-
if (!isBaseCoder(base))
|
|
10554
|
-
throw new Error(`apply: invalid base value ${inner}`);
|
|
10555
|
-
return wrap({
|
|
10556
|
-
size: inner.size,
|
|
10557
|
-
encodeStream: (w, value) => {
|
|
10558
|
-
let innerValue;
|
|
10559
|
-
try {
|
|
10560
|
-
innerValue = base.decode(value);
|
|
10561
|
-
}
|
|
10562
|
-
catch (e) {
|
|
10563
|
-
throw w.err('' + e);
|
|
10564
|
-
}
|
|
10565
|
-
return inner.encodeStream(w, innerValue);
|
|
10566
|
-
},
|
|
10567
|
-
decodeStream: (r) => {
|
|
10568
|
-
const innerValue = inner.decodeStream(r);
|
|
10569
|
-
try {
|
|
10570
|
-
return base.encode(innerValue);
|
|
10571
|
-
}
|
|
10572
|
-
catch (e) {
|
|
10573
|
-
throw r.err('' + e);
|
|
10574
|
-
}
|
|
10575
|
-
},
|
|
10576
|
-
});
|
|
10577
|
-
}
|
|
10578
|
-
/**
|
|
10579
|
-
* Flag CoderType that encodes/decodes a boolean value based on the presence of a marker.
|
|
10580
|
-
* @param flagValue - Marker value.
|
|
10581
|
-
* @param xor - Whether to invert the flag behavior.
|
|
10582
|
-
* @returns CoderType representing the flag value.
|
|
10583
|
-
* @example
|
|
10584
|
-
* const flag = P.flag(new Uint8Array([0x01, 0x02])); // Encodes true as u8a([0x01, 0x02]), false as u8a([])
|
|
10585
|
-
* const flagXor = P.flag(new Uint8Array([0x01, 0x02]), true); // Encodes true as u8a([]), false as u8a([0x01, 0x02])
|
|
10586
|
-
* // Conditional encoding with flagged
|
|
10587
|
-
* const s = P.struct({ f: P.flag(new Uint8Array([0x0, 0x1])), f2: P.flagged('f', P.U32BE) });
|
|
10588
|
-
*/
|
|
10589
|
-
const flag = (flagValue, xor = false) => {
|
|
10590
|
-
if (!isBytes$1(flagValue))
|
|
10591
|
-
throw new Error(`flag/flagValue: expected Uint8Array, got ${typeof flagValue}`);
|
|
10592
|
-
if (typeof xor !== 'boolean')
|
|
10593
|
-
throw new Error(`flag/xor: expected boolean, got ${typeof xor}`);
|
|
10594
|
-
return wrap({
|
|
10595
|
-
size: flagValue.length,
|
|
10596
|
-
encodeStream: (w, value) => {
|
|
10597
|
-
if (!!value !== xor)
|
|
10598
|
-
w.bytes(flagValue);
|
|
10599
|
-
},
|
|
10600
|
-
decodeStream: (r) => {
|
|
10601
|
-
let hasFlag = r.leftBytes >= flagValue.length;
|
|
10602
|
-
if (hasFlag) {
|
|
10603
|
-
hasFlag = equalBytes$1(r.bytes(flagValue.length, true), flagValue);
|
|
10604
|
-
// Found flag, advance cursor position
|
|
10605
|
-
if (hasFlag)
|
|
10606
|
-
r.bytes(flagValue.length);
|
|
10607
|
-
}
|
|
10608
|
-
return hasFlag !== xor; // hasFlag ^ xor
|
|
10609
|
-
},
|
|
10610
|
-
validate: (value) => {
|
|
10611
|
-
if (value !== undefined && typeof value !== 'boolean')
|
|
10612
|
-
throw new Error(`flag: expected boolean value or undefined, got ${typeof value}`);
|
|
10613
|
-
return value;
|
|
10614
|
-
},
|
|
10615
|
-
});
|
|
10616
|
-
};
|
|
10617
|
-
/**
|
|
10618
|
-
* Conditional CoderType that encodes/decodes a value only if a flag is present.
|
|
10619
|
-
* @param path - Path to the flag value or a CoderType for the flag.
|
|
10620
|
-
* @param inner - Inner CoderType for the value.
|
|
10621
|
-
* @param def - Optional default value to use if the flag is not present.
|
|
10622
|
-
* @returns CoderType representing the conditional value.
|
|
10623
|
-
* @example
|
|
10624
|
-
* const s = P.struct({
|
|
10625
|
-
* f: P.flag(new Uint8Array([0x0, 0x1])),
|
|
10626
|
-
* f2: P.flagged('f', P.U32BE)
|
|
10627
|
-
* });
|
|
10628
|
-
*
|
|
10629
|
-
* @example
|
|
10630
|
-
* const s2 = P.struct({
|
|
10631
|
-
* f: P.flag(new Uint8Array([0x0, 0x1])),
|
|
10632
|
-
* f2: P.flagged('f', P.U32BE, 123)
|
|
10633
|
-
* });
|
|
10634
|
-
*/
|
|
10635
|
-
function flagged(path, inner, def) {
|
|
10636
|
-
if (!isCoder(inner))
|
|
10637
|
-
throw new Error(`flagged: invalid inner value ${inner}`);
|
|
10638
|
-
return wrap({
|
|
10639
|
-
encodeStream: (w, value) => {
|
|
10640
|
-
{
|
|
10641
|
-
if (Path.resolve(w.stack, path))
|
|
10642
|
-
inner.encodeStream(w, value);
|
|
10643
|
-
}
|
|
10644
|
-
},
|
|
10645
|
-
decodeStream: (r) => {
|
|
10646
|
-
let hasFlag = false;
|
|
10647
|
-
hasFlag = !!Path.resolve(r.stack, path);
|
|
10648
|
-
// If there is a flag -- decode and return value
|
|
10649
|
-
if (hasFlag)
|
|
10650
|
-
return inner.decodeStream(r);
|
|
10651
|
-
return;
|
|
10652
|
-
},
|
|
10653
|
-
});
|
|
10654
|
-
}
|
|
10655
|
-
/**
|
|
10656
|
-
* Magic value CoderType that encodes/decodes a constant value.
|
|
10657
|
-
* This can be used to check for a specific magic value or sequence of bytes at the beginning of a data structure.
|
|
10658
|
-
* @param inner - Inner CoderType for the value.
|
|
10659
|
-
* @param constant - Constant value.
|
|
10660
|
-
* @param check - Whether to check the decoded value against the constant.
|
|
10661
|
-
* @returns CoderType representing the magic value.
|
|
10662
|
-
* @example
|
|
10663
|
-
* // Always encodes constant as bytes using inner CoderType, throws if encoded value is not present
|
|
10664
|
-
* const magicU8 = P.magic(P.U8, 0x42);
|
|
10665
|
-
*/
|
|
10666
|
-
function magic(inner, constant, check = true) {
|
|
10667
|
-
if (!isCoder(inner))
|
|
10668
|
-
throw new Error(`magic: invalid inner value ${inner}`);
|
|
10669
|
-
if (typeof check !== 'boolean')
|
|
10670
|
-
throw new Error(`magic: expected boolean, got ${typeof check}`);
|
|
10671
|
-
return wrap({
|
|
10672
|
-
size: inner.size,
|
|
10673
|
-
encodeStream: (w, _value) => inner.encodeStream(w, constant),
|
|
10674
|
-
decodeStream: (r) => {
|
|
10675
|
-
const value = inner.decodeStream(r);
|
|
10676
|
-
if ((check && typeof value !== 'object' && value !== constant) ||
|
|
10677
|
-
(isBytes$1(constant) && !equalBytes$1(constant, value))) {
|
|
10678
|
-
throw r.err(`magic: invalid value: ${value} !== ${constant}`);
|
|
10679
|
-
}
|
|
10680
|
-
return;
|
|
10681
|
-
},
|
|
10682
|
-
validate: (value) => {
|
|
10683
|
-
if (value !== undefined)
|
|
10684
|
-
throw new Error(`magic: wrong value=${typeof value}`);
|
|
10685
|
-
return value;
|
|
10686
|
-
},
|
|
10687
|
-
});
|
|
10688
|
-
}
|
|
10689
|
-
function sizeof(fields) {
|
|
10690
|
-
let size = 0;
|
|
10691
|
-
for (const f of fields) {
|
|
10692
|
-
if (f.size === undefined)
|
|
10693
|
-
return;
|
|
10694
|
-
if (!isNum(f.size))
|
|
10695
|
-
throw new Error(`sizeof: wrong element size=${size}`);
|
|
10696
|
-
size += f.size;
|
|
10697
|
-
}
|
|
10698
|
-
return size;
|
|
10699
|
-
}
|
|
10700
|
-
/**
|
|
10701
|
-
* Structure of composable primitives (C/Rust struct)
|
|
10702
|
-
* @param fields - Object mapping field names to CoderTypes.
|
|
10703
|
-
* @returns CoderType representing the structure.
|
|
10704
|
-
* @example
|
|
10705
|
-
* // Define a structure with a 32-bit big-endian unsigned integer, a string, and a nested structure
|
|
10706
|
-
* const myStruct = P.struct({
|
|
10707
|
-
* id: P.U32BE,
|
|
10708
|
-
* name: P.string(P.U8),
|
|
10709
|
-
* nested: P.struct({
|
|
10710
|
-
* flag: P.bool,
|
|
10711
|
-
* value: P.I16LE
|
|
10712
|
-
* })
|
|
10713
|
-
* });
|
|
10714
|
-
*/
|
|
10715
|
-
function struct(fields) {
|
|
10716
|
-
if (!isPlainObject(fields))
|
|
10717
|
-
throw new Error(`struct: expected plain object, got ${fields}`);
|
|
10718
|
-
for (const name in fields) {
|
|
10719
|
-
if (!isCoder(fields[name]))
|
|
10720
|
-
throw new Error(`struct: field ${name} is not CoderType`);
|
|
10721
|
-
}
|
|
10722
|
-
return wrap({
|
|
10723
|
-
size: sizeof(Object.values(fields)),
|
|
10724
|
-
encodeStream: (w, value) => {
|
|
10725
|
-
w.pushObj(value, (fieldFn) => {
|
|
10726
|
-
for (const name in fields)
|
|
10727
|
-
fieldFn(name, () => fields[name].encodeStream(w, value[name]));
|
|
10728
|
-
});
|
|
10729
|
-
},
|
|
10730
|
-
decodeStream: (r) => {
|
|
10731
|
-
const res = {};
|
|
10732
|
-
r.pushObj(res, (fieldFn) => {
|
|
10733
|
-
for (const name in fields)
|
|
10734
|
-
fieldFn(name, () => (res[name] = fields[name].decodeStream(r)));
|
|
10735
|
-
});
|
|
10736
|
-
return res;
|
|
10737
|
-
},
|
|
10738
|
-
validate: (value) => {
|
|
10739
|
-
if (typeof value !== 'object' || value === null)
|
|
10740
|
-
throw new Error(`struct: invalid value ${value}`);
|
|
10741
|
-
return value;
|
|
10742
|
-
},
|
|
10743
|
-
});
|
|
10744
|
-
}
|
|
10745
|
-
/**
|
|
10746
|
-
* Tuple (unnamed structure) of CoderTypes. Same as struct but with unnamed fields.
|
|
10747
|
-
* @param fields - Array of CoderTypes.
|
|
10748
|
-
* @returns CoderType representing the tuple.
|
|
10749
|
-
* @example
|
|
10750
|
-
* const myTuple = P.tuple([P.U8, P.U16LE, P.string(P.U8)]);
|
|
10751
|
-
*/
|
|
10752
|
-
function tuple(fields) {
|
|
10753
|
-
if (!Array.isArray(fields))
|
|
10754
|
-
throw new Error(`Packed.Tuple: got ${typeof fields} instead of array`);
|
|
10755
|
-
for (let i = 0; i < fields.length; i++) {
|
|
10756
|
-
if (!isCoder(fields[i]))
|
|
10757
|
-
throw new Error(`tuple: field ${i} is not CoderType`);
|
|
10758
|
-
}
|
|
10759
|
-
return wrap({
|
|
10760
|
-
size: sizeof(fields),
|
|
10761
|
-
encodeStream: (w, value) => {
|
|
10762
|
-
// TODO: fix types
|
|
10763
|
-
if (!Array.isArray(value))
|
|
10764
|
-
throw w.err(`tuple: invalid value ${value}`);
|
|
10765
|
-
w.pushObj(value, (fieldFn) => {
|
|
10766
|
-
for (let i = 0; i < fields.length; i++)
|
|
10767
|
-
fieldFn(`${i}`, () => fields[i].encodeStream(w, value[i]));
|
|
10768
|
-
});
|
|
10769
|
-
},
|
|
10770
|
-
decodeStream: (r) => {
|
|
10771
|
-
const res = [];
|
|
10772
|
-
r.pushObj(res, (fieldFn) => {
|
|
10773
|
-
for (let i = 0; i < fields.length; i++)
|
|
10774
|
-
fieldFn(`${i}`, () => res.push(fields[i].decodeStream(r)));
|
|
10775
|
-
});
|
|
10776
|
-
return res;
|
|
10777
|
-
},
|
|
10778
|
-
validate: (value) => {
|
|
10779
|
-
if (!Array.isArray(value))
|
|
10780
|
-
throw new Error(`tuple: invalid value ${value}`);
|
|
10781
|
-
if (value.length !== fields.length)
|
|
10782
|
-
throw new Error(`tuple: wrong length=${value.length}, expected ${fields.length}`);
|
|
10783
|
-
return value;
|
|
10784
|
-
},
|
|
10785
|
-
});
|
|
10786
|
-
}
|
|
10787
|
-
/**
|
|
10788
|
-
* Array of items (inner type) with a specified length.
|
|
10789
|
-
* @param len - Length CoderType (dynamic size), number (fixed size), Uint8Array (for terminator), or null (will parse until end of buffer)
|
|
10790
|
-
* @param inner - CoderType for encoding/decoding each array item.
|
|
10791
|
-
* @returns CoderType representing the array.
|
|
10792
|
-
* @example
|
|
10793
|
-
* const a1 = P.array(P.U16BE, child); // Dynamic size array (prefixed with P.U16BE number of array length)
|
|
10794
|
-
* const a2 = P.array(4, child); // Fixed size array
|
|
10795
|
-
* const a3 = P.array(null, child); // Unknown size array, will parse until end of buffer
|
|
10796
|
-
* const a4 = P.array(new Uint8Array([0]), child); // zero-terminated array (NOTE: terminator can be any buffer)
|
|
10797
|
-
*/
|
|
10798
|
-
function array(len, inner) {
|
|
10799
|
-
if (!isCoder(inner))
|
|
10800
|
-
throw new Error(`array: invalid inner value ${inner}`);
|
|
10801
|
-
// By construction length is inside array (otherwise there will be various incorrect stack states)
|
|
10802
|
-
// But forcing users always write '..' seems like bad idea. Also, breaking change.
|
|
10803
|
-
const _length = lengthCoder(typeof len === 'string' ? `../${len}` : len);
|
|
10804
|
-
return wrap({
|
|
10805
|
-
size: typeof len === 'number' && inner.size ? len * inner.size : undefined,
|
|
10806
|
-
encodeStream: (w, value) => {
|
|
10807
|
-
const _w = w;
|
|
10808
|
-
_w.pushObj(value, (fieldFn) => {
|
|
10809
|
-
if (!isBytes$1(len))
|
|
10810
|
-
_length.encodeStream(w, value.length);
|
|
10811
|
-
for (let i = 0; i < value.length; i++) {
|
|
10812
|
-
fieldFn(`${i}`, () => {
|
|
10813
|
-
const elm = value[i];
|
|
10814
|
-
const startPos = w.pos;
|
|
10815
|
-
inner.encodeStream(w, elm);
|
|
10816
|
-
if (isBytes$1(len)) {
|
|
10817
|
-
// Terminator is bigger than elm size, so skip
|
|
10818
|
-
if (len.length > _w.pos - startPos)
|
|
10819
|
-
return;
|
|
10820
|
-
const data = _w.finish(false).subarray(startPos, _w.pos);
|
|
10821
|
-
// There is still possible case when multiple elements create terminator,
|
|
10822
|
-
// but it is hard to catch here, will be very slow
|
|
10823
|
-
if (equalBytes$1(data.subarray(0, len.length), len))
|
|
10824
|
-
throw _w.err(`array: inner element encoding same as separator. elm=${elm} data=${data}`);
|
|
10825
|
-
}
|
|
10826
|
-
});
|
|
10827
|
-
}
|
|
10828
|
-
});
|
|
10829
|
-
if (isBytes$1(len))
|
|
10830
|
-
w.bytes(len);
|
|
10831
|
-
},
|
|
10832
|
-
decodeStream: (r) => {
|
|
10833
|
-
const res = [];
|
|
10834
|
-
r.pushObj(res, (fieldFn) => {
|
|
10835
|
-
if (len === null) {
|
|
10836
|
-
for (let i = 0; !r.isEnd(); i++) {
|
|
10837
|
-
fieldFn(`${i}`, () => res.push(inner.decodeStream(r)));
|
|
10838
|
-
if (inner.size && r.leftBytes < inner.size)
|
|
10839
|
-
break;
|
|
10840
|
-
}
|
|
10841
|
-
}
|
|
10842
|
-
else if (isBytes$1(len)) {
|
|
10843
|
-
for (let i = 0;; i++) {
|
|
10844
|
-
if (equalBytes$1(r.bytes(len.length, true), len)) {
|
|
10845
|
-
// Advance cursor position if terminator found
|
|
10846
|
-
r.bytes(len.length);
|
|
10847
|
-
break;
|
|
10848
|
-
}
|
|
10849
|
-
fieldFn(`${i}`, () => res.push(inner.decodeStream(r)));
|
|
10850
|
-
}
|
|
10851
|
-
}
|
|
10852
|
-
else {
|
|
10853
|
-
let length;
|
|
10854
|
-
fieldFn('arrayLen', () => (length = _length.decodeStream(r)));
|
|
10855
|
-
for (let i = 0; i < length; i++)
|
|
10856
|
-
fieldFn(`${i}`, () => res.push(inner.decodeStream(r)));
|
|
10857
|
-
}
|
|
10858
|
-
});
|
|
10859
|
-
return res;
|
|
10860
|
-
},
|
|
10861
|
-
validate: (value) => {
|
|
10862
|
-
if (!Array.isArray(value))
|
|
10863
|
-
throw new Error(`array: invalid value ${value}`);
|
|
10864
|
-
return value;
|
|
10865
|
-
},
|
|
10866
|
-
});
|
|
10867
|
-
}
|
|
10868
|
-
|
|
10869
|
-
const Point = secp256k1.ProjectivePoint;
|
|
10870
|
-
const CURVE_ORDER = secp256k1.CURVE.n;
|
|
10871
|
-
const isBytes = utils.isBytes;
|
|
10872
|
-
const concatBytes = utils.concatBytes;
|
|
10873
|
-
const equalBytes = utils.equalBytes;
|
|
10874
|
-
const hash160 = (msg) => ripemd160(sha256$1(msg));
|
|
10875
|
-
const sha256x2 = (...msgs) => sha256$1(sha256$1(concatBytes(...msgs)));
|
|
10876
|
-
const pubSchnorr = schnorr.getPublicKey;
|
|
10877
|
-
const pubECDSA = secp256k1.getPublicKey;
|
|
10878
|
-
// low-r signature grinding. Used to reduce tx size by 1 byte.
|
|
10879
|
-
// noble/secp256k1 does not support the feature: it is not used outside of BTC.
|
|
10880
|
-
// We implement it manually, because in BTC it's common.
|
|
10881
|
-
// Not best way, but closest to bitcoin implementation (easier to check)
|
|
10882
|
-
const hasLowR = (sig) => sig.r < CURVE_ORDER / 2n;
|
|
10883
|
-
function signECDSA(hash, privateKey, lowR = false) {
|
|
10884
|
-
let sig = secp256k1.sign(hash, privateKey);
|
|
10885
|
-
if (lowR && !hasLowR(sig)) {
|
|
10886
|
-
const extraEntropy = new Uint8Array(32);
|
|
10887
|
-
let counter = 0;
|
|
10888
|
-
while (!hasLowR(sig)) {
|
|
10889
|
-
extraEntropy.set(U32LE.encode(counter++));
|
|
10890
|
-
sig = secp256k1.sign(hash, privateKey, { extraEntropy });
|
|
10891
|
-
if (counter > 4294967295)
|
|
10892
|
-
throw new Error('lowR counter overflow: report the error');
|
|
10893
|
-
}
|
|
10894
|
-
}
|
|
10895
|
-
return sig.toDERRawBytes();
|
|
10896
|
-
}
|
|
10897
|
-
const signSchnorr = schnorr.sign;
|
|
10898
|
-
const tagSchnorr = schnorr.utils.taggedHash;
|
|
10899
|
-
var PubT;
|
|
10900
|
-
(function (PubT) {
|
|
10901
|
-
PubT[PubT["ecdsa"] = 0] = "ecdsa";
|
|
10902
|
-
PubT[PubT["schnorr"] = 1] = "schnorr";
|
|
10903
|
-
})(PubT || (PubT = {}));
|
|
10904
|
-
function validatePubkey(pub, type) {
|
|
10905
|
-
const len = pub.length;
|
|
10906
|
-
if (type === PubT.ecdsa) {
|
|
10907
|
-
if (len === 32)
|
|
10908
|
-
throw new Error('Expected non-Schnorr key');
|
|
10909
|
-
Point.fromHex(pub); // does assertValidity
|
|
10910
|
-
return pub;
|
|
10911
|
-
}
|
|
10912
|
-
else if (type === PubT.schnorr) {
|
|
10913
|
-
if (len !== 32)
|
|
10914
|
-
throw new Error('Expected 32-byte Schnorr key');
|
|
10915
|
-
schnorr.utils.lift_x(schnorr.utils.bytesToNumberBE(pub));
|
|
10916
|
-
return pub;
|
|
10917
|
-
}
|
|
10918
|
-
else {
|
|
10919
|
-
throw new Error('Unknown key type');
|
|
10920
|
-
}
|
|
10921
|
-
}
|
|
10922
|
-
function tapTweak(a, b) {
|
|
10923
|
-
const u = schnorr.utils;
|
|
10924
|
-
const t = u.taggedHash('TapTweak', a, b);
|
|
10925
|
-
const tn = u.bytesToNumberBE(t);
|
|
10926
|
-
if (tn >= CURVE_ORDER)
|
|
10927
|
-
throw new Error('tweak higher than curve order');
|
|
10928
|
-
return tn;
|
|
10929
|
-
}
|
|
10930
|
-
function taprootTweakPrivKey(privKey, merkleRoot = Uint8Array.of()) {
|
|
10931
|
-
const u = schnorr.utils;
|
|
10932
|
-
const seckey0 = u.bytesToNumberBE(privKey); // seckey0 = int_from_bytes(seckey0)
|
|
10933
|
-
const P = Point.fromPrivateKey(seckey0); // P = point_mul(G, seckey0)
|
|
10934
|
-
// seckey = seckey0 if has_even_y(P) else SECP256K1_ORDER - seckey0
|
|
10935
|
-
const seckey = P.hasEvenY() ? seckey0 : u.mod(-seckey0, CURVE_ORDER);
|
|
10936
|
-
const xP = u.pointToBytes(P);
|
|
10937
|
-
// t = int_from_bytes(tagged_hash("TapTweak", bytes_from_int(x(P)) + h)); >= SECP256K1_ORDER check
|
|
10938
|
-
const t = tapTweak(xP, merkleRoot);
|
|
10939
|
-
// bytes_from_int((seckey + t) % SECP256K1_ORDER)
|
|
10940
|
-
return u.numberToBytesBE(u.mod(seckey + t, CURVE_ORDER), 32);
|
|
10941
|
-
}
|
|
10942
|
-
function taprootTweakPubkey(pubKey, h) {
|
|
10943
|
-
const u = schnorr.utils;
|
|
10944
|
-
const t = tapTweak(pubKey, h); // t = int_from_bytes(tagged_hash("TapTweak", pubkey + h))
|
|
10945
|
-
const P = u.lift_x(u.bytesToNumberBE(pubKey)); // P = lift_x(int_from_bytes(pubkey))
|
|
10946
|
-
const Q = P.add(Point.fromPrivateKey(t)); // Q = point_add(P, point_mul(G, t))
|
|
10947
|
-
const parity = Q.hasEvenY() ? 0 : 1; // 0 if has_even_y(Q) else 1
|
|
10948
|
-
return [u.pointToBytes(Q), parity]; // bytes_from_int(x(Q))
|
|
10949
|
-
}
|
|
10950
|
-
// Another stupid decision, where lack of standard affects security.
|
|
10951
|
-
// Multisig needs to be generated with some key.
|
|
10952
|
-
// We are using approach from BIP 341/bitcoinjs-lib: SHA256(uncompressedDER(SECP256K1_GENERATOR_POINT))
|
|
10953
|
-
// It is possible to switch SECP256K1_GENERATOR_POINT with some random point;
|
|
10954
|
-
// but it's too complex to prove.
|
|
10955
|
-
// Also used by bitcoin-core and bitcoinjs-lib
|
|
10956
|
-
sha256$1(Point.BASE.toRawBytes(false));
|
|
10957
|
-
const NETWORK = {
|
|
10958
|
-
bech32: 'bc',
|
|
10959
|
-
pubKeyHash: 0x00,
|
|
10960
|
-
scriptHash: 0x05,
|
|
10961
|
-
wif: 0x80,
|
|
10962
|
-
};
|
|
10963
|
-
// Exported for tests, internal method
|
|
10964
|
-
function compareBytes(a, b) {
|
|
10965
|
-
if (!isBytes(a) || !isBytes(b))
|
|
10966
|
-
throw new Error(`cmp: wrong type a=${typeof a} b=${typeof b}`);
|
|
10967
|
-
// -1 -> a<b, 0 -> a==b, 1 -> a>b
|
|
10968
|
-
const len = Math.min(a.length, b.length);
|
|
10969
|
-
for (let i = 0; i < len; i++)
|
|
10970
|
-
if (a[i] != b[i])
|
|
10971
|
-
return Math.sign(a[i] - b[i]);
|
|
10972
|
-
return Math.sign(a.length - b.length);
|
|
10973
|
-
}
|
|
10974
|
-
|
|
10975
|
-
// prettier-ignore
|
|
10976
|
-
var OP;
|
|
10977
|
-
(function (OP) {
|
|
10978
|
-
OP[OP["OP_0"] = 0] = "OP_0";
|
|
10979
|
-
OP[OP["PUSHDATA1"] = 76] = "PUSHDATA1";
|
|
10980
|
-
OP[OP["PUSHDATA2"] = 77] = "PUSHDATA2";
|
|
10981
|
-
OP[OP["PUSHDATA4"] = 78] = "PUSHDATA4";
|
|
10982
|
-
OP[OP["1NEGATE"] = 79] = "1NEGATE";
|
|
10983
|
-
OP[OP["RESERVED"] = 80] = "RESERVED";
|
|
10984
|
-
OP[OP["OP_1"] = 81] = "OP_1";
|
|
10985
|
-
OP[OP["OP_2"] = 82] = "OP_2";
|
|
10986
|
-
OP[OP["OP_3"] = 83] = "OP_3";
|
|
10987
|
-
OP[OP["OP_4"] = 84] = "OP_4";
|
|
10988
|
-
OP[OP["OP_5"] = 85] = "OP_5";
|
|
10989
|
-
OP[OP["OP_6"] = 86] = "OP_6";
|
|
10990
|
-
OP[OP["OP_7"] = 87] = "OP_7";
|
|
10991
|
-
OP[OP["OP_8"] = 88] = "OP_8";
|
|
10992
|
-
OP[OP["OP_9"] = 89] = "OP_9";
|
|
10993
|
-
OP[OP["OP_10"] = 90] = "OP_10";
|
|
10994
|
-
OP[OP["OP_11"] = 91] = "OP_11";
|
|
10995
|
-
OP[OP["OP_12"] = 92] = "OP_12";
|
|
10996
|
-
OP[OP["OP_13"] = 93] = "OP_13";
|
|
10997
|
-
OP[OP["OP_14"] = 94] = "OP_14";
|
|
10998
|
-
OP[OP["OP_15"] = 95] = "OP_15";
|
|
10999
|
-
OP[OP["OP_16"] = 96] = "OP_16";
|
|
11000
|
-
// Control
|
|
11001
|
-
OP[OP["NOP"] = 97] = "NOP";
|
|
11002
|
-
OP[OP["VER"] = 98] = "VER";
|
|
11003
|
-
OP[OP["IF"] = 99] = "IF";
|
|
11004
|
-
OP[OP["NOTIF"] = 100] = "NOTIF";
|
|
11005
|
-
OP[OP["VERIF"] = 101] = "VERIF";
|
|
11006
|
-
OP[OP["VERNOTIF"] = 102] = "VERNOTIF";
|
|
11007
|
-
OP[OP["ELSE"] = 103] = "ELSE";
|
|
11008
|
-
OP[OP["ENDIF"] = 104] = "ENDIF";
|
|
11009
|
-
OP[OP["VERIFY"] = 105] = "VERIFY";
|
|
11010
|
-
OP[OP["RETURN"] = 106] = "RETURN";
|
|
11011
|
-
// Stack
|
|
11012
|
-
OP[OP["TOALTSTACK"] = 107] = "TOALTSTACK";
|
|
11013
|
-
OP[OP["FROMALTSTACK"] = 108] = "FROMALTSTACK";
|
|
11014
|
-
OP[OP["2DROP"] = 109] = "2DROP";
|
|
11015
|
-
OP[OP["2DUP"] = 110] = "2DUP";
|
|
11016
|
-
OP[OP["3DUP"] = 111] = "3DUP";
|
|
11017
|
-
OP[OP["2OVER"] = 112] = "2OVER";
|
|
11018
|
-
OP[OP["2ROT"] = 113] = "2ROT";
|
|
11019
|
-
OP[OP["2SWAP"] = 114] = "2SWAP";
|
|
11020
|
-
OP[OP["IFDUP"] = 115] = "IFDUP";
|
|
11021
|
-
OP[OP["DEPTH"] = 116] = "DEPTH";
|
|
11022
|
-
OP[OP["DROP"] = 117] = "DROP";
|
|
11023
|
-
OP[OP["DUP"] = 118] = "DUP";
|
|
11024
|
-
OP[OP["NIP"] = 119] = "NIP";
|
|
11025
|
-
OP[OP["OVER"] = 120] = "OVER";
|
|
11026
|
-
OP[OP["PICK"] = 121] = "PICK";
|
|
11027
|
-
OP[OP["ROLL"] = 122] = "ROLL";
|
|
11028
|
-
OP[OP["ROT"] = 123] = "ROT";
|
|
11029
|
-
OP[OP["SWAP"] = 124] = "SWAP";
|
|
11030
|
-
OP[OP["TUCK"] = 125] = "TUCK";
|
|
11031
|
-
// Splice
|
|
11032
|
-
OP[OP["CAT"] = 126] = "CAT";
|
|
11033
|
-
OP[OP["SUBSTR"] = 127] = "SUBSTR";
|
|
11034
|
-
OP[OP["LEFT"] = 128] = "LEFT";
|
|
11035
|
-
OP[OP["RIGHT"] = 129] = "RIGHT";
|
|
11036
|
-
OP[OP["SIZE"] = 130] = "SIZE";
|
|
11037
|
-
// Boolean logic
|
|
11038
|
-
OP[OP["INVERT"] = 131] = "INVERT";
|
|
11039
|
-
OP[OP["AND"] = 132] = "AND";
|
|
11040
|
-
OP[OP["OR"] = 133] = "OR";
|
|
11041
|
-
OP[OP["XOR"] = 134] = "XOR";
|
|
11042
|
-
OP[OP["EQUAL"] = 135] = "EQUAL";
|
|
11043
|
-
OP[OP["EQUALVERIFY"] = 136] = "EQUALVERIFY";
|
|
11044
|
-
OP[OP["RESERVED1"] = 137] = "RESERVED1";
|
|
11045
|
-
OP[OP["RESERVED2"] = 138] = "RESERVED2";
|
|
11046
|
-
// Numbers
|
|
11047
|
-
OP[OP["1ADD"] = 139] = "1ADD";
|
|
11048
|
-
OP[OP["1SUB"] = 140] = "1SUB";
|
|
11049
|
-
OP[OP["2MUL"] = 141] = "2MUL";
|
|
11050
|
-
OP[OP["2DIV"] = 142] = "2DIV";
|
|
11051
|
-
OP[OP["NEGATE"] = 143] = "NEGATE";
|
|
11052
|
-
OP[OP["ABS"] = 144] = "ABS";
|
|
11053
|
-
OP[OP["NOT"] = 145] = "NOT";
|
|
11054
|
-
OP[OP["0NOTEQUAL"] = 146] = "0NOTEQUAL";
|
|
11055
|
-
OP[OP["ADD"] = 147] = "ADD";
|
|
11056
|
-
OP[OP["SUB"] = 148] = "SUB";
|
|
11057
|
-
OP[OP["MUL"] = 149] = "MUL";
|
|
11058
|
-
OP[OP["DIV"] = 150] = "DIV";
|
|
11059
|
-
OP[OP["MOD"] = 151] = "MOD";
|
|
11060
|
-
OP[OP["LSHIFT"] = 152] = "LSHIFT";
|
|
11061
|
-
OP[OP["RSHIFT"] = 153] = "RSHIFT";
|
|
11062
|
-
OP[OP["BOOLAND"] = 154] = "BOOLAND";
|
|
11063
|
-
OP[OP["BOOLOR"] = 155] = "BOOLOR";
|
|
11064
|
-
OP[OP["NUMEQUAL"] = 156] = "NUMEQUAL";
|
|
11065
|
-
OP[OP["NUMEQUALVERIFY"] = 157] = "NUMEQUALVERIFY";
|
|
11066
|
-
OP[OP["NUMNOTEQUAL"] = 158] = "NUMNOTEQUAL";
|
|
11067
|
-
OP[OP["LESSTHAN"] = 159] = "LESSTHAN";
|
|
11068
|
-
OP[OP["GREATERTHAN"] = 160] = "GREATERTHAN";
|
|
11069
|
-
OP[OP["LESSTHANOREQUAL"] = 161] = "LESSTHANOREQUAL";
|
|
11070
|
-
OP[OP["GREATERTHANOREQUAL"] = 162] = "GREATERTHANOREQUAL";
|
|
11071
|
-
OP[OP["MIN"] = 163] = "MIN";
|
|
11072
|
-
OP[OP["MAX"] = 164] = "MAX";
|
|
11073
|
-
OP[OP["WITHIN"] = 165] = "WITHIN";
|
|
11074
|
-
// Crypto
|
|
11075
|
-
OP[OP["RIPEMD160"] = 166] = "RIPEMD160";
|
|
11076
|
-
OP[OP["SHA1"] = 167] = "SHA1";
|
|
11077
|
-
OP[OP["SHA256"] = 168] = "SHA256";
|
|
11078
|
-
OP[OP["HASH160"] = 169] = "HASH160";
|
|
11079
|
-
OP[OP["HASH256"] = 170] = "HASH256";
|
|
11080
|
-
OP[OP["CODESEPARATOR"] = 171] = "CODESEPARATOR";
|
|
11081
|
-
OP[OP["CHECKSIG"] = 172] = "CHECKSIG";
|
|
11082
|
-
OP[OP["CHECKSIGVERIFY"] = 173] = "CHECKSIGVERIFY";
|
|
11083
|
-
OP[OP["CHECKMULTISIG"] = 174] = "CHECKMULTISIG";
|
|
11084
|
-
OP[OP["CHECKMULTISIGVERIFY"] = 175] = "CHECKMULTISIGVERIFY";
|
|
11085
|
-
// Expansion
|
|
11086
|
-
OP[OP["NOP1"] = 176] = "NOP1";
|
|
11087
|
-
OP[OP["CHECKLOCKTIMEVERIFY"] = 177] = "CHECKLOCKTIMEVERIFY";
|
|
11088
|
-
OP[OP["CHECKSEQUENCEVERIFY"] = 178] = "CHECKSEQUENCEVERIFY";
|
|
11089
|
-
OP[OP["NOP4"] = 179] = "NOP4";
|
|
11090
|
-
OP[OP["NOP5"] = 180] = "NOP5";
|
|
11091
|
-
OP[OP["NOP6"] = 181] = "NOP6";
|
|
11092
|
-
OP[OP["NOP7"] = 182] = "NOP7";
|
|
11093
|
-
OP[OP["NOP8"] = 183] = "NOP8";
|
|
11094
|
-
OP[OP["NOP9"] = 184] = "NOP9";
|
|
11095
|
-
OP[OP["NOP10"] = 185] = "NOP10";
|
|
11096
|
-
// BIP 342
|
|
11097
|
-
OP[OP["CHECKSIGADD"] = 186] = "CHECKSIGADD";
|
|
11098
|
-
// Invalid
|
|
11099
|
-
OP[OP["INVALID"] = 255] = "INVALID";
|
|
11100
|
-
})(OP || (OP = {}));
|
|
11101
|
-
// We can encode almost any number as ScriptNum, however, parsing will be a problem
|
|
11102
|
-
// since we can't know if buffer is a number or something else.
|
|
11103
|
-
function ScriptNum(bytesLimit = 6, forceMinimal = false) {
|
|
11104
|
-
return wrap({
|
|
11105
|
-
encodeStream: (w, value) => {
|
|
11106
|
-
if (value === 0n)
|
|
11107
|
-
return;
|
|
11108
|
-
const neg = value < 0;
|
|
11109
|
-
const val = BigInt(value);
|
|
11110
|
-
const nums = [];
|
|
11111
|
-
for (let abs = neg ? -val : val; abs; abs >>= 8n)
|
|
11112
|
-
nums.push(Number(abs & 0xffn));
|
|
11113
|
-
if (nums[nums.length - 1] >= 0x80)
|
|
11114
|
-
nums.push(neg ? 0x80 : 0);
|
|
11115
|
-
else if (neg)
|
|
11116
|
-
nums[nums.length - 1] |= 0x80;
|
|
11117
|
-
w.bytes(new Uint8Array(nums));
|
|
11118
|
-
},
|
|
11119
|
-
decodeStream: (r) => {
|
|
11120
|
-
const len = r.leftBytes;
|
|
11121
|
-
if (len > bytesLimit)
|
|
11122
|
-
throw new Error(`ScriptNum: number (${len}) bigger than limit=${bytesLimit}`);
|
|
11123
|
-
if (len === 0)
|
|
11124
|
-
return 0n;
|
|
11125
|
-
if (forceMinimal) {
|
|
11126
|
-
const data = r.bytes(len, true);
|
|
11127
|
-
// MSB is zero (without sign bit) -> not minimally encoded
|
|
11128
|
-
if ((data[data.length - 1] & 0x7f) === 0) {
|
|
11129
|
-
// exception
|
|
11130
|
-
if (len <= 1 || (data[data.length - 2] & 0x80) === 0)
|
|
11131
|
-
throw new Error('Non-minimally encoded ScriptNum');
|
|
11132
|
-
}
|
|
11133
|
-
}
|
|
11134
|
-
let last = 0;
|
|
11135
|
-
let res = 0n;
|
|
11136
|
-
for (let i = 0; i < len; ++i) {
|
|
11137
|
-
last = r.byte();
|
|
11138
|
-
res |= BigInt(last) << (8n * BigInt(i));
|
|
11139
|
-
}
|
|
11140
|
-
if (last >= 0x80) {
|
|
11141
|
-
res &= (2n ** BigInt(len * 8) - 1n) >> 1n;
|
|
11142
|
-
res = -res;
|
|
11143
|
-
}
|
|
11144
|
-
return res;
|
|
11145
|
-
},
|
|
11146
|
-
});
|
|
11147
|
-
}
|
|
11148
|
-
function OpToNum(op, bytesLimit = 4, forceMinimal = true) {
|
|
11149
|
-
if (typeof op === 'number')
|
|
11150
|
-
return op;
|
|
11151
|
-
if (isBytes(op)) {
|
|
11152
|
-
try {
|
|
11153
|
-
const val = ScriptNum(bytesLimit, forceMinimal).decode(op);
|
|
11154
|
-
if (val > Number.MAX_SAFE_INTEGER)
|
|
11155
|
-
return;
|
|
11156
|
-
return Number(val);
|
|
11157
|
-
}
|
|
11158
|
-
catch (e) {
|
|
11159
|
-
return;
|
|
11160
|
-
}
|
|
11161
|
-
}
|
|
11162
|
-
return;
|
|
11163
|
-
}
|
|
11164
|
-
// Converts script bytes to parsed script
|
|
11165
|
-
// 5221030000000000000000000000000000000000000000000000000000000000000001210300000000000000000000000000000000000000000000000000000000000000022103000000000000000000000000000000000000000000000000000000000000000353ae
|
|
11166
|
-
// =>
|
|
11167
|
-
// OP_2
|
|
11168
|
-
// 030000000000000000000000000000000000000000000000000000000000000001
|
|
11169
|
-
// 030000000000000000000000000000000000000000000000000000000000000002
|
|
11170
|
-
// 030000000000000000000000000000000000000000000000000000000000000003
|
|
11171
|
-
// OP_3
|
|
11172
|
-
// CHECKMULTISIG
|
|
11173
|
-
const Script = wrap({
|
|
11174
|
-
encodeStream: (w, value) => {
|
|
11175
|
-
for (let o of value) {
|
|
11176
|
-
if (typeof o === 'string') {
|
|
11177
|
-
if (OP[o] === undefined)
|
|
11178
|
-
throw new Error(`Unknown opcode=${o}`);
|
|
11179
|
-
w.byte(OP[o]);
|
|
11180
|
-
continue;
|
|
11181
|
-
}
|
|
11182
|
-
else if (typeof o === 'number') {
|
|
11183
|
-
if (o === 0x00) {
|
|
11184
|
-
w.byte(0x00);
|
|
11185
|
-
continue;
|
|
11186
|
-
}
|
|
11187
|
-
else if (1 <= o && o <= 16) {
|
|
11188
|
-
w.byte(OP.OP_1 - 1 + o);
|
|
11189
|
-
continue;
|
|
11190
|
-
}
|
|
11191
|
-
}
|
|
11192
|
-
// Encode big numbers
|
|
11193
|
-
if (typeof o === 'number')
|
|
11194
|
-
o = ScriptNum().encode(BigInt(o));
|
|
11195
|
-
if (!isBytes(o))
|
|
11196
|
-
throw new Error(`Wrong Script OP=${o} (${typeof o})`);
|
|
11197
|
-
// Bytes
|
|
11198
|
-
const len = o.length;
|
|
11199
|
-
if (len < OP.PUSHDATA1)
|
|
11200
|
-
w.byte(len);
|
|
11201
|
-
else if (len <= 0xff) {
|
|
11202
|
-
w.byte(OP.PUSHDATA1);
|
|
11203
|
-
w.byte(len);
|
|
11204
|
-
}
|
|
11205
|
-
else if (len <= 0xffff) {
|
|
11206
|
-
w.byte(OP.PUSHDATA2);
|
|
11207
|
-
w.bytes(U16LE.encode(len));
|
|
11208
|
-
}
|
|
11209
|
-
else {
|
|
11210
|
-
w.byte(OP.PUSHDATA4);
|
|
11211
|
-
w.bytes(U32LE.encode(len));
|
|
11212
|
-
}
|
|
11213
|
-
w.bytes(o);
|
|
11214
|
-
}
|
|
11215
|
-
},
|
|
11216
|
-
decodeStream: (r) => {
|
|
11217
|
-
const out = [];
|
|
11218
|
-
while (!r.isEnd()) {
|
|
11219
|
-
const cur = r.byte();
|
|
11220
|
-
// if 0 < cur < 78
|
|
11221
|
-
if (OP.OP_0 < cur && cur <= OP.PUSHDATA4) {
|
|
11222
|
-
let len;
|
|
11223
|
-
if (cur < OP.PUSHDATA1)
|
|
11224
|
-
len = cur;
|
|
11225
|
-
else if (cur === OP.PUSHDATA1)
|
|
11226
|
-
len = U8.decodeStream(r);
|
|
11227
|
-
else if (cur === OP.PUSHDATA2)
|
|
11228
|
-
len = U16LE.decodeStream(r);
|
|
11229
|
-
else if (cur === OP.PUSHDATA4)
|
|
11230
|
-
len = U32LE.decodeStream(r);
|
|
11231
|
-
else
|
|
11232
|
-
throw new Error('Should be not possible');
|
|
11233
|
-
out.push(r.bytes(len));
|
|
11234
|
-
}
|
|
11235
|
-
else if (cur === 0x00) {
|
|
11236
|
-
out.push(0);
|
|
11237
|
-
}
|
|
11238
|
-
else if (OP.OP_1 <= cur && cur <= OP.OP_16) {
|
|
11239
|
-
out.push(cur - (OP.OP_1 - 1));
|
|
11240
|
-
}
|
|
11241
|
-
else {
|
|
11242
|
-
const op = OP[cur];
|
|
11243
|
-
if (op === undefined)
|
|
11244
|
-
throw new Error(`Unknown opcode=${cur.toString(16)}`);
|
|
11245
|
-
out.push(op);
|
|
11246
|
-
}
|
|
11247
|
-
}
|
|
11248
|
-
return out;
|
|
11249
|
-
},
|
|
11250
|
-
});
|
|
11251
|
-
// BTC specific variable length integer encoding
|
|
11252
|
-
// https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer
|
|
11253
|
-
const CSLimits = {
|
|
11254
|
-
0xfd: [0xfd, 2, 253n, 65535n],
|
|
11255
|
-
0xfe: [0xfe, 4, 65536n, 4294967295n],
|
|
11256
|
-
0xff: [0xff, 8, 4294967296n, 18446744073709551615n],
|
|
11257
|
-
};
|
|
11258
|
-
const CompactSize = wrap({
|
|
11259
|
-
encodeStream: (w, value) => {
|
|
11260
|
-
if (typeof value === 'number')
|
|
11261
|
-
value = BigInt(value);
|
|
11262
|
-
if (0n <= value && value <= 252n)
|
|
11263
|
-
return w.byte(Number(value));
|
|
11264
|
-
for (const [flag, bytes, start, stop] of Object.values(CSLimits)) {
|
|
11265
|
-
if (start > value || value > stop)
|
|
11266
|
-
continue;
|
|
11267
|
-
w.byte(flag);
|
|
11268
|
-
for (let i = 0; i < bytes; i++)
|
|
11269
|
-
w.byte(Number((value >> (8n * BigInt(i))) & 0xffn));
|
|
11270
|
-
return;
|
|
11271
|
-
}
|
|
11272
|
-
throw w.err(`VarInt too big: ${value}`);
|
|
11273
|
-
},
|
|
11274
|
-
decodeStream: (r) => {
|
|
11275
|
-
const b0 = r.byte();
|
|
11276
|
-
if (b0 <= 0xfc)
|
|
11277
|
-
return BigInt(b0);
|
|
11278
|
-
const [_, bytes, start] = CSLimits[b0];
|
|
11279
|
-
let num = 0n;
|
|
11280
|
-
for (let i = 0; i < bytes; i++)
|
|
11281
|
-
num |= BigInt(r.byte()) << (8n * BigInt(i));
|
|
11282
|
-
if (num < start)
|
|
11283
|
-
throw r.err(`Wrong CompactSize(${8 * bytes})`);
|
|
11284
|
-
return num;
|
|
11285
|
-
},
|
|
11286
|
-
});
|
|
11287
|
-
// Same thing, but in number instead of bigint. Checks for safe integer inside
|
|
11288
|
-
const CompactSizeLen = apply(CompactSize, coders.numberBigint);
|
|
11289
|
-
// ui8a of size <CompactSize>
|
|
11290
|
-
const VarBytes = createBytes(CompactSize);
|
|
11291
|
-
// SegWit v0 stack of witness buffers
|
|
11292
|
-
const RawWitness = array(CompactSizeLen, VarBytes);
|
|
11293
|
-
// Array of size <CompactSize>
|
|
11294
|
-
const BTCArray = (t) => array(CompactSize, t);
|
|
11295
|
-
const RawInput = struct({
|
|
11296
|
-
txid: createBytes(32, true), // hash(prev_tx),
|
|
11297
|
-
index: U32LE, // output number of previous tx
|
|
11298
|
-
finalScriptSig: VarBytes, // btc merges input and output script, executes it. If ok = tx passes
|
|
11299
|
-
sequence: U32LE, // ?
|
|
11300
|
-
});
|
|
11301
|
-
const RawOutput = struct({ amount: U64LE, script: VarBytes });
|
|
11302
|
-
// https://en.bitcoin.it/wiki/Protocol_documentation#tx
|
|
11303
|
-
const _RawTx = struct({
|
|
11304
|
-
version: I32LE,
|
|
11305
|
-
segwitFlag: flag(new Uint8Array([0x00, 0x01])),
|
|
11306
|
-
inputs: BTCArray(RawInput),
|
|
11307
|
-
outputs: BTCArray(RawOutput),
|
|
11308
|
-
witnesses: flagged('segwitFlag', array('inputs/length', RawWitness)),
|
|
11309
|
-
// < 500000000 Block number at which this transaction is unlocked
|
|
11310
|
-
// >= 500000000 UNIX timestamp at which this transaction is unlocked
|
|
11311
|
-
// Handled as part of PSBTv2
|
|
11312
|
-
lockTime: U32LE,
|
|
11313
|
-
});
|
|
11314
|
-
function validateRawTx(tx) {
|
|
11315
|
-
if (tx.segwitFlag && tx.witnesses && !tx.witnesses.length)
|
|
11316
|
-
throw new Error('Segwit flag with empty witnesses array');
|
|
11317
|
-
return tx;
|
|
11318
|
-
}
|
|
11319
|
-
const RawTx = validate(_RawTx, validateRawTx);
|
|
11320
|
-
// Pre-SegWit serialization format (for PSBTv0)
|
|
11321
|
-
const RawOldTx = struct({
|
|
11322
|
-
version: I32LE,
|
|
11323
|
-
inputs: BTCArray(RawInput),
|
|
11324
|
-
outputs: BTCArray(RawOutput),
|
|
11325
|
-
lockTime: U32LE,
|
|
11326
|
-
});
|
|
11327
|
-
|
|
11328
|
-
// PSBT BIP174, BIP370, BIP371
|
|
11329
|
-
// Can be 33 or 64 bytes
|
|
11330
|
-
const PubKeyECDSA = validate(createBytes(null), (pub) => validatePubkey(pub, PubT.ecdsa));
|
|
11331
|
-
const PubKeySchnorr = validate(createBytes(32), (pub) => validatePubkey(pub, PubT.schnorr));
|
|
11332
|
-
const SignatureSchnorr = validate(createBytes(null), (sig) => {
|
|
11333
|
-
if (sig.length !== 64 && sig.length !== 65)
|
|
11334
|
-
throw new Error('Schnorr signature should be 64 or 65 bytes long');
|
|
11335
|
-
return sig;
|
|
11336
|
-
});
|
|
11337
|
-
const BIP32Der = struct({
|
|
11338
|
-
fingerprint: U32BE,
|
|
11339
|
-
path: array(null, U32LE),
|
|
11340
|
-
});
|
|
11341
|
-
const TaprootBIP32Der = struct({
|
|
11342
|
-
hashes: array(CompactSizeLen, createBytes(32)),
|
|
11343
|
-
der: BIP32Der,
|
|
11344
|
-
});
|
|
11345
|
-
// The 78 byte serialized extended public key as defined by BIP 32.
|
|
11346
|
-
const GlobalXPUB = createBytes(78);
|
|
11347
|
-
const tapScriptSigKey = struct({ pubKey: PubKeySchnorr, leafHash: createBytes(32) });
|
|
11348
|
-
// Complex structure for PSBT fields
|
|
11349
|
-
// <control byte with leaf version and parity bit> <internal key p> <C> <E> <AB>
|
|
11350
|
-
const _TaprootControlBlock = struct({
|
|
11351
|
-
version: U8, // With parity :(
|
|
11352
|
-
internalKey: createBytes(32),
|
|
11353
|
-
merklePath: array(null, createBytes(32)),
|
|
11354
|
-
});
|
|
11355
|
-
const TaprootControlBlock = validate(_TaprootControlBlock, (cb) => {
|
|
11356
|
-
if (cb.merklePath.length > 128)
|
|
11357
|
-
throw new Error('TaprootControlBlock: merklePath should be of length 0..128 (inclusive)');
|
|
11358
|
-
return cb;
|
|
11359
|
-
});
|
|
11360
|
-
// {<8-bit uint depth> <8-bit uint leaf version> <compact size uint scriptlen> <bytes script>}*
|
|
11361
|
-
const tapTree = array(null, struct({
|
|
11362
|
-
depth: U8,
|
|
11363
|
-
version: U8,
|
|
11364
|
-
script: VarBytes,
|
|
11365
|
-
}));
|
|
11366
|
-
const BytesInf = createBytes(null); // Bytes will conflict with Bytes type
|
|
11367
|
-
const Bytes20 = createBytes(20);
|
|
11368
|
-
const Bytes32 = createBytes(32);
|
|
11369
|
-
// versionsRequiringExclusing = !versionsAllowsInclusion (as set)
|
|
11370
|
-
// {name: [tag, keyCoder, valueCoder, versionsRequiringInclusion, versionsRequiringExclusing, versionsAllowsInclusion, silentIgnore]}
|
|
11371
|
-
// SilentIgnore: we use some v2 fields for v1 representation too, so we just clean them before serialize
|
|
11372
|
-
// Tables from BIP-0174 (https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki)
|
|
11373
|
-
// prettier-ignore
|
|
11374
|
-
const PSBTGlobal = {
|
|
11375
|
-
unsignedTx: [0x00, false, RawOldTx, [0], [0], false],
|
|
11376
|
-
xpub: [0x01, GlobalXPUB, BIP32Der, [], [0, 2], false],
|
|
11377
|
-
txVersion: [0x02, false, U32LE, [2], [2], false],
|
|
11378
|
-
fallbackLocktime: [0x03, false, U32LE, [], [2], false],
|
|
11379
|
-
inputCount: [0x04, false, CompactSizeLen, [2], [2], false],
|
|
11380
|
-
outputCount: [0x05, false, CompactSizeLen, [2], [2], false],
|
|
11381
|
-
txModifiable: [0x06, false, U8, [], [2], false], // TODO: bitfield
|
|
11382
|
-
version: [0xfb, false, U32LE, [], [0, 2], false],
|
|
11383
|
-
proprietary: [0xfc, BytesInf, BytesInf, [], [0, 2], false],
|
|
11384
|
-
};
|
|
11385
|
-
// prettier-ignore
|
|
11386
|
-
const PSBTInput = {
|
|
11387
|
-
nonWitnessUtxo: [0x00, false, RawTx, [], [0, 2], false],
|
|
11388
|
-
witnessUtxo: [0x01, false, RawOutput, [], [0, 2], false],
|
|
11389
|
-
partialSig: [0x02, PubKeyECDSA, BytesInf, [], [0, 2], false],
|
|
11390
|
-
sighashType: [0x03, false, U32LE, [], [0, 2], false],
|
|
11391
|
-
redeemScript: [0x04, false, BytesInf, [], [0, 2], false],
|
|
11392
|
-
witnessScript: [0x05, false, BytesInf, [], [0, 2], false],
|
|
11393
|
-
bip32Derivation: [0x06, PubKeyECDSA, BIP32Der, [], [0, 2], false],
|
|
11394
|
-
finalScriptSig: [0x07, false, BytesInf, [], [0, 2], false],
|
|
11395
|
-
finalScriptWitness: [0x08, false, RawWitness, [], [0, 2], false],
|
|
11396
|
-
porCommitment: [0x09, false, BytesInf, [], [0, 2], false],
|
|
11397
|
-
ripemd160: [0x0a, Bytes20, BytesInf, [], [0, 2], false],
|
|
11398
|
-
sha256: [0x0b, Bytes32, BytesInf, [], [0, 2], false],
|
|
11399
|
-
hash160: [0x0c, Bytes20, BytesInf, [], [0, 2], false],
|
|
11400
|
-
hash256: [0x0d, Bytes32, BytesInf, [], [0, 2], false],
|
|
11401
|
-
txid: [0x0e, false, Bytes32, [2], [2], true],
|
|
11402
|
-
index: [0x0f, false, U32LE, [2], [2], true],
|
|
11403
|
-
sequence: [0x10, false, U32LE, [], [2], true],
|
|
11404
|
-
requiredTimeLocktime: [0x11, false, U32LE, [], [2], false],
|
|
11405
|
-
requiredHeightLocktime: [0x12, false, U32LE, [], [2], false],
|
|
11406
|
-
tapKeySig: [0x13, false, SignatureSchnorr, [], [0, 2], false],
|
|
11407
|
-
tapScriptSig: [0x14, tapScriptSigKey, SignatureSchnorr, [], [0, 2], false],
|
|
11408
|
-
tapLeafScript: [0x15, TaprootControlBlock, BytesInf, [], [0, 2], false],
|
|
11409
|
-
tapBip32Derivation: [0x16, Bytes32, TaprootBIP32Der, [], [0, 2], false],
|
|
11410
|
-
tapInternalKey: [0x17, false, PubKeySchnorr, [], [0, 2], false],
|
|
11411
|
-
tapMerkleRoot: [0x18, false, Bytes32, [], [0, 2], false],
|
|
11412
|
-
proprietary: [0xfc, BytesInf, BytesInf, [], [0, 2], false],
|
|
11413
|
-
};
|
|
11414
|
-
// All other keys removed when finalizing
|
|
11415
|
-
const PSBTInputFinalKeys = [
|
|
11416
|
-
'txid',
|
|
11417
|
-
'sequence',
|
|
11418
|
-
'index',
|
|
11419
|
-
'witnessUtxo',
|
|
11420
|
-
'nonWitnessUtxo',
|
|
11421
|
-
'finalScriptSig',
|
|
11422
|
-
'finalScriptWitness',
|
|
11423
|
-
'unknown',
|
|
11424
|
-
];
|
|
11425
|
-
// Can be modified even on signed input
|
|
11426
|
-
const PSBTInputUnsignedKeys = [
|
|
11427
|
-
'partialSig',
|
|
11428
|
-
'finalScriptSig',
|
|
11429
|
-
'finalScriptWitness',
|
|
11430
|
-
'tapKeySig',
|
|
11431
|
-
'tapScriptSig',
|
|
11432
|
-
];
|
|
11433
|
-
// prettier-ignore
|
|
11434
|
-
const PSBTOutput = {
|
|
11435
|
-
redeemScript: [0x00, false, BytesInf, [], [0, 2], false],
|
|
11436
|
-
witnessScript: [0x01, false, BytesInf, [], [0, 2], false],
|
|
11437
|
-
bip32Derivation: [0x02, PubKeyECDSA, BIP32Der, [], [0, 2], false],
|
|
11438
|
-
amount: [0x03, false, I64LE, [2], [2], true],
|
|
11439
|
-
script: [0x04, false, BytesInf, [2], [2], true],
|
|
11440
|
-
tapInternalKey: [0x05, false, PubKeySchnorr, [], [0, 2], false],
|
|
11441
|
-
tapTree: [0x06, false, tapTree, [], [0, 2], false],
|
|
11442
|
-
tapBip32Derivation: [0x07, PubKeySchnorr, TaprootBIP32Der, [], [0, 2], false],
|
|
11443
|
-
proprietary: [0xfc, BytesInf, BytesInf, [], [0, 2], false],
|
|
11444
|
-
};
|
|
11445
|
-
// Can be modified even on signed input
|
|
11446
|
-
const PSBTOutputUnsignedKeys = [];
|
|
11447
|
-
const PSBTKeyPair = array(NULL, struct({
|
|
11448
|
-
// <key> := <keylen> <keytype> <keydata> WHERE keylen = len(keytype)+len(keydata)
|
|
11449
|
-
key: prefix(CompactSizeLen, struct({ type: CompactSizeLen, key: createBytes(null) })),
|
|
11450
|
-
// <value> := <valuelen> <valuedata>
|
|
11451
|
-
value: createBytes(CompactSizeLen),
|
|
11452
|
-
}));
|
|
11453
|
-
function PSBTKeyInfo(info) {
|
|
11454
|
-
const [type, kc, vc, reqInc, allowInc, silentIgnore] = info;
|
|
11455
|
-
return { type, kc, vc, reqInc, allowInc, silentIgnore };
|
|
11456
|
-
}
|
|
11457
|
-
struct({ type: CompactSizeLen, key: createBytes(null) });
|
|
11458
|
-
// Key cannot be 'unknown', value coder cannot be array for elements with empty key
|
|
11459
|
-
function PSBTKeyMap(psbtEnum) {
|
|
11460
|
-
// -> Record<type, [keyName, ...coders]>
|
|
11461
|
-
const byType = {};
|
|
11462
|
-
for (const k in psbtEnum) {
|
|
11463
|
-
const [num, kc, vc] = psbtEnum[k];
|
|
11464
|
-
byType[num] = [k, kc, vc];
|
|
11465
|
-
}
|
|
11466
|
-
return wrap({
|
|
11467
|
-
encodeStream: (w, value) => {
|
|
11468
|
-
let out = [];
|
|
11469
|
-
// Because we use order of psbtEnum, keymap is sorted here
|
|
11470
|
-
for (const name in psbtEnum) {
|
|
11471
|
-
const val = value[name];
|
|
11472
|
-
if (val === undefined)
|
|
11473
|
-
continue;
|
|
11474
|
-
const [type, kc, vc] = psbtEnum[name];
|
|
11475
|
-
if (!kc) {
|
|
11476
|
-
out.push({ key: { type, key: EMPTY }, value: vc.encode(val) });
|
|
11477
|
-
}
|
|
11478
|
-
else {
|
|
11479
|
-
// Low level interface, returns keys as is (with duplicates). Useful for debug
|
|
11480
|
-
const kv = val.map(([k, v]) => [
|
|
11481
|
-
kc.encode(k),
|
|
11482
|
-
vc.encode(v),
|
|
11483
|
-
]);
|
|
11484
|
-
// sort by keys
|
|
11485
|
-
kv.sort((a, b) => compareBytes(a[0], b[0]));
|
|
11486
|
-
for (const [key, value] of kv)
|
|
11487
|
-
out.push({ key: { key, type }, value });
|
|
11488
|
-
}
|
|
11489
|
-
}
|
|
11490
|
-
if (value.unknown) {
|
|
11491
|
-
value.unknown.sort((a, b) => compareBytes(a[0].key, b[0].key));
|
|
11492
|
-
for (const [k, v] of value.unknown)
|
|
11493
|
-
out.push({ key: k, value: v });
|
|
11494
|
-
}
|
|
11495
|
-
PSBTKeyPair.encodeStream(w, out);
|
|
11496
|
-
},
|
|
11497
|
-
decodeStream: (r) => {
|
|
11498
|
-
const raw = PSBTKeyPair.decodeStream(r);
|
|
11499
|
-
const out = {};
|
|
11500
|
-
const noKey = {};
|
|
11501
|
-
for (const elm of raw) {
|
|
11502
|
-
let name = 'unknown';
|
|
11503
|
-
let key = elm.key.key;
|
|
11504
|
-
let value = elm.value;
|
|
11505
|
-
if (byType[elm.key.type]) {
|
|
11506
|
-
const [_name, kc, vc] = byType[elm.key.type];
|
|
11507
|
-
name = _name;
|
|
11508
|
-
if (!kc && key.length) {
|
|
11509
|
-
throw new Error(`PSBT: Non-empty key for ${name} (key=${hex.encode(key)} value=${hex.encode(value)}`);
|
|
11510
|
-
}
|
|
11511
|
-
key = kc ? kc.decode(key) : undefined;
|
|
11512
|
-
value = vc.decode(value);
|
|
11513
|
-
if (!kc) {
|
|
11514
|
-
if (out[name])
|
|
11515
|
-
throw new Error(`PSBT: Same keys: ${name} (key=${key} value=${value})`);
|
|
11516
|
-
out[name] = value;
|
|
11517
|
-
noKey[name] = true;
|
|
11518
|
-
continue;
|
|
11519
|
-
}
|
|
11520
|
-
}
|
|
11521
|
-
else {
|
|
11522
|
-
// For unknown: add key type inside key
|
|
11523
|
-
key = { type: elm.key.type, key: elm.key.key };
|
|
11524
|
-
}
|
|
11525
|
-
// Only keyed elements at this point
|
|
11526
|
-
if (noKey[name])
|
|
11527
|
-
throw new Error(`PSBT: Key type with empty key and no key=${name} val=${value}`);
|
|
11528
|
-
if (!out[name])
|
|
11529
|
-
out[name] = [];
|
|
11530
|
-
out[name].push([key, value]);
|
|
11531
|
-
}
|
|
11532
|
-
return out;
|
|
11533
|
-
},
|
|
11534
|
-
});
|
|
11535
|
-
}
|
|
11536
|
-
const PSBTInputCoder = validate(PSBTKeyMap(PSBTInput), (i) => {
|
|
11537
|
-
if (i.finalScriptWitness && !i.finalScriptWitness.length)
|
|
11538
|
-
throw new Error('validateInput: empty finalScriptWitness');
|
|
11539
|
-
//if (i.finalScriptSig && !i.finalScriptSig.length) throw new Error('validateInput: empty finalScriptSig');
|
|
11540
|
-
if (i.partialSig && !i.partialSig.length)
|
|
11541
|
-
throw new Error('Empty partialSig');
|
|
11542
|
-
if (i.partialSig)
|
|
11543
|
-
for (const [k] of i.partialSig)
|
|
11544
|
-
validatePubkey(k, PubT.ecdsa);
|
|
11545
|
-
if (i.bip32Derivation)
|
|
11546
|
-
for (const [k] of i.bip32Derivation)
|
|
11547
|
-
validatePubkey(k, PubT.ecdsa);
|
|
11548
|
-
// Locktime = unsigned little endian integer greater than or equal to 500000000 representing
|
|
11549
|
-
if (i.requiredTimeLocktime !== undefined && i.requiredTimeLocktime < 500000000)
|
|
11550
|
-
throw new Error(`validateInput: wrong timeLocktime=${i.requiredTimeLocktime}`);
|
|
11551
|
-
// unsigned little endian integer greater than 0 and less than 500000000
|
|
11552
|
-
if (i.requiredHeightLocktime !== undefined &&
|
|
11553
|
-
(i.requiredHeightLocktime <= 0 || i.requiredHeightLocktime >= 500000000))
|
|
11554
|
-
throw new Error(`validateInput: wrong heighLocktime=${i.requiredHeightLocktime}`);
|
|
11555
|
-
if (i.tapLeafScript) {
|
|
11556
|
-
// tap leaf version appears here twice: in control block and at the end of script
|
|
11557
|
-
for (const [k, v] of i.tapLeafScript) {
|
|
11558
|
-
if ((k.version & 254) !== v[v.length - 1])
|
|
11559
|
-
throw new Error('validateInput: tapLeafScript version mimatch');
|
|
11560
|
-
if (v[v.length - 1] & 1)
|
|
11561
|
-
throw new Error('validateInput: tapLeafScript version has parity bit!');
|
|
11562
|
-
}
|
|
11563
|
-
}
|
|
11564
|
-
return i;
|
|
11565
|
-
});
|
|
11566
|
-
const PSBTOutputCoder = validate(PSBTKeyMap(PSBTOutput), (o) => {
|
|
11567
|
-
if (o.bip32Derivation)
|
|
11568
|
-
for (const [k] of o.bip32Derivation)
|
|
11569
|
-
validatePubkey(k, PubT.ecdsa);
|
|
11570
|
-
return o;
|
|
11571
|
-
});
|
|
11572
|
-
const PSBTGlobalCoder = validate(PSBTKeyMap(PSBTGlobal), (g) => {
|
|
11573
|
-
const version = g.version || 0;
|
|
11574
|
-
if (version === 0) {
|
|
11575
|
-
if (!g.unsignedTx)
|
|
11576
|
-
throw new Error('PSBTv0: missing unsignedTx');
|
|
11577
|
-
for (const inp of g.unsignedTx.inputs)
|
|
11578
|
-
if (inp.finalScriptSig && inp.finalScriptSig.length)
|
|
11579
|
-
throw new Error('PSBTv0: input scriptSig found in unsignedTx');
|
|
11580
|
-
}
|
|
11581
|
-
return g;
|
|
11582
|
-
});
|
|
11583
|
-
const _RawPSBTV0 = struct({
|
|
11584
|
-
magic: magic(string(new Uint8Array([0xff])), 'psbt'),
|
|
11585
|
-
global: PSBTGlobalCoder,
|
|
11586
|
-
inputs: array('global/unsignedTx/inputs/length', PSBTInputCoder),
|
|
11587
|
-
outputs: array(null, PSBTOutputCoder),
|
|
11588
|
-
});
|
|
11589
|
-
const _RawPSBTV2 = struct({
|
|
11590
|
-
magic: magic(string(new Uint8Array([0xff])), 'psbt'),
|
|
11591
|
-
global: PSBTGlobalCoder,
|
|
11592
|
-
inputs: array('global/inputCount', PSBTInputCoder),
|
|
11593
|
-
outputs: array('global/outputCount', PSBTOutputCoder),
|
|
11594
|
-
});
|
|
11595
|
-
struct({
|
|
11596
|
-
magic: magic(string(new Uint8Array([0xff])), 'psbt'),
|
|
11597
|
-
items: array(null, apply(array(NULL, tuple([createHex(CompactSizeLen), createBytes(CompactSize)])), coders.dict())),
|
|
11598
|
-
});
|
|
11599
|
-
function validatePSBTFields(version, info, lst) {
|
|
11600
|
-
for (const k in lst) {
|
|
11601
|
-
if (k === 'unknown')
|
|
11602
|
-
continue;
|
|
11603
|
-
if (!info[k])
|
|
11604
|
-
continue;
|
|
11605
|
-
const { allowInc } = PSBTKeyInfo(info[k]);
|
|
11606
|
-
if (!allowInc.includes(version))
|
|
11607
|
-
throw new Error(`PSBTv${version}: field ${k} is not allowed`);
|
|
11608
|
-
}
|
|
11609
|
-
for (const k in info) {
|
|
11610
|
-
const { reqInc } = PSBTKeyInfo(info[k]);
|
|
11611
|
-
if (reqInc.includes(version) && lst[k] === undefined)
|
|
11612
|
-
throw new Error(`PSBTv${version}: missing required field ${k}`);
|
|
11613
|
-
}
|
|
11614
|
-
}
|
|
11615
|
-
function cleanPSBTFields(version, info, lst) {
|
|
11616
|
-
const out = {};
|
|
11617
|
-
for (const _k in lst) {
|
|
11618
|
-
const k = _k;
|
|
11619
|
-
if (k !== 'unknown') {
|
|
11620
|
-
if (!info[k])
|
|
11621
|
-
continue;
|
|
11622
|
-
const { allowInc, silentIgnore } = PSBTKeyInfo(info[k]);
|
|
11623
|
-
if (!allowInc.includes(version)) {
|
|
11624
|
-
if (silentIgnore)
|
|
11625
|
-
continue;
|
|
11626
|
-
throw new Error(`Failed to serialize in PSBTv${version}: ${k} but versions allows inclusion=${allowInc}`);
|
|
11627
|
-
}
|
|
11628
|
-
}
|
|
11629
|
-
out[k] = lst[k];
|
|
11630
|
-
}
|
|
11631
|
-
return out;
|
|
11632
|
-
}
|
|
11633
|
-
function validatePSBT(tx) {
|
|
11634
|
-
const version = (tx && tx.global && tx.global.version) || 0;
|
|
11635
|
-
validatePSBTFields(version, PSBTGlobal, tx.global);
|
|
11636
|
-
for (const i of tx.inputs)
|
|
11637
|
-
validatePSBTFields(version, PSBTInput, i);
|
|
11638
|
-
for (const o of tx.outputs)
|
|
11639
|
-
validatePSBTFields(version, PSBTOutput, o);
|
|
11640
|
-
// We allow only one empty element at the end of map (compat with bitcoinjs-lib bug)
|
|
11641
|
-
const inputCount = !version ? tx.global.unsignedTx.inputs.length : tx.global.inputCount;
|
|
11642
|
-
if (tx.inputs.length < inputCount)
|
|
11643
|
-
throw new Error('Not enough inputs');
|
|
11644
|
-
const inputsLeft = tx.inputs.slice(inputCount);
|
|
11645
|
-
if (inputsLeft.length > 1 || (inputsLeft.length && Object.keys(inputsLeft[0]).length))
|
|
11646
|
-
throw new Error(`Unexpected inputs left in tx=${inputsLeft}`);
|
|
11647
|
-
// Same for inputs
|
|
11648
|
-
const outputCount = !version ? tx.global.unsignedTx.outputs.length : tx.global.outputCount;
|
|
11649
|
-
if (tx.outputs.length < outputCount)
|
|
11650
|
-
throw new Error('Not outputs inputs');
|
|
11651
|
-
const outputsLeft = tx.outputs.slice(outputCount);
|
|
11652
|
-
if (outputsLeft.length > 1 || (outputsLeft.length && Object.keys(outputsLeft[0]).length))
|
|
11653
|
-
throw new Error(`Unexpected outputs left in tx=${outputsLeft}`);
|
|
11654
|
-
return tx;
|
|
11655
|
-
}
|
|
11656
|
-
function mergeKeyMap(psbtEnum, val, cur, allowedFields, allowUnknown) {
|
|
11657
|
-
const res = { ...cur, ...val };
|
|
11658
|
-
// All arguments can be provided as hex
|
|
11659
|
-
for (const k in psbtEnum) {
|
|
11660
|
-
const key = k;
|
|
11661
|
-
const [_, kC, vC] = psbtEnum[key];
|
|
11662
|
-
const cannotChange = allowedFields && !allowedFields.includes(k);
|
|
11663
|
-
if (val[k] === undefined && k in val) {
|
|
11664
|
-
if (cannotChange)
|
|
11665
|
-
throw new Error(`Cannot remove signed field=${k}`);
|
|
11666
|
-
delete res[k];
|
|
11667
|
-
}
|
|
11668
|
-
else if (kC) {
|
|
11669
|
-
const oldKV = (cur && cur[k] ? cur[k] : []);
|
|
11670
|
-
let newKV = val[key];
|
|
11671
|
-
if (newKV) {
|
|
11672
|
-
if (!Array.isArray(newKV))
|
|
11673
|
-
throw new Error(`keyMap(${k}): KV pairs should be [k, v][]`);
|
|
11674
|
-
// Decode hex in k-v
|
|
11675
|
-
newKV = newKV.map((val) => {
|
|
11676
|
-
if (val.length !== 2)
|
|
11677
|
-
throw new Error(`keyMap(${k}): KV pairs should be [k, v][]`);
|
|
11678
|
-
return [
|
|
11679
|
-
typeof val[0] === 'string' ? kC.decode(hex.decode(val[0])) : val[0],
|
|
11680
|
-
typeof val[1] === 'string' ? vC.decode(hex.decode(val[1])) : val[1],
|
|
11681
|
-
];
|
|
11682
|
-
});
|
|
11683
|
-
const map = {};
|
|
11684
|
-
const add = (kStr, k, v) => {
|
|
11685
|
-
if (map[kStr] === undefined) {
|
|
11686
|
-
map[kStr] = [k, v];
|
|
11687
|
-
return;
|
|
11688
|
-
}
|
|
11689
|
-
const oldVal = hex.encode(vC.encode(map[kStr][1]));
|
|
11690
|
-
const newVal = hex.encode(vC.encode(v));
|
|
11691
|
-
if (oldVal !== newVal)
|
|
11692
|
-
throw new Error(`keyMap(${key}): same key=${kStr} oldVal=${oldVal} newVal=${newVal}`);
|
|
11693
|
-
};
|
|
11694
|
-
for (const [k, v] of oldKV) {
|
|
11695
|
-
const kStr = hex.encode(kC.encode(k));
|
|
11696
|
-
add(kStr, k, v);
|
|
11697
|
-
}
|
|
11698
|
-
for (const [k, v] of newKV) {
|
|
11699
|
-
const kStr = hex.encode(kC.encode(k));
|
|
11700
|
-
// undefined removes previous value
|
|
11701
|
-
if (v === undefined) {
|
|
11702
|
-
if (cannotChange)
|
|
11703
|
-
throw new Error(`Cannot remove signed field=${key}/${k}`);
|
|
11704
|
-
delete map[kStr];
|
|
11705
|
-
}
|
|
11706
|
-
else
|
|
11707
|
-
add(kStr, k, v);
|
|
11708
|
-
}
|
|
11709
|
-
res[key] = Object.values(map);
|
|
11710
|
-
}
|
|
11711
|
-
}
|
|
11712
|
-
else if (typeof res[k] === 'string') {
|
|
11713
|
-
res[k] = vC.decode(hex.decode(res[k]));
|
|
11714
|
-
}
|
|
11715
|
-
else if (cannotChange && k in val && cur && cur[k] !== undefined) {
|
|
11716
|
-
if (!equalBytes(vC.encode(val[k]), vC.encode(cur[k])))
|
|
11717
|
-
throw new Error(`Cannot change signed field=${k}`);
|
|
11718
|
-
}
|
|
11719
|
-
}
|
|
11720
|
-
// Remove unknown keys except the "unknown" array if allowUnknown is true
|
|
11721
|
-
for (const k in res) {
|
|
11722
|
-
if (!psbtEnum[k]) {
|
|
11723
|
-
if (allowUnknown && k === 'unknown')
|
|
11724
|
-
continue;
|
|
11725
|
-
delete res[k];
|
|
11726
|
-
}
|
|
11727
|
-
}
|
|
11728
|
-
return res;
|
|
11729
|
-
}
|
|
11730
|
-
const RawPSBTV0 = validate(_RawPSBTV0, validatePSBT);
|
|
11731
|
-
const RawPSBTV2 = validate(_RawPSBTV2, validatePSBT);
|
|
11732
|
-
|
|
11733
|
-
const OutP2A = {
|
|
11734
|
-
encode(from) {
|
|
11735
|
-
if (from.length !== 2 || from[0] !== 1 || !isBytes(from[1]) || hex.encode(from[1]) !== '4e73')
|
|
11736
|
-
return;
|
|
11737
|
-
return { type: 'p2a', script: Script.encode(from) };
|
|
11738
|
-
},
|
|
11739
|
-
decode: (to) => {
|
|
11740
|
-
if (to.type !== 'p2a')
|
|
11741
|
-
return;
|
|
11742
|
-
return [1, hex.decode('4e73')];
|
|
11743
|
-
},
|
|
11744
|
-
};
|
|
11745
|
-
function isValidPubkey(pub, type) {
|
|
11746
|
-
try {
|
|
11747
|
-
validatePubkey(pub, type);
|
|
11748
|
-
return true;
|
|
11749
|
-
}
|
|
11750
|
-
catch (e) {
|
|
11751
|
-
return false;
|
|
11752
|
-
}
|
|
11753
|
-
}
|
|
11754
|
-
const OutPK = {
|
|
11755
|
-
encode(from) {
|
|
11756
|
-
if (from.length !== 2 ||
|
|
11757
|
-
!isBytes(from[0]) ||
|
|
11758
|
-
!isValidPubkey(from[0], PubT.ecdsa) ||
|
|
11759
|
-
from[1] !== 'CHECKSIG')
|
|
11760
|
-
return;
|
|
11761
|
-
return { type: 'pk', pubkey: from[0] };
|
|
11762
|
-
},
|
|
11763
|
-
decode: (to) => (to.type === 'pk' ? [to.pubkey, 'CHECKSIG'] : undefined),
|
|
11764
|
-
};
|
|
11765
|
-
const OutPKH = {
|
|
11766
|
-
encode(from) {
|
|
11767
|
-
if (from.length !== 5 || from[0] !== 'DUP' || from[1] !== 'HASH160' || !isBytes(from[2]))
|
|
11768
|
-
return;
|
|
11769
|
-
if (from[3] !== 'EQUALVERIFY' || from[4] !== 'CHECKSIG')
|
|
11770
|
-
return;
|
|
11771
|
-
return { type: 'pkh', hash: from[2] };
|
|
11772
|
-
},
|
|
11773
|
-
decode: (to) => to.type === 'pkh' ? ['DUP', 'HASH160', to.hash, 'EQUALVERIFY', 'CHECKSIG'] : undefined,
|
|
11774
|
-
};
|
|
11775
|
-
const OutSH = {
|
|
11776
|
-
encode(from) {
|
|
11777
|
-
if (from.length !== 3 || from[0] !== 'HASH160' || !isBytes(from[1]) || from[2] !== 'EQUAL')
|
|
11778
|
-
return;
|
|
11779
|
-
return { type: 'sh', hash: from[1] };
|
|
11780
|
-
},
|
|
11781
|
-
decode: (to) => to.type === 'sh' ? ['HASH160', to.hash, 'EQUAL'] : undefined,
|
|
11782
|
-
};
|
|
11783
|
-
const OutWSH = {
|
|
11784
|
-
encode(from) {
|
|
11785
|
-
if (from.length !== 2 || from[0] !== 0 || !isBytes(from[1]))
|
|
11786
|
-
return;
|
|
11787
|
-
if (from[1].length !== 32)
|
|
11788
|
-
return;
|
|
11789
|
-
return { type: 'wsh', hash: from[1] };
|
|
11790
|
-
},
|
|
11791
|
-
decode: (to) => (to.type === 'wsh' ? [0, to.hash] : undefined),
|
|
11792
|
-
};
|
|
11793
|
-
const OutWPKH = {
|
|
11794
|
-
encode(from) {
|
|
11795
|
-
if (from.length !== 2 || from[0] !== 0 || !isBytes(from[1]))
|
|
11796
|
-
return;
|
|
11797
|
-
if (from[1].length !== 20)
|
|
11798
|
-
return;
|
|
11799
|
-
return { type: 'wpkh', hash: from[1] };
|
|
11800
|
-
},
|
|
11801
|
-
decode: (to) => (to.type === 'wpkh' ? [0, to.hash] : undefined),
|
|
11802
|
-
};
|
|
11803
|
-
const OutMS = {
|
|
11804
|
-
encode(from) {
|
|
11805
|
-
const last = from.length - 1;
|
|
11806
|
-
if (from[last] !== 'CHECKMULTISIG')
|
|
11807
|
-
return;
|
|
11808
|
-
const m = from[0];
|
|
11809
|
-
const n = from[last - 1];
|
|
11810
|
-
if (typeof m !== 'number' || typeof n !== 'number')
|
|
11811
|
-
return;
|
|
11812
|
-
const pubkeys = from.slice(1, -2);
|
|
11813
|
-
if (n !== pubkeys.length)
|
|
11814
|
-
return;
|
|
11815
|
-
for (const pub of pubkeys)
|
|
11816
|
-
if (!isBytes(pub))
|
|
11817
|
-
return;
|
|
11818
|
-
return { type: 'ms', m, pubkeys: pubkeys }; // we don't need n, since it is the same as pubkeys
|
|
11819
|
-
},
|
|
11820
|
-
// checkmultisig(n, ..pubkeys, m)
|
|
11821
|
-
decode: (to) => to.type === 'ms' ? [to.m, ...to.pubkeys, to.pubkeys.length, 'CHECKMULTISIG'] : undefined,
|
|
11822
|
-
};
|
|
11823
|
-
const OutTR = {
|
|
11824
|
-
encode(from) {
|
|
11825
|
-
if (from.length !== 2 || from[0] !== 1 || !isBytes(from[1]))
|
|
11826
|
-
return;
|
|
11827
|
-
return { type: 'tr', pubkey: from[1] };
|
|
11828
|
-
},
|
|
11829
|
-
decode: (to) => (to.type === 'tr' ? [1, to.pubkey] : undefined),
|
|
11830
|
-
};
|
|
11831
|
-
const OutTRNS = {
|
|
11832
|
-
encode(from) {
|
|
11833
|
-
const last = from.length - 1;
|
|
11834
|
-
if (from[last] !== 'CHECKSIG')
|
|
11835
|
-
return;
|
|
11836
|
-
const pubkeys = [];
|
|
11837
|
-
// On error return, since it can be different script
|
|
11838
|
-
for (let i = 0; i < last; i++) {
|
|
11839
|
-
const elm = from[i];
|
|
11840
|
-
if (i & 1) {
|
|
11841
|
-
if (elm !== 'CHECKSIGVERIFY' || i === last - 1)
|
|
11842
|
-
return;
|
|
11843
|
-
continue;
|
|
11844
|
-
}
|
|
11845
|
-
if (!isBytes(elm))
|
|
11846
|
-
return;
|
|
11847
|
-
pubkeys.push(elm);
|
|
11848
|
-
}
|
|
11849
|
-
return { type: 'tr_ns', pubkeys };
|
|
11850
|
-
},
|
|
11851
|
-
decode: (to) => {
|
|
11852
|
-
if (to.type !== 'tr_ns')
|
|
11853
|
-
return;
|
|
11854
|
-
const out = [];
|
|
11855
|
-
for (let i = 0; i < to.pubkeys.length - 1; i++)
|
|
11856
|
-
out.push(to.pubkeys[i], 'CHECKSIGVERIFY');
|
|
11857
|
-
out.push(to.pubkeys[to.pubkeys.length - 1], 'CHECKSIG');
|
|
11858
|
-
return out;
|
|
11859
|
-
},
|
|
11860
|
-
};
|
|
11861
|
-
const OutTRMS = {
|
|
11862
|
-
encode(from) {
|
|
11863
|
-
const last = from.length - 1;
|
|
11864
|
-
if (from[last] !== 'NUMEQUAL' || from[1] !== 'CHECKSIG')
|
|
11865
|
-
return;
|
|
11866
|
-
const pubkeys = [];
|
|
11867
|
-
const m = OpToNum(from[last - 1]);
|
|
11868
|
-
if (typeof m !== 'number')
|
|
11869
|
-
return;
|
|
11870
|
-
for (let i = 0; i < last - 1; i++) {
|
|
11871
|
-
const elm = from[i];
|
|
11872
|
-
if (i & 1) {
|
|
11873
|
-
if (elm !== (i === 1 ? 'CHECKSIG' : 'CHECKSIGADD'))
|
|
11874
|
-
throw new Error('OutScript.encode/tr_ms: wrong element');
|
|
11875
|
-
continue;
|
|
11876
|
-
}
|
|
11877
|
-
if (!isBytes(elm))
|
|
11878
|
-
throw new Error('OutScript.encode/tr_ms: wrong key element');
|
|
11879
|
-
pubkeys.push(elm);
|
|
11880
|
-
}
|
|
11881
|
-
return { type: 'tr_ms', pubkeys, m };
|
|
11882
|
-
},
|
|
11883
|
-
decode: (to) => {
|
|
11884
|
-
if (to.type !== 'tr_ms')
|
|
11885
|
-
return;
|
|
11886
|
-
const out = [to.pubkeys[0], 'CHECKSIG'];
|
|
11887
|
-
for (let i = 1; i < to.pubkeys.length; i++)
|
|
11888
|
-
out.push(to.pubkeys[i], 'CHECKSIGADD');
|
|
11889
|
-
out.push(to.m, 'NUMEQUAL');
|
|
11890
|
-
return out;
|
|
11891
|
-
},
|
|
11892
|
-
};
|
|
11893
|
-
const OutUnknown = {
|
|
11894
|
-
encode(from) {
|
|
11895
|
-
return { type: 'unknown', script: Script.encode(from) };
|
|
11896
|
-
},
|
|
11897
|
-
decode: (to) => to.type === 'unknown' ? Script.decode(to.script) : undefined,
|
|
11898
|
-
};
|
|
11899
|
-
// /Payments
|
|
11900
|
-
const OutScripts = [
|
|
11901
|
-
OutP2A,
|
|
11902
|
-
OutPK,
|
|
11903
|
-
OutPKH,
|
|
11904
|
-
OutSH,
|
|
11905
|
-
OutWSH,
|
|
11906
|
-
OutWPKH,
|
|
11907
|
-
OutMS,
|
|
11908
|
-
OutTR,
|
|
11909
|
-
OutTRNS,
|
|
11910
|
-
OutTRMS,
|
|
11911
|
-
OutUnknown,
|
|
11912
|
-
];
|
|
11913
|
-
// TODO: we can support user supplied output scripts now
|
|
11914
|
-
// - addOutScript
|
|
11915
|
-
// - removeOutScript
|
|
11916
|
-
// - We can do that as log we modify array in-place
|
|
11917
|
-
// - Actually is very hard, since there is sign/finalize logic
|
|
11918
|
-
const _OutScript = apply(Script, coders.match(OutScripts));
|
|
11919
|
-
// We can validate this once, because of packed & coders
|
|
11920
|
-
const OutScript = validate(_OutScript, (i) => {
|
|
11921
|
-
if (i.type === 'pk' && !isValidPubkey(i.pubkey, PubT.ecdsa))
|
|
11922
|
-
throw new Error('OutScript/pk: wrong key');
|
|
11923
|
-
if ((i.type === 'pkh' || i.type === 'sh' || i.type === 'wpkh') &&
|
|
11924
|
-
(!isBytes(i.hash) || i.hash.length !== 20))
|
|
11925
|
-
throw new Error(`OutScript/${i.type}: wrong hash`);
|
|
11926
|
-
if (i.type === 'wsh' && (!isBytes(i.hash) || i.hash.length !== 32))
|
|
11927
|
-
throw new Error(`OutScript/wsh: wrong hash`);
|
|
11928
|
-
if (i.type === 'tr' && (!isBytes(i.pubkey) || !isValidPubkey(i.pubkey, PubT.schnorr)))
|
|
11929
|
-
throw new Error('OutScript/tr: wrong taproot public key');
|
|
11930
|
-
if (i.type === 'ms' || i.type === 'tr_ns' || i.type === 'tr_ms')
|
|
11931
|
-
if (!Array.isArray(i.pubkeys))
|
|
11932
|
-
throw new Error('OutScript/multisig: wrong pubkeys array');
|
|
11933
|
-
if (i.type === 'ms') {
|
|
11934
|
-
const n = i.pubkeys.length;
|
|
11935
|
-
for (const p of i.pubkeys)
|
|
11936
|
-
if (!isValidPubkey(p, PubT.ecdsa))
|
|
11937
|
-
throw new Error('OutScript/multisig: wrong pubkey');
|
|
11938
|
-
if (i.m <= 0 || n > 16 || i.m > n)
|
|
11939
|
-
throw new Error('OutScript/multisig: invalid params');
|
|
11940
|
-
}
|
|
11941
|
-
if (i.type === 'tr_ns' || i.type === 'tr_ms') {
|
|
11942
|
-
for (const p of i.pubkeys)
|
|
11943
|
-
if (!isValidPubkey(p, PubT.schnorr))
|
|
11944
|
-
throw new Error(`OutScript/${i.type}: wrong pubkey`);
|
|
11945
|
-
}
|
|
11946
|
-
if (i.type === 'tr_ms') {
|
|
11947
|
-
const n = i.pubkeys.length;
|
|
11948
|
-
if (i.m <= 0 || n > 999 || i.m > n)
|
|
11949
|
-
throw new Error('OutScript/tr_ms: invalid params');
|
|
11950
|
-
}
|
|
11951
|
-
return i;
|
|
11952
|
-
});
|
|
11953
|
-
// Basic sanity check for scripts
|
|
11954
|
-
function checkWSH(s, witnessScript) {
|
|
11955
|
-
if (!equalBytes(s.hash, sha256$1(witnessScript)))
|
|
11956
|
-
throw new Error('checkScript: wsh wrong witnessScript hash');
|
|
11957
|
-
const w = OutScript.decode(witnessScript);
|
|
11958
|
-
if (w.type === 'tr' || w.type === 'tr_ns' || w.type === 'tr_ms')
|
|
11959
|
-
throw new Error(`checkScript: P2${w.type} cannot be wrapped in P2SH`);
|
|
11960
|
-
if (w.type === 'wpkh' || w.type === 'sh')
|
|
11961
|
-
throw new Error(`checkScript: P2${w.type} cannot be wrapped in P2WSH`);
|
|
11962
|
-
}
|
|
11963
|
-
function checkScript(script, redeemScript, witnessScript) {
|
|
11964
|
-
if (script) {
|
|
11965
|
-
const s = OutScript.decode(script);
|
|
11966
|
-
// ms||pk maybe work, but there will be no address, hard to spend
|
|
11967
|
-
if (s.type === 'tr_ns' || s.type === 'tr_ms' || s.type === 'ms' || s.type == 'pk')
|
|
11968
|
-
throw new Error(`checkScript: non-wrapped ${s.type}`);
|
|
11969
|
-
if (s.type === 'sh' && redeemScript) {
|
|
11970
|
-
if (!equalBytes(s.hash, hash160(redeemScript)))
|
|
11971
|
-
throw new Error('checkScript: sh wrong redeemScript hash');
|
|
11972
|
-
const r = OutScript.decode(redeemScript);
|
|
11973
|
-
if (r.type === 'tr' || r.type === 'tr_ns' || r.type === 'tr_ms')
|
|
11974
|
-
throw new Error(`checkScript: P2${r.type} cannot be wrapped in P2SH`);
|
|
11975
|
-
// Not sure if this unspendable, but we cannot represent this via PSBT
|
|
11976
|
-
if (r.type === 'sh')
|
|
11977
|
-
throw new Error('checkScript: P2SH cannot be wrapped in P2SH');
|
|
11978
|
-
}
|
|
11979
|
-
if (s.type === 'wsh' && witnessScript)
|
|
11980
|
-
checkWSH(s, witnessScript);
|
|
11981
|
-
}
|
|
11982
|
-
if (redeemScript) {
|
|
11983
|
-
const r = OutScript.decode(redeemScript);
|
|
11984
|
-
if (r.type === 'wsh' && witnessScript)
|
|
11985
|
-
checkWSH(r, witnessScript);
|
|
11986
|
-
}
|
|
11987
|
-
}
|
|
11988
|
-
const TAP_LEAF_VERSION = 0xc0;
|
|
11989
|
-
const tapLeafHash = (script, version = TAP_LEAF_VERSION) => tagSchnorr('TapLeaf', new Uint8Array([version]), VarBytes.encode(script));
|
|
11990
|
-
const base58check = createBase58check(sha256$1);
|
|
11991
|
-
function validateWitness(version, data) {
|
|
11992
|
-
if (data.length < 2 || data.length > 40)
|
|
11993
|
-
throw new Error('Witness: invalid length');
|
|
11994
|
-
if (version > 16)
|
|
11995
|
-
throw new Error('Witness: invalid version');
|
|
11996
|
-
if (version === 0 && !(data.length === 20 || data.length === 32))
|
|
11997
|
-
throw new Error('Witness: invalid length for version');
|
|
11998
|
-
}
|
|
11999
|
-
function programToWitness(version, data, network = NETWORK) {
|
|
12000
|
-
validateWitness(version, data);
|
|
12001
|
-
const coder = version === 0 ? bech32 : bech32m;
|
|
12002
|
-
return coder.encode(network.bech32, [version].concat(coder.toWords(data)));
|
|
12003
|
-
}
|
|
12004
|
-
function formatKey(hashed, prefix) {
|
|
12005
|
-
return base58check.encode(concatBytes(Uint8Array.from(prefix), hashed));
|
|
12006
|
-
}
|
|
12007
|
-
// Returns OutType, which can be used to create outscript
|
|
12008
|
-
function Address(network = NETWORK) {
|
|
12009
|
-
return {
|
|
12010
|
-
encode(from) {
|
|
12011
|
-
const { type } = from;
|
|
12012
|
-
if (type === 'wpkh')
|
|
12013
|
-
return programToWitness(0, from.hash, network);
|
|
12014
|
-
else if (type === 'wsh')
|
|
12015
|
-
return programToWitness(0, from.hash, network);
|
|
12016
|
-
else if (type === 'tr')
|
|
12017
|
-
return programToWitness(1, from.pubkey, network);
|
|
12018
|
-
else if (type === 'pkh')
|
|
12019
|
-
return formatKey(from.hash, [network.pubKeyHash]);
|
|
12020
|
-
else if (type === 'sh')
|
|
12021
|
-
return formatKey(from.hash, [network.scriptHash]);
|
|
12022
|
-
throw new Error(`Unknown address type=${type}`);
|
|
12023
|
-
},
|
|
12024
|
-
decode(address) {
|
|
12025
|
-
if (address.length < 14 || address.length > 74)
|
|
12026
|
-
throw new Error('Invalid address length');
|
|
12027
|
-
// Bech32
|
|
12028
|
-
if (network.bech32 && address.toLowerCase().startsWith(`${network.bech32}1`)) {
|
|
12029
|
-
let res;
|
|
12030
|
-
try {
|
|
12031
|
-
res = bech32.decode(address);
|
|
12032
|
-
if (res.words[0] !== 0)
|
|
12033
|
-
throw new Error(`bech32: wrong version=${res.words[0]}`);
|
|
12034
|
-
}
|
|
12035
|
-
catch (_) {
|
|
12036
|
-
// Starting from version 1 it is decoded as bech32m
|
|
12037
|
-
res = bech32m.decode(address);
|
|
12038
|
-
if (res.words[0] === 0)
|
|
12039
|
-
throw new Error(`bech32m: wrong version=${res.words[0]}`);
|
|
12040
|
-
}
|
|
12041
|
-
if (res.prefix !== network.bech32)
|
|
12042
|
-
throw new Error(`wrong bech32 prefix=${res.prefix}`);
|
|
12043
|
-
const [version, ...program] = res.words;
|
|
12044
|
-
const data = bech32.fromWords(program);
|
|
12045
|
-
validateWitness(version, data);
|
|
12046
|
-
if (version === 0 && data.length === 32)
|
|
12047
|
-
return { type: 'wsh', hash: data };
|
|
12048
|
-
else if (version === 0 && data.length === 20)
|
|
12049
|
-
return { type: 'wpkh', hash: data };
|
|
12050
|
-
else if (version === 1 && data.length === 32)
|
|
12051
|
-
return { type: 'tr', pubkey: data };
|
|
12052
|
-
else
|
|
12053
|
-
throw new Error('Unknown witness program');
|
|
12054
|
-
}
|
|
12055
|
-
const data = base58check.decode(address);
|
|
12056
|
-
if (data.length !== 21)
|
|
12057
|
-
throw new Error('Invalid base58 address');
|
|
12058
|
-
// Pay To Public Key Hash
|
|
12059
|
-
if (data[0] === network.pubKeyHash) {
|
|
12060
|
-
return { type: 'pkh', hash: data.slice(1) };
|
|
12061
|
-
}
|
|
12062
|
-
else if (data[0] === network.scriptHash) {
|
|
12063
|
-
return {
|
|
12064
|
-
type: 'sh',
|
|
12065
|
-
hash: data.slice(1),
|
|
12066
|
-
};
|
|
12067
|
-
}
|
|
12068
|
-
throw new Error(`Invalid address prefix=${data[0]}`);
|
|
12069
|
-
},
|
|
12070
|
-
};
|
|
12071
|
-
}
|
|
12072
|
-
|
|
12073
|
-
const EMPTY32 = new Uint8Array(32);
|
|
12074
|
-
const EMPTY_OUTPUT = {
|
|
12075
|
-
amount: 0xffffffffffffffffn,
|
|
12076
|
-
script: EMPTY,
|
|
12077
|
-
};
|
|
12078
|
-
const toVsize = (weight) => Math.ceil(weight / 4);
|
|
12079
|
-
const PRECISION = 8;
|
|
12080
|
-
const DEFAULT_VERSION$2 = 2;
|
|
12081
|
-
const DEFAULT_LOCKTIME = 0;
|
|
12082
|
-
const DEFAULT_SEQUENCE = 4294967295;
|
|
12083
|
-
coders.decimal(PRECISION);
|
|
12084
|
-
// Same as value || def, but doesn't overwrites zero ('0', 0, 0n, etc)
|
|
12085
|
-
const def = (value, def) => (value === undefined ? def : value);
|
|
12086
|
-
function cloneDeep(obj) {
|
|
12087
|
-
if (Array.isArray(obj))
|
|
12088
|
-
return obj.map((i) => cloneDeep(i));
|
|
12089
|
-
// slice of nodejs Buffer doesn't copy
|
|
12090
|
-
else if (isBytes(obj))
|
|
12091
|
-
return Uint8Array.from(obj);
|
|
12092
|
-
// immutable
|
|
12093
|
-
else if (['number', 'bigint', 'boolean', 'string', 'undefined'].includes(typeof obj))
|
|
12094
|
-
return obj;
|
|
12095
|
-
// null is object
|
|
12096
|
-
else if (obj === null)
|
|
12097
|
-
return obj;
|
|
12098
|
-
// should be last, so it won't catch other types
|
|
12099
|
-
else if (typeof obj === 'object') {
|
|
12100
|
-
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, cloneDeep(v)]));
|
|
12101
|
-
}
|
|
12102
|
-
throw new Error(`cloneDeep: unknown type=${obj} (${typeof obj})`);
|
|
12103
|
-
}
|
|
12104
|
-
/**
|
|
12105
|
-
* Internal, exported only for backwards-compat. Use `SigHash` instead.
|
|
12106
|
-
* @deprecated
|
|
12107
|
-
*/
|
|
12108
|
-
var SignatureHash;
|
|
12109
|
-
(function (SignatureHash) {
|
|
12110
|
-
SignatureHash[SignatureHash["DEFAULT"] = 0] = "DEFAULT";
|
|
12111
|
-
SignatureHash[SignatureHash["ALL"] = 1] = "ALL";
|
|
12112
|
-
SignatureHash[SignatureHash["NONE"] = 2] = "NONE";
|
|
12113
|
-
SignatureHash[SignatureHash["SINGLE"] = 3] = "SINGLE";
|
|
12114
|
-
SignatureHash[SignatureHash["ANYONECANPAY"] = 128] = "ANYONECANPAY";
|
|
12115
|
-
})(SignatureHash || (SignatureHash = {}));
|
|
12116
|
-
var SigHash;
|
|
12117
|
-
(function (SigHash) {
|
|
12118
|
-
SigHash[SigHash["DEFAULT"] = 0] = "DEFAULT";
|
|
12119
|
-
SigHash[SigHash["ALL"] = 1] = "ALL";
|
|
12120
|
-
SigHash[SigHash["NONE"] = 2] = "NONE";
|
|
12121
|
-
SigHash[SigHash["SINGLE"] = 3] = "SINGLE";
|
|
12122
|
-
SigHash[SigHash["DEFAULT_ANYONECANPAY"] = 128] = "DEFAULT_ANYONECANPAY";
|
|
12123
|
-
SigHash[SigHash["ALL_ANYONECANPAY"] = 129] = "ALL_ANYONECANPAY";
|
|
12124
|
-
SigHash[SigHash["NONE_ANYONECANPAY"] = 130] = "NONE_ANYONECANPAY";
|
|
12125
|
-
SigHash[SigHash["SINGLE_ANYONECANPAY"] = 131] = "SINGLE_ANYONECANPAY";
|
|
12126
|
-
})(SigHash || (SigHash = {}));
|
|
12127
|
-
function getTaprootKeys(privKey, pubKey, internalKey, merkleRoot = EMPTY) {
|
|
12128
|
-
if (equalBytes(internalKey, pubKey)) {
|
|
12129
|
-
privKey = taprootTweakPrivKey(privKey, merkleRoot);
|
|
12130
|
-
pubKey = pubSchnorr(privKey);
|
|
12131
|
-
}
|
|
12132
|
-
return { privKey, pubKey };
|
|
12133
|
-
}
|
|
12134
|
-
// Force check amount/script
|
|
12135
|
-
function outputBeforeSign(i) {
|
|
12136
|
-
if (i.script === undefined || i.amount === undefined)
|
|
12137
|
-
throw new Error('Transaction/output: script and amount required');
|
|
12138
|
-
return { script: i.script, amount: i.amount };
|
|
12139
|
-
}
|
|
12140
|
-
// Force check index/txid/sequence
|
|
12141
|
-
function inputBeforeSign(i) {
|
|
12142
|
-
if (i.txid === undefined || i.index === undefined)
|
|
12143
|
-
throw new Error('Transaction/input: txid and index required');
|
|
12144
|
-
return {
|
|
12145
|
-
txid: i.txid,
|
|
12146
|
-
index: i.index,
|
|
12147
|
-
sequence: def(i.sequence, DEFAULT_SEQUENCE),
|
|
12148
|
-
finalScriptSig: def(i.finalScriptSig, EMPTY),
|
|
12149
|
-
};
|
|
12150
|
-
}
|
|
12151
|
-
function cleanFinalInput(i) {
|
|
12152
|
-
for (const _k in i) {
|
|
12153
|
-
const k = _k;
|
|
12154
|
-
if (!PSBTInputFinalKeys.includes(k))
|
|
12155
|
-
delete i[k];
|
|
12156
|
-
}
|
|
12157
|
-
}
|
|
12158
|
-
// (TxHash, Idx)
|
|
12159
|
-
const TxHashIdx = struct({ txid: createBytes(32, true), index: U32LE });
|
|
12160
|
-
function validateSigHash(s) {
|
|
12161
|
-
if (typeof s !== 'number' || typeof SigHash[s] !== 'string')
|
|
12162
|
-
throw new Error(`Invalid SigHash=${s}`);
|
|
12163
|
-
return s;
|
|
12164
|
-
}
|
|
12165
|
-
function unpackSighash(hashType) {
|
|
12166
|
-
const masked = hashType & 0b0011111;
|
|
12167
|
-
return {
|
|
12168
|
-
isAny: !!(hashType & SignatureHash.ANYONECANPAY),
|
|
12169
|
-
isNone: masked === SignatureHash.NONE,
|
|
12170
|
-
isSingle: masked === SignatureHash.SINGLE,
|
|
12171
|
-
};
|
|
12172
|
-
}
|
|
12173
|
-
function validateOpts(opts) {
|
|
12174
|
-
if (opts !== undefined && {}.toString.call(opts) !== '[object Object]')
|
|
12175
|
-
throw new Error(`Wrong object type for transaction options: ${opts}`);
|
|
12176
|
-
const _opts = {
|
|
12177
|
-
...opts,
|
|
12178
|
-
// Defaults
|
|
12179
|
-
version: def(opts.version, DEFAULT_VERSION$2),
|
|
12180
|
-
lockTime: def(opts.lockTime, 0),
|
|
12181
|
-
PSBTVersion: def(opts.PSBTVersion, 0),
|
|
12182
|
-
};
|
|
12183
|
-
if (typeof _opts.allowUnknowInput !== 'undefined')
|
|
12184
|
-
opts.allowUnknownInputs = _opts.allowUnknowInput;
|
|
12185
|
-
if (typeof _opts.allowUnknowOutput !== 'undefined')
|
|
12186
|
-
opts.allowUnknownOutputs = _opts.allowUnknowOutput;
|
|
12187
|
-
if (typeof _opts.lockTime !== 'number')
|
|
12188
|
-
throw new Error('Transaction lock time should be number');
|
|
12189
|
-
U32LE.encode(_opts.lockTime); // Additional range checks that lockTime
|
|
12190
|
-
// There is no PSBT v1, and any new version will probably have fields which we don't know how to parse, which
|
|
12191
|
-
// can lead to constructing broken transactions
|
|
12192
|
-
if (_opts.PSBTVersion !== 0 && _opts.PSBTVersion !== 2)
|
|
12193
|
-
throw new Error(`Unknown PSBT version ${_opts.PSBTVersion}`);
|
|
12194
|
-
// Flags
|
|
12195
|
-
for (const k of [
|
|
12196
|
-
'allowUnknownVersion',
|
|
12197
|
-
'allowUnknownOutputs',
|
|
12198
|
-
'allowUnknownInputs',
|
|
12199
|
-
'disableScriptCheck',
|
|
12200
|
-
'bip174jsCompat',
|
|
12201
|
-
'allowLegacyWitnessUtxo',
|
|
12202
|
-
'lowR',
|
|
12203
|
-
]) {
|
|
12204
|
-
const v = _opts[k];
|
|
12205
|
-
if (v === undefined)
|
|
12206
|
-
continue; // optional
|
|
12207
|
-
if (typeof v !== 'boolean')
|
|
12208
|
-
throw new Error(`Transation options wrong type: ${k}=${v} (${typeof v})`);
|
|
12209
|
-
}
|
|
12210
|
-
// 0 and -1 happens in tests
|
|
12211
|
-
if (_opts.allowUnknownVersion
|
|
12212
|
-
? typeof _opts.version === 'number'
|
|
12213
|
-
: ![-1, 0, 1, 2, 3].includes(_opts.version))
|
|
12214
|
-
throw new Error(`Unknown version: ${_opts.version}`);
|
|
12215
|
-
if (_opts.customScripts !== undefined) {
|
|
12216
|
-
const cs = _opts.customScripts;
|
|
12217
|
-
if (!Array.isArray(cs)) {
|
|
12218
|
-
throw new Error(`wrong custom scripts type (expected array): customScripts=${cs} (${typeof cs})`);
|
|
12219
|
-
}
|
|
12220
|
-
for (const s of cs) {
|
|
12221
|
-
if (typeof s.encode !== 'function' || typeof s.decode !== 'function')
|
|
12222
|
-
throw new Error(`wrong script=${s} (${typeof s})`);
|
|
12223
|
-
if (s.finalizeTaproot !== undefined && typeof s.finalizeTaproot !== 'function')
|
|
12224
|
-
throw new Error(`wrong script=${s} (${typeof s})`);
|
|
12225
|
-
}
|
|
12226
|
-
}
|
|
12227
|
-
return Object.freeze(_opts);
|
|
12228
|
-
}
|
|
12229
|
-
// NOTE: we cannot do this inside PSBTInput coder, because there is no index/txid at this point!
|
|
12230
|
-
function validateInput(i) {
|
|
12231
|
-
if (i.nonWitnessUtxo && i.index !== undefined) {
|
|
12232
|
-
const last = i.nonWitnessUtxo.outputs.length - 1;
|
|
12233
|
-
if (i.index > last)
|
|
12234
|
-
throw new Error(`validateInput: index(${i.index}) not in nonWitnessUtxo`);
|
|
12235
|
-
const prevOut = i.nonWitnessUtxo.outputs[i.index];
|
|
12236
|
-
if (i.witnessUtxo &&
|
|
12237
|
-
(!equalBytes(i.witnessUtxo.script, prevOut.script) || i.witnessUtxo.amount !== prevOut.amount))
|
|
12238
|
-
throw new Error('validateInput: witnessUtxo different from nonWitnessUtxo');
|
|
12239
|
-
if (i.txid) {
|
|
12240
|
-
const outputs = i.nonWitnessUtxo.outputs;
|
|
12241
|
-
if (outputs.length - 1 < i.index)
|
|
12242
|
-
throw new Error('nonWitnessUtxo: incorect output index');
|
|
12243
|
-
// At this point, we are using previous tx output to create new input.
|
|
12244
|
-
// Script safety checks are unnecessary:
|
|
12245
|
-
// - User has no control over previous tx. If somebody send money in same tx
|
|
12246
|
-
// as unspendable output, we still want user able to spend money
|
|
12247
|
-
// - We still want some checks to notify user about possible errors early
|
|
12248
|
-
// in case user wants to use wrong input by mistake
|
|
12249
|
-
// - Worst case: tx will be rejected by nodes. Still better than disallowing user
|
|
12250
|
-
// to spend real input, no matter how broken it looks
|
|
12251
|
-
const tx = Transaction.fromRaw(RawTx.encode(i.nonWitnessUtxo), {
|
|
12252
|
-
allowUnknownOutputs: true,
|
|
12253
|
-
disableScriptCheck: true,
|
|
12254
|
-
allowUnknownInputs: true,
|
|
12255
|
-
});
|
|
12256
|
-
const txid = hex.encode(i.txid);
|
|
12257
|
-
// PSBTv2 vectors have non-final tx in inputs
|
|
12258
|
-
if (tx.isFinal && tx.id !== txid)
|
|
12259
|
-
throw new Error(`nonWitnessUtxo: wrong txid, exp=${txid} got=${tx.id}`);
|
|
12260
|
-
}
|
|
12261
|
-
}
|
|
12262
|
-
return i;
|
|
12263
|
-
}
|
|
12264
|
-
// Normalizes input
|
|
12265
|
-
function getPrevOut(input) {
|
|
12266
|
-
if (input.nonWitnessUtxo) {
|
|
12267
|
-
if (input.index === undefined)
|
|
12268
|
-
throw new Error('Unknown input index');
|
|
12269
|
-
return input.nonWitnessUtxo.outputs[input.index];
|
|
12270
|
-
}
|
|
12271
|
-
else if (input.witnessUtxo)
|
|
12272
|
-
return input.witnessUtxo;
|
|
12273
|
-
else
|
|
12274
|
-
throw new Error('Cannot find previous output info');
|
|
12275
|
-
}
|
|
12276
|
-
function normalizeInput(i, cur, allowedFields, disableScriptCheck = false, allowUnknown = false) {
|
|
12277
|
-
let { nonWitnessUtxo, txid } = i;
|
|
12278
|
-
// String support for common fields. We usually prefer Uint8Array to avoid errors
|
|
12279
|
-
// like hex looking string accidentally passed, however, in case of nonWitnessUtxo
|
|
12280
|
-
// it is better to expect string, since constructing this complex object will be
|
|
12281
|
-
// difficult for user
|
|
12282
|
-
if (typeof nonWitnessUtxo === 'string')
|
|
12283
|
-
nonWitnessUtxo = hex.decode(nonWitnessUtxo);
|
|
12284
|
-
if (isBytes(nonWitnessUtxo))
|
|
12285
|
-
nonWitnessUtxo = RawTx.decode(nonWitnessUtxo);
|
|
12286
|
-
if (!('nonWitnessUtxo' in i) && nonWitnessUtxo === undefined)
|
|
12287
|
-
nonWitnessUtxo = cur?.nonWitnessUtxo;
|
|
12288
|
-
if (typeof txid === 'string')
|
|
12289
|
-
txid = hex.decode(txid);
|
|
12290
|
-
// TODO: if we have nonWitnessUtxo, we can extract txId from here
|
|
12291
|
-
if (txid === undefined)
|
|
12292
|
-
txid = cur?.txid;
|
|
12293
|
-
let res = { ...cur, ...i, nonWitnessUtxo, txid };
|
|
12294
|
-
if (!('nonWitnessUtxo' in i) && res.nonWitnessUtxo === undefined)
|
|
12295
|
-
delete res.nonWitnessUtxo;
|
|
12296
|
-
if (res.sequence === undefined)
|
|
12297
|
-
res.sequence = DEFAULT_SEQUENCE;
|
|
12298
|
-
if (res.tapMerkleRoot === null)
|
|
12299
|
-
delete res.tapMerkleRoot;
|
|
12300
|
-
res = mergeKeyMap(PSBTInput, res, cur, allowedFields, allowUnknown);
|
|
12301
|
-
PSBTInputCoder.encode(res); // Validates that everything is correct at this point
|
|
12302
|
-
let prevOut;
|
|
12303
|
-
if (res.nonWitnessUtxo && res.index !== undefined)
|
|
12304
|
-
prevOut = res.nonWitnessUtxo.outputs[res.index];
|
|
12305
|
-
else if (res.witnessUtxo)
|
|
12306
|
-
prevOut = res.witnessUtxo;
|
|
12307
|
-
if (prevOut && !disableScriptCheck)
|
|
12308
|
-
checkScript(prevOut && prevOut.script, res.redeemScript, res.witnessScript);
|
|
12309
|
-
return res;
|
|
12310
|
-
}
|
|
12311
|
-
function getInputType(input, allowLegacyWitnessUtxo = false) {
|
|
12312
|
-
let txType = 'legacy';
|
|
12313
|
-
let defaultSighash = SignatureHash.ALL;
|
|
12314
|
-
const prevOut = getPrevOut(input);
|
|
12315
|
-
const first = OutScript.decode(prevOut.script);
|
|
12316
|
-
let type = first.type;
|
|
12317
|
-
let cur = first;
|
|
12318
|
-
const stack = [first];
|
|
12319
|
-
if (first.type === 'tr') {
|
|
12320
|
-
defaultSighash = SignatureHash.DEFAULT;
|
|
12321
|
-
return {
|
|
12322
|
-
txType: 'taproot',
|
|
12323
|
-
type: 'tr',
|
|
12324
|
-
last: first,
|
|
12325
|
-
lastScript: prevOut.script,
|
|
12326
|
-
defaultSighash,
|
|
12327
|
-
sighash: input.sighashType || defaultSighash,
|
|
12328
|
-
};
|
|
12329
|
-
}
|
|
12330
|
-
else {
|
|
12331
|
-
if (first.type === 'wpkh' || first.type === 'wsh')
|
|
12332
|
-
txType = 'segwit';
|
|
12333
|
-
if (first.type === 'sh') {
|
|
12334
|
-
if (!input.redeemScript)
|
|
12335
|
-
throw new Error('inputType: sh without redeemScript');
|
|
12336
|
-
let child = OutScript.decode(input.redeemScript);
|
|
12337
|
-
if (child.type === 'wpkh' || child.type === 'wsh')
|
|
12338
|
-
txType = 'segwit';
|
|
12339
|
-
stack.push(child);
|
|
12340
|
-
cur = child;
|
|
12341
|
-
type += `-${child.type}`;
|
|
12342
|
-
}
|
|
12343
|
-
// wsh can be inside sh
|
|
12344
|
-
if (cur.type === 'wsh') {
|
|
12345
|
-
if (!input.witnessScript)
|
|
12346
|
-
throw new Error('inputType: wsh without witnessScript');
|
|
12347
|
-
let child = OutScript.decode(input.witnessScript);
|
|
12348
|
-
if (child.type === 'wsh')
|
|
12349
|
-
txType = 'segwit';
|
|
12350
|
-
stack.push(child);
|
|
12351
|
-
cur = child;
|
|
12352
|
-
type += `-${child.type}`;
|
|
12353
|
-
}
|
|
12354
|
-
const last = stack[stack.length - 1];
|
|
12355
|
-
if (last.type === 'sh' || last.type === 'wsh')
|
|
12356
|
-
throw new Error('inputType: sh/wsh cannot be terminal type');
|
|
12357
|
-
const lastScript = OutScript.encode(last);
|
|
12358
|
-
const res = {
|
|
12359
|
-
type,
|
|
12360
|
-
txType,
|
|
12361
|
-
last,
|
|
12362
|
-
lastScript,
|
|
12363
|
-
defaultSighash,
|
|
12364
|
-
sighash: input.sighashType || defaultSighash,
|
|
12365
|
-
};
|
|
12366
|
-
if (txType === 'legacy' && !allowLegacyWitnessUtxo && !input.nonWitnessUtxo) {
|
|
12367
|
-
throw new Error(`Transaction/sign: legacy input without nonWitnessUtxo, can result in attack that forces paying higher fees. Pass allowLegacyWitnessUtxo=true, if you sure`);
|
|
12368
|
-
}
|
|
12369
|
-
return res;
|
|
12370
|
-
}
|
|
12371
|
-
}
|
|
12372
|
-
class Transaction {
|
|
12373
|
-
constructor(opts = {}) {
|
|
12374
|
-
this.global = {};
|
|
12375
|
-
this.inputs = []; // use getInput()
|
|
12376
|
-
this.outputs = []; // use getOutput()
|
|
12377
|
-
const _opts = (this.opts = validateOpts(opts));
|
|
12378
|
-
// Merge with global structure of PSBTv2
|
|
12379
|
-
if (_opts.lockTime !== DEFAULT_LOCKTIME)
|
|
12380
|
-
this.global.fallbackLocktime = _opts.lockTime;
|
|
12381
|
-
this.global.txVersion = _opts.version;
|
|
12382
|
-
}
|
|
12383
|
-
// Import
|
|
12384
|
-
static fromRaw(raw, opts = {}) {
|
|
12385
|
-
const parsed = RawTx.decode(raw);
|
|
12386
|
-
const tx = new Transaction({ ...opts, version: parsed.version, lockTime: parsed.lockTime });
|
|
12387
|
-
for (const o of parsed.outputs)
|
|
12388
|
-
tx.addOutput(o);
|
|
12389
|
-
tx.outputs = parsed.outputs;
|
|
12390
|
-
tx.inputs = parsed.inputs;
|
|
12391
|
-
if (parsed.witnesses) {
|
|
12392
|
-
for (let i = 0; i < parsed.witnesses.length; i++)
|
|
12393
|
-
tx.inputs[i].finalScriptWitness = parsed.witnesses[i];
|
|
12394
|
-
}
|
|
12395
|
-
return tx;
|
|
12396
|
-
}
|
|
12397
|
-
// PSBT
|
|
12398
|
-
static fromPSBT(psbt_, opts = {}) {
|
|
12399
|
-
let parsed;
|
|
12400
|
-
try {
|
|
12401
|
-
parsed = RawPSBTV0.decode(psbt_);
|
|
12402
|
-
}
|
|
12403
|
-
catch (e0) {
|
|
12404
|
-
try {
|
|
12405
|
-
parsed = RawPSBTV2.decode(psbt_);
|
|
12406
|
-
}
|
|
12407
|
-
catch (e2) {
|
|
12408
|
-
// Throw error for v0 parsing, since it popular, otherwise it would be shadowed by v2 error
|
|
12409
|
-
throw e0;
|
|
12410
|
-
}
|
|
12411
|
-
}
|
|
12412
|
-
const PSBTVersion = parsed.global.version || 0;
|
|
12413
|
-
if (PSBTVersion !== 0 && PSBTVersion !== 2)
|
|
12414
|
-
throw new Error(`Wrong PSBT version=${PSBTVersion}`);
|
|
12415
|
-
const unsigned = parsed.global.unsignedTx;
|
|
12416
|
-
const version = PSBTVersion === 0 ? unsigned?.version : parsed.global.txVersion;
|
|
12417
|
-
const lockTime = PSBTVersion === 0 ? unsigned?.lockTime : parsed.global.fallbackLocktime;
|
|
12418
|
-
const tx = new Transaction({ ...opts, version, lockTime, PSBTVersion });
|
|
12419
|
-
// We need slice here, because otherwise
|
|
12420
|
-
const inputCount = PSBTVersion === 0 ? unsigned?.inputs.length : parsed.global.inputCount;
|
|
12421
|
-
tx.inputs = parsed.inputs.slice(0, inputCount).map((i, j) => validateInput({
|
|
12422
|
-
finalScriptSig: EMPTY,
|
|
12423
|
-
...parsed.global.unsignedTx?.inputs[j],
|
|
12424
|
-
...i,
|
|
12425
|
-
}));
|
|
12426
|
-
const outputCount = PSBTVersion === 0 ? unsigned?.outputs.length : parsed.global.outputCount;
|
|
12427
|
-
tx.outputs = parsed.outputs.slice(0, outputCount).map((i, j) => ({
|
|
12428
|
-
...i,
|
|
12429
|
-
...parsed.global.unsignedTx?.outputs[j],
|
|
12430
|
-
}));
|
|
12431
|
-
tx.global = { ...parsed.global, txVersion: version }; // just in case proprietary/unknown fields
|
|
12432
|
-
if (lockTime !== DEFAULT_LOCKTIME)
|
|
12433
|
-
tx.global.fallbackLocktime = lockTime;
|
|
12434
|
-
return tx;
|
|
12435
|
-
}
|
|
12436
|
-
toPSBT(PSBTVersion = this.opts.PSBTVersion) {
|
|
12437
|
-
if (PSBTVersion !== 0 && PSBTVersion !== 2)
|
|
12438
|
-
throw new Error(`Wrong PSBT version=${PSBTVersion}`);
|
|
12439
|
-
// if (PSBTVersion === 0 && this.inputs.length === 0) {
|
|
12440
|
-
// throw new Error(
|
|
12441
|
-
// 'PSBT version=0 export for transaction without inputs disabled, please use version=2. Please check `toPSBT` method for explanation.'
|
|
12442
|
-
// );
|
|
12443
|
-
// }
|
|
12444
|
-
const inputs = this.inputs.map((i) => validateInput(cleanPSBTFields(PSBTVersion, PSBTInput, i)));
|
|
12445
|
-
for (const inp of inputs) {
|
|
12446
|
-
// Don't serialize empty fields
|
|
12447
|
-
if (inp.partialSig && !inp.partialSig.length)
|
|
12448
|
-
delete inp.partialSig;
|
|
12449
|
-
if (inp.finalScriptSig && !inp.finalScriptSig.length)
|
|
12450
|
-
delete inp.finalScriptSig;
|
|
12451
|
-
if (inp.finalScriptWitness && !inp.finalScriptWitness.length)
|
|
12452
|
-
delete inp.finalScriptWitness;
|
|
12453
|
-
}
|
|
12454
|
-
const outputs = this.outputs.map((i) => cleanPSBTFields(PSBTVersion, PSBTOutput, i));
|
|
12455
|
-
const global = { ...this.global };
|
|
12456
|
-
if (PSBTVersion === 0) {
|
|
12457
|
-
/*
|
|
12458
|
-
- Bitcoin raw transaction expects to have at least 1 input because it uses case with zero inputs as marker for SegWit
|
|
12459
|
-
- this means we cannot serialize raw tx with zero inputs since it will be parsed as SegWit tx
|
|
12460
|
-
- Parsing of PSBTv0 depends on unsignedTx (it looks for input count here)
|
|
12461
|
-
- BIP-174 requires old serialization format (without witnesses) inside global, which solves this
|
|
12462
|
-
*/
|
|
12463
|
-
global.unsignedTx = RawOldTx.decode(RawOldTx.encode({
|
|
12464
|
-
version: this.version,
|
|
12465
|
-
lockTime: this.lockTime,
|
|
12466
|
-
inputs: this.inputs.map(inputBeforeSign).map((i) => ({
|
|
12467
|
-
...i,
|
|
12468
|
-
finalScriptSig: EMPTY,
|
|
12469
|
-
})),
|
|
12470
|
-
outputs: this.outputs.map(outputBeforeSign),
|
|
12471
|
-
}));
|
|
12472
|
-
delete global.fallbackLocktime;
|
|
12473
|
-
delete global.txVersion;
|
|
12474
|
-
}
|
|
12475
|
-
else {
|
|
12476
|
-
global.version = PSBTVersion;
|
|
12477
|
-
global.txVersion = this.version;
|
|
12478
|
-
global.inputCount = this.inputs.length;
|
|
12479
|
-
global.outputCount = this.outputs.length;
|
|
12480
|
-
if (global.fallbackLocktime && global.fallbackLocktime === DEFAULT_LOCKTIME)
|
|
12481
|
-
delete global.fallbackLocktime;
|
|
12482
|
-
}
|
|
12483
|
-
if (this.opts.bip174jsCompat) {
|
|
12484
|
-
if (!inputs.length)
|
|
12485
|
-
inputs.push({});
|
|
12486
|
-
if (!outputs.length)
|
|
12487
|
-
outputs.push({});
|
|
12488
|
-
}
|
|
12489
|
-
return (PSBTVersion === 0 ? RawPSBTV0 : RawPSBTV2).encode({
|
|
12490
|
-
global,
|
|
12491
|
-
inputs,
|
|
12492
|
-
outputs,
|
|
12493
|
-
});
|
|
12494
|
-
}
|
|
12495
|
-
// BIP370 lockTime (https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#determining-lock-time)
|
|
12496
|
-
get lockTime() {
|
|
12497
|
-
let height = DEFAULT_LOCKTIME;
|
|
12498
|
-
let heightCnt = 0;
|
|
12499
|
-
let time = DEFAULT_LOCKTIME;
|
|
12500
|
-
let timeCnt = 0;
|
|
12501
|
-
for (const i of this.inputs) {
|
|
12502
|
-
if (i.requiredHeightLocktime) {
|
|
12503
|
-
height = Math.max(height, i.requiredHeightLocktime);
|
|
12504
|
-
heightCnt++;
|
|
12505
|
-
}
|
|
12506
|
-
if (i.requiredTimeLocktime) {
|
|
12507
|
-
time = Math.max(time, i.requiredTimeLocktime);
|
|
12508
|
-
timeCnt++;
|
|
12509
|
-
}
|
|
12510
|
-
}
|
|
12511
|
-
if (heightCnt && heightCnt >= timeCnt)
|
|
12512
|
-
return height;
|
|
12513
|
-
if (time !== DEFAULT_LOCKTIME)
|
|
12514
|
-
return time;
|
|
12515
|
-
return this.global.fallbackLocktime || DEFAULT_LOCKTIME;
|
|
12516
|
-
}
|
|
12517
|
-
get version() {
|
|
12518
|
-
// Should be not possible
|
|
12519
|
-
if (this.global.txVersion === undefined)
|
|
12520
|
-
throw new Error('No global.txVersion');
|
|
12521
|
-
return this.global.txVersion;
|
|
12522
|
-
}
|
|
12523
|
-
inputStatus(idx) {
|
|
12524
|
-
this.checkInputIdx(idx);
|
|
12525
|
-
const input = this.inputs[idx];
|
|
12526
|
-
// Finalized
|
|
12527
|
-
if (input.finalScriptSig && input.finalScriptSig.length)
|
|
12528
|
-
return 'finalized';
|
|
12529
|
-
if (input.finalScriptWitness && input.finalScriptWitness.length)
|
|
12530
|
-
return 'finalized';
|
|
12531
|
-
// Signed taproot
|
|
12532
|
-
if (input.tapKeySig)
|
|
12533
|
-
return 'signed';
|
|
12534
|
-
if (input.tapScriptSig && input.tapScriptSig.length)
|
|
12535
|
-
return 'signed';
|
|
12536
|
-
// Signed
|
|
12537
|
-
if (input.partialSig && input.partialSig.length)
|
|
12538
|
-
return 'signed';
|
|
12539
|
-
return 'unsigned';
|
|
12540
|
-
}
|
|
12541
|
-
// Cannot replace unpackSighash, tests rely on very generic implemenetation with signing inputs outside of range
|
|
12542
|
-
// We will lose some vectors -> smaller test coverage of preimages (very important!)
|
|
12543
|
-
inputSighash(idx) {
|
|
12544
|
-
this.checkInputIdx(idx);
|
|
12545
|
-
const inputSighash = this.inputs[idx].sighashType;
|
|
12546
|
-
const sighash = inputSighash === undefined ? SignatureHash.DEFAULT : inputSighash;
|
|
12547
|
-
// ALL or DEFAULT -- everything signed
|
|
12548
|
-
// NONE -- all inputs + no outputs
|
|
12549
|
-
// SINGLE -- all inputs + output with same index
|
|
12550
|
-
// ALL + ANYONE -- specific input + all outputs
|
|
12551
|
-
// NONE + ANYONE -- specific input + no outputs
|
|
12552
|
-
// SINGLE -- specific inputs + output with same index
|
|
12553
|
-
const sigOutputs = sighash === SignatureHash.DEFAULT ? SignatureHash.ALL : sighash & 0b11;
|
|
12554
|
-
const sigInputs = sighash & SignatureHash.ANYONECANPAY;
|
|
12555
|
-
return { sigInputs, sigOutputs };
|
|
12556
|
-
}
|
|
12557
|
-
// Very nice for debug purposes, but slow. If there is too much inputs/outputs to add, will be quadratic.
|
|
12558
|
-
// Some cache will be nice, but there chance to have bugs with cache invalidation
|
|
12559
|
-
signStatus() {
|
|
12560
|
-
// if addInput or addOutput is not possible, then all inputs or outputs are signed
|
|
12561
|
-
let addInput = true, addOutput = true;
|
|
12562
|
-
let inputs = [], outputs = [];
|
|
12563
|
-
for (let idx = 0; idx < this.inputs.length; idx++) {
|
|
12564
|
-
const status = this.inputStatus(idx);
|
|
12565
|
-
// Unsigned input doesn't affect anything
|
|
12566
|
-
if (status === 'unsigned')
|
|
12567
|
-
continue;
|
|
12568
|
-
const { sigInputs, sigOutputs } = this.inputSighash(idx);
|
|
12569
|
-
// Input type
|
|
12570
|
-
if (sigInputs === SignatureHash.ANYONECANPAY)
|
|
12571
|
-
inputs.push(idx);
|
|
12572
|
-
else
|
|
12573
|
-
addInput = false;
|
|
12574
|
-
// Output type
|
|
12575
|
-
if (sigOutputs === SignatureHash.ALL)
|
|
12576
|
-
addOutput = false;
|
|
12577
|
-
else if (sigOutputs === SignatureHash.SINGLE)
|
|
12578
|
-
outputs.push(idx);
|
|
12579
|
-
else if (sigOutputs === SignatureHash.NONE) ;
|
|
12580
|
-
else
|
|
12581
|
-
throw new Error(`Wrong signature hash output type: ${sigOutputs}`);
|
|
12582
|
-
}
|
|
12583
|
-
return { addInput, addOutput, inputs, outputs };
|
|
12584
|
-
}
|
|
12585
|
-
get isFinal() {
|
|
12586
|
-
for (let idx = 0; idx < this.inputs.length; idx++)
|
|
12587
|
-
if (this.inputStatus(idx) !== 'finalized')
|
|
12588
|
-
return false;
|
|
12589
|
-
return true;
|
|
12590
|
-
}
|
|
12591
|
-
// Info utils
|
|
12592
|
-
get hasWitnesses() {
|
|
12593
|
-
let out = false;
|
|
12594
|
-
for (const i of this.inputs)
|
|
12595
|
-
if (i.finalScriptWitness && i.finalScriptWitness.length)
|
|
12596
|
-
out = true;
|
|
12597
|
-
return out;
|
|
12598
|
-
}
|
|
12599
|
-
// https://en.bitcoin.it/wiki/Weight_units
|
|
12600
|
-
get weight() {
|
|
12601
|
-
if (!this.isFinal)
|
|
12602
|
-
throw new Error('Transaction is not finalized');
|
|
12603
|
-
let out = 32;
|
|
12604
|
-
// Outputs
|
|
12605
|
-
const outputs = this.outputs.map(outputBeforeSign);
|
|
12606
|
-
out += 4 * CompactSizeLen.encode(this.outputs.length).length;
|
|
12607
|
-
for (const o of outputs)
|
|
12608
|
-
out += 32 + 4 * VarBytes.encode(o.script).length;
|
|
12609
|
-
// Inputs
|
|
12610
|
-
if (this.hasWitnesses)
|
|
12611
|
-
out += 2;
|
|
12612
|
-
out += 4 * CompactSizeLen.encode(this.inputs.length).length;
|
|
12613
|
-
for (const i of this.inputs) {
|
|
12614
|
-
out += 160 + 4 * VarBytes.encode(i.finalScriptSig || EMPTY).length;
|
|
12615
|
-
if (this.hasWitnesses && i.finalScriptWitness)
|
|
12616
|
-
out += RawWitness.encode(i.finalScriptWitness).length;
|
|
12617
|
-
}
|
|
12618
|
-
return out;
|
|
12619
|
-
}
|
|
12620
|
-
get vsize() {
|
|
12621
|
-
return toVsize(this.weight);
|
|
12622
|
-
}
|
|
12623
|
-
toBytes(withScriptSig = false, withWitness = false) {
|
|
12624
|
-
return RawTx.encode({
|
|
12625
|
-
version: this.version,
|
|
12626
|
-
lockTime: this.lockTime,
|
|
12627
|
-
inputs: this.inputs.map(inputBeforeSign).map((i) => ({
|
|
12628
|
-
...i,
|
|
12629
|
-
finalScriptSig: (withScriptSig && i.finalScriptSig) || EMPTY,
|
|
12630
|
-
})),
|
|
12631
|
-
outputs: this.outputs.map(outputBeforeSign),
|
|
12632
|
-
witnesses: this.inputs.map((i) => i.finalScriptWitness || []),
|
|
12633
|
-
segwitFlag: withWitness && this.hasWitnesses,
|
|
12634
|
-
});
|
|
12635
|
-
}
|
|
12636
|
-
get unsignedTx() {
|
|
12637
|
-
return this.toBytes(false, false);
|
|
12638
|
-
}
|
|
12639
|
-
get hex() {
|
|
12640
|
-
return hex.encode(this.toBytes(true, this.hasWitnesses));
|
|
12641
|
-
}
|
|
12642
|
-
get hash() {
|
|
12643
|
-
if (!this.isFinal)
|
|
12644
|
-
throw new Error('Transaction is not finalized');
|
|
12645
|
-
return hex.encode(sha256x2(this.toBytes(true)));
|
|
12646
|
-
}
|
|
12647
|
-
get id() {
|
|
12648
|
-
if (!this.isFinal)
|
|
12649
|
-
throw new Error('Transaction is not finalized');
|
|
12650
|
-
return hex.encode(sha256x2(this.toBytes(true)).reverse());
|
|
12651
|
-
}
|
|
12652
|
-
// Input stuff
|
|
12653
|
-
checkInputIdx(idx) {
|
|
12654
|
-
if (!Number.isSafeInteger(idx) || 0 > idx || idx >= this.inputs.length)
|
|
12655
|
-
throw new Error(`Wrong input index=${idx}`);
|
|
12656
|
-
}
|
|
12657
|
-
getInput(idx) {
|
|
12658
|
-
this.checkInputIdx(idx);
|
|
12659
|
-
return cloneDeep(this.inputs[idx]);
|
|
12660
|
-
}
|
|
12661
|
-
get inputsLength() {
|
|
12662
|
-
return this.inputs.length;
|
|
12663
|
-
}
|
|
12664
|
-
// Modification
|
|
12665
|
-
addInput(input, _ignoreSignStatus = false) {
|
|
12666
|
-
if (!_ignoreSignStatus && !this.signStatus().addInput)
|
|
12667
|
-
throw new Error('Tx has signed inputs, cannot add new one');
|
|
12668
|
-
this.inputs.push(normalizeInput(input, undefined, undefined, this.opts.disableScriptCheck));
|
|
12669
|
-
return this.inputs.length - 1;
|
|
12670
|
-
}
|
|
12671
|
-
updateInput(idx, input, _ignoreSignStatus = false) {
|
|
12672
|
-
this.checkInputIdx(idx);
|
|
12673
|
-
let allowedFields = undefined;
|
|
12674
|
-
if (!_ignoreSignStatus) {
|
|
12675
|
-
const status = this.signStatus();
|
|
12676
|
-
if (!status.addInput || status.inputs.includes(idx))
|
|
12677
|
-
allowedFields = PSBTInputUnsignedKeys;
|
|
12678
|
-
}
|
|
12679
|
-
this.inputs[idx] = normalizeInput(input, this.inputs[idx], allowedFields, this.opts.disableScriptCheck, this.opts.allowUnknown);
|
|
12680
|
-
}
|
|
12681
|
-
// Output stuff
|
|
12682
|
-
checkOutputIdx(idx) {
|
|
12683
|
-
if (!Number.isSafeInteger(idx) || 0 > idx || idx >= this.outputs.length)
|
|
12684
|
-
throw new Error(`Wrong output index=${idx}`);
|
|
12685
|
-
}
|
|
12686
|
-
getOutput(idx) {
|
|
12687
|
-
this.checkOutputIdx(idx);
|
|
12688
|
-
return cloneDeep(this.outputs[idx]);
|
|
12689
|
-
}
|
|
12690
|
-
getOutputAddress(idx, network = NETWORK) {
|
|
12691
|
-
const out = this.getOutput(idx);
|
|
12692
|
-
if (!out.script)
|
|
12693
|
-
return;
|
|
12694
|
-
return Address(network).encode(OutScript.decode(out.script));
|
|
12695
|
-
}
|
|
12696
|
-
get outputsLength() {
|
|
12697
|
-
return this.outputs.length;
|
|
12698
|
-
}
|
|
12699
|
-
normalizeOutput(o, cur, allowedFields) {
|
|
12700
|
-
let { amount, script } = o;
|
|
12701
|
-
if (amount === undefined)
|
|
12702
|
-
amount = cur?.amount;
|
|
12703
|
-
if (typeof amount !== 'bigint')
|
|
12704
|
-
throw new Error(`Wrong amount type, should be of type bigint in sats, but got ${amount} of type ${typeof amount}`);
|
|
12705
|
-
if (typeof script === 'string')
|
|
12706
|
-
script = hex.decode(script);
|
|
12707
|
-
if (script === undefined)
|
|
12708
|
-
script = cur?.script;
|
|
12709
|
-
let res = { ...cur, ...o, amount, script };
|
|
12710
|
-
if (res.amount === undefined)
|
|
12711
|
-
delete res.amount;
|
|
12712
|
-
res = mergeKeyMap(PSBTOutput, res, cur, allowedFields, this.opts.allowUnknown);
|
|
12713
|
-
PSBTOutputCoder.encode(res);
|
|
12714
|
-
if (res.script &&
|
|
12715
|
-
!this.opts.allowUnknownOutputs &&
|
|
12716
|
-
OutScript.decode(res.script).type === 'unknown') {
|
|
12717
|
-
throw new Error('Transaction/output: unknown output script type, there is a chance that input is unspendable. Pass allowUnknownOutputs=true, if you sure');
|
|
12718
|
-
}
|
|
12719
|
-
if (!this.opts.disableScriptCheck)
|
|
12720
|
-
checkScript(res.script, res.redeemScript, res.witnessScript);
|
|
12721
|
-
return res;
|
|
12722
|
-
}
|
|
12723
|
-
addOutput(o, _ignoreSignStatus = false) {
|
|
12724
|
-
if (!_ignoreSignStatus && !this.signStatus().addOutput)
|
|
12725
|
-
throw new Error('Tx has signed outputs, cannot add new one');
|
|
12726
|
-
this.outputs.push(this.normalizeOutput(o));
|
|
12727
|
-
return this.outputs.length - 1;
|
|
12728
|
-
}
|
|
12729
|
-
updateOutput(idx, output, _ignoreSignStatus = false) {
|
|
12730
|
-
this.checkOutputIdx(idx);
|
|
12731
|
-
let allowedFields = undefined;
|
|
12732
|
-
if (!_ignoreSignStatus) {
|
|
12733
|
-
const status = this.signStatus();
|
|
12734
|
-
if (!status.addOutput || status.outputs.includes(idx))
|
|
12735
|
-
allowedFields = PSBTOutputUnsignedKeys;
|
|
12736
|
-
}
|
|
12737
|
-
this.outputs[idx] = this.normalizeOutput(output, this.outputs[idx], allowedFields);
|
|
12738
|
-
}
|
|
12739
|
-
addOutputAddress(address, amount, network = NETWORK) {
|
|
12740
|
-
return this.addOutput({ script: OutScript.encode(Address(network).decode(address)), amount });
|
|
12741
|
-
}
|
|
12742
|
-
// Utils
|
|
12743
|
-
get fee() {
|
|
12744
|
-
let res = 0n;
|
|
12745
|
-
for (const i of this.inputs) {
|
|
12746
|
-
const prevOut = getPrevOut(i);
|
|
12747
|
-
if (!prevOut)
|
|
12748
|
-
throw new Error('Empty input amount');
|
|
12749
|
-
res += prevOut.amount;
|
|
12750
|
-
}
|
|
12751
|
-
const outputs = this.outputs.map(outputBeforeSign);
|
|
12752
|
-
for (const o of outputs)
|
|
12753
|
-
res -= o.amount;
|
|
12754
|
-
return res;
|
|
12755
|
-
}
|
|
12756
|
-
// Signing
|
|
12757
|
-
// Based on https://github.com/bitcoin/bitcoin/blob/5871b5b5ab57a0caf9b7514eb162c491c83281d5/test/functional/test_framework/script.py#L624
|
|
12758
|
-
// There is optimization opportunity to re-use hashes for multiple inputs for witness v0/v1,
|
|
12759
|
-
// but we are trying to be less complicated for audit purpose for now.
|
|
12760
|
-
preimageLegacy(idx, prevOutScript, hashType) {
|
|
12761
|
-
const { isAny, isNone, isSingle } = unpackSighash(hashType);
|
|
12762
|
-
if (idx < 0 || !Number.isSafeInteger(idx))
|
|
12763
|
-
throw new Error(`Invalid input idx=${idx}`);
|
|
12764
|
-
if ((isSingle && idx >= this.outputs.length) || idx >= this.inputs.length)
|
|
12765
|
-
return U256BE.encode(1n);
|
|
12766
|
-
prevOutScript = Script.encode(Script.decode(prevOutScript).filter((i) => i !== 'CODESEPARATOR'));
|
|
12767
|
-
let inputs = this.inputs
|
|
12768
|
-
.map(inputBeforeSign)
|
|
12769
|
-
.map((input, inputIdx) => ({
|
|
12770
|
-
...input,
|
|
12771
|
-
finalScriptSig: inputIdx === idx ? prevOutScript : EMPTY,
|
|
12772
|
-
}));
|
|
12773
|
-
if (isAny)
|
|
12774
|
-
inputs = [inputs[idx]];
|
|
12775
|
-
else if (isNone || isSingle) {
|
|
12776
|
-
inputs = inputs.map((input, inputIdx) => ({
|
|
12777
|
-
...input,
|
|
12778
|
-
sequence: inputIdx === idx ? input.sequence : 0,
|
|
12779
|
-
}));
|
|
12780
|
-
}
|
|
12781
|
-
let outputs = this.outputs.map(outputBeforeSign);
|
|
12782
|
-
if (isNone)
|
|
12783
|
-
outputs = [];
|
|
12784
|
-
else if (isSingle) {
|
|
12785
|
-
outputs = outputs.slice(0, idx).fill(EMPTY_OUTPUT).concat([outputs[idx]]);
|
|
12786
|
-
}
|
|
12787
|
-
const tmpTx = RawTx.encode({
|
|
12788
|
-
lockTime: this.lockTime,
|
|
12789
|
-
version: this.version,
|
|
12790
|
-
segwitFlag: false,
|
|
12791
|
-
inputs,
|
|
12792
|
-
outputs,
|
|
12793
|
-
});
|
|
12794
|
-
return sha256x2(tmpTx, I32LE.encode(hashType));
|
|
12795
|
-
}
|
|
12796
|
-
preimageWitnessV0(idx, prevOutScript, hashType, amount) {
|
|
12797
|
-
const { isAny, isNone, isSingle } = unpackSighash(hashType);
|
|
12798
|
-
let inputHash = EMPTY32;
|
|
12799
|
-
let sequenceHash = EMPTY32;
|
|
12800
|
-
let outputHash = EMPTY32;
|
|
12801
|
-
const inputs = this.inputs.map(inputBeforeSign);
|
|
12802
|
-
const outputs = this.outputs.map(outputBeforeSign);
|
|
12803
|
-
if (!isAny)
|
|
12804
|
-
inputHash = sha256x2(...inputs.map(TxHashIdx.encode));
|
|
12805
|
-
if (!isAny && !isSingle && !isNone)
|
|
12806
|
-
sequenceHash = sha256x2(...inputs.map((i) => U32LE.encode(i.sequence)));
|
|
12807
|
-
if (!isSingle && !isNone) {
|
|
12808
|
-
outputHash = sha256x2(...outputs.map(RawOutput.encode));
|
|
12809
|
-
}
|
|
12810
|
-
else if (isSingle && idx < outputs.length)
|
|
12811
|
-
outputHash = sha256x2(RawOutput.encode(outputs[idx]));
|
|
12812
|
-
const input = inputs[idx];
|
|
12813
|
-
return sha256x2(I32LE.encode(this.version), inputHash, sequenceHash, createBytes(32, true).encode(input.txid), U32LE.encode(input.index), VarBytes.encode(prevOutScript), U64LE.encode(amount), U32LE.encode(input.sequence), outputHash, U32LE.encode(this.lockTime), U32LE.encode(hashType));
|
|
12814
|
-
}
|
|
12815
|
-
preimageWitnessV1(idx, prevOutScript, hashType, amount, codeSeparator = -1, leafScript, leafVer = 0xc0, annex) {
|
|
12816
|
-
if (!Array.isArray(amount) || this.inputs.length !== amount.length)
|
|
12817
|
-
throw new Error(`Invalid amounts array=${amount}`);
|
|
12818
|
-
if (!Array.isArray(prevOutScript) || this.inputs.length !== prevOutScript.length)
|
|
12819
|
-
throw new Error(`Invalid prevOutScript array=${prevOutScript}`);
|
|
12820
|
-
const out = [
|
|
12821
|
-
U8.encode(0),
|
|
12822
|
-
U8.encode(hashType), // U8 sigHash
|
|
12823
|
-
I32LE.encode(this.version),
|
|
12824
|
-
U32LE.encode(this.lockTime),
|
|
12825
|
-
];
|
|
12826
|
-
const outType = hashType === SignatureHash.DEFAULT ? SignatureHash.ALL : hashType & 0b11;
|
|
12827
|
-
const inType = hashType & SignatureHash.ANYONECANPAY;
|
|
12828
|
-
const inputs = this.inputs.map(inputBeforeSign);
|
|
12829
|
-
const outputs = this.outputs.map(outputBeforeSign);
|
|
12830
|
-
if (inType !== SignatureHash.ANYONECANPAY) {
|
|
12831
|
-
out.push(...[
|
|
12832
|
-
inputs.map(TxHashIdx.encode),
|
|
12833
|
-
amount.map(U64LE.encode),
|
|
12834
|
-
prevOutScript.map(VarBytes.encode),
|
|
12835
|
-
inputs.map((i) => U32LE.encode(i.sequence)),
|
|
12836
|
-
].map((i) => sha256$1(concatBytes(...i))));
|
|
12837
|
-
}
|
|
12838
|
-
if (outType === SignatureHash.ALL) {
|
|
12839
|
-
out.push(sha256$1(concatBytes(...outputs.map(RawOutput.encode))));
|
|
12840
|
-
}
|
|
12841
|
-
const spendType = (annex ? 1 : 0) | (leafScript ? 2 : 0);
|
|
12842
|
-
out.push(new Uint8Array([spendType]));
|
|
12843
|
-
if (inType === SignatureHash.ANYONECANPAY) {
|
|
12844
|
-
const inp = inputs[idx];
|
|
12845
|
-
out.push(TxHashIdx.encode(inp), U64LE.encode(amount[idx]), VarBytes.encode(prevOutScript[idx]), U32LE.encode(inp.sequence));
|
|
12846
|
-
}
|
|
12847
|
-
else
|
|
12848
|
-
out.push(U32LE.encode(idx));
|
|
12849
|
-
if (spendType & 1)
|
|
12850
|
-
out.push(sha256$1(VarBytes.encode(annex || EMPTY)));
|
|
12851
|
-
if (outType === SignatureHash.SINGLE)
|
|
12852
|
-
out.push(idx < outputs.length ? sha256$1(RawOutput.encode(outputs[idx])) : EMPTY32);
|
|
12853
|
-
if (leafScript)
|
|
12854
|
-
out.push(tapLeafHash(leafScript, leafVer), U8.encode(0), I32LE.encode(codeSeparator));
|
|
12855
|
-
return tagSchnorr('TapSighash', ...out);
|
|
12856
|
-
}
|
|
12857
|
-
// Signer can be privateKey OR instance of bip32 HD stuff
|
|
12858
|
-
signIdx(privateKey, idx, allowedSighash, _auxRand) {
|
|
12859
|
-
this.checkInputIdx(idx);
|
|
12860
|
-
const input = this.inputs[idx];
|
|
12861
|
-
const inputType = getInputType(input, this.opts.allowLegacyWitnessUtxo);
|
|
12862
|
-
// Handle BIP32 HDKey
|
|
12863
|
-
if (!isBytes(privateKey)) {
|
|
12864
|
-
if (!input.bip32Derivation || !input.bip32Derivation.length)
|
|
12865
|
-
throw new Error('bip32Derivation: empty');
|
|
12866
|
-
const signers = input.bip32Derivation
|
|
12867
|
-
.filter((i) => i[1].fingerprint == privateKey.fingerprint)
|
|
12868
|
-
.map(([pubKey, { path }]) => {
|
|
12869
|
-
let s = privateKey;
|
|
12870
|
-
for (const i of path)
|
|
12871
|
-
s = s.deriveChild(i);
|
|
12872
|
-
if (!equalBytes(s.publicKey, pubKey))
|
|
12873
|
-
throw new Error('bip32Derivation: wrong pubKey');
|
|
12874
|
-
if (!s.privateKey)
|
|
12875
|
-
throw new Error('bip32Derivation: no privateKey');
|
|
12876
|
-
return s;
|
|
12877
|
-
});
|
|
12878
|
-
if (!signers.length)
|
|
12879
|
-
throw new Error(`bip32Derivation: no items with fingerprint=${privateKey.fingerprint}`);
|
|
12880
|
-
let signed = false;
|
|
12881
|
-
for (const s of signers)
|
|
12882
|
-
if (this.signIdx(s.privateKey, idx))
|
|
12883
|
-
signed = true;
|
|
12884
|
-
return signed;
|
|
12885
|
-
}
|
|
12886
|
-
// Sighash checks
|
|
12887
|
-
// Just for compat with bitcoinjs-lib, so users won't face unexpected behaviour.
|
|
12888
|
-
if (!allowedSighash)
|
|
12889
|
-
allowedSighash = [inputType.defaultSighash];
|
|
12890
|
-
else
|
|
12891
|
-
allowedSighash.forEach(validateSigHash);
|
|
12892
|
-
const sighash = inputType.sighash;
|
|
12893
|
-
if (!allowedSighash.includes(sighash)) {
|
|
12894
|
-
throw new Error(`Input with not allowed sigHash=${sighash}. Allowed: ${allowedSighash.join(', ')}`);
|
|
12895
|
-
}
|
|
12896
|
-
// It is possible to sign these inputs for legacy/segwit v0 (but no taproot!),
|
|
12897
|
-
// however this was because of bug in bitcoin-core, which remains here because of consensus.
|
|
12898
|
-
// If this is absolutely neccessary for your case, please open issue.
|
|
12899
|
-
// We disable it to avoid complicated workflow where SINGLE will block adding new outputs
|
|
12900
|
-
const { sigOutputs } = this.inputSighash(idx);
|
|
12901
|
-
if (sigOutputs === SignatureHash.SINGLE && idx >= this.outputs.length) {
|
|
12902
|
-
throw new Error(`Input with sighash SINGLE, but there is no output with corresponding index=${idx}`);
|
|
12903
|
-
}
|
|
12904
|
-
// Actual signing
|
|
12905
|
-
// Taproot
|
|
12906
|
-
const prevOut = getPrevOut(input);
|
|
12907
|
-
if (inputType.txType === 'taproot') {
|
|
12908
|
-
const prevOuts = this.inputs.map(getPrevOut);
|
|
12909
|
-
const prevOutScript = prevOuts.map((i) => i.script);
|
|
12910
|
-
const amount = prevOuts.map((i) => i.amount);
|
|
12911
|
-
let signed = false;
|
|
12912
|
-
let schnorrPub = pubSchnorr(privateKey);
|
|
12913
|
-
let merkleRoot = input.tapMerkleRoot || EMPTY;
|
|
12914
|
-
if (input.tapInternalKey) {
|
|
12915
|
-
// internal + tweak = tweaked key
|
|
12916
|
-
// if internal key == current public key, we need to tweak private key,
|
|
12917
|
-
// otherwise sign as is. bitcoinjs implementation always wants tweaked
|
|
12918
|
-
// priv key to be provided
|
|
12919
|
-
const { pubKey, privKey } = getTaprootKeys(privateKey, schnorrPub, input.tapInternalKey, merkleRoot);
|
|
12920
|
-
const [taprootPubKey, _] = taprootTweakPubkey(input.tapInternalKey, merkleRoot);
|
|
12921
|
-
if (equalBytes(taprootPubKey, pubKey)) {
|
|
12922
|
-
const hash = this.preimageWitnessV1(idx, prevOutScript, sighash, amount);
|
|
12923
|
-
const sig = concatBytes(signSchnorr(hash, privKey, _auxRand), sighash !== SignatureHash.DEFAULT ? new Uint8Array([sighash]) : EMPTY);
|
|
12924
|
-
this.updateInput(idx, { tapKeySig: sig }, true);
|
|
12925
|
-
signed = true;
|
|
12926
|
-
}
|
|
12927
|
-
}
|
|
12928
|
-
if (input.tapLeafScript) {
|
|
12929
|
-
input.tapScriptSig = input.tapScriptSig || [];
|
|
12930
|
-
for (const [_, _script] of input.tapLeafScript) {
|
|
12931
|
-
const script = _script.subarray(0, -1);
|
|
12932
|
-
const scriptDecoded = Script.decode(script);
|
|
12933
|
-
const ver = _script[_script.length - 1];
|
|
12934
|
-
const hash = tapLeafHash(script, ver);
|
|
12935
|
-
// NOTE: no need to tweak internal key here, since we don't support nested p2tr
|
|
12936
|
-
const pos = scriptDecoded.findIndex((i) => isBytes(i) && equalBytes(i, schnorrPub));
|
|
12937
|
-
// Skip if there is no public key in tapLeafScript
|
|
12938
|
-
if (pos === -1)
|
|
12939
|
-
continue;
|
|
12940
|
-
const msg = this.preimageWitnessV1(idx, prevOutScript, sighash, amount, undefined, script, ver);
|
|
12941
|
-
const sig = concatBytes(signSchnorr(msg, privateKey, _auxRand), sighash !== SignatureHash.DEFAULT ? new Uint8Array([sighash]) : EMPTY);
|
|
12942
|
-
this.updateInput(idx, { tapScriptSig: [[{ pubKey: schnorrPub, leafHash: hash }, sig]] }, true);
|
|
12943
|
-
signed = true;
|
|
12944
|
-
}
|
|
12945
|
-
}
|
|
12946
|
-
if (!signed)
|
|
12947
|
-
throw new Error('No taproot scripts signed');
|
|
12948
|
-
return true;
|
|
12949
|
-
}
|
|
12950
|
-
else {
|
|
12951
|
-
// only compressed keys are supported for now
|
|
12952
|
-
const pubKey = pubECDSA(privateKey);
|
|
12953
|
-
// TODO: replace with explicit checks
|
|
12954
|
-
// Check if script has public key or its has inside
|
|
12955
|
-
let hasPubkey = false;
|
|
12956
|
-
const pubKeyHash = hash160(pubKey);
|
|
12957
|
-
for (const i of Script.decode(inputType.lastScript)) {
|
|
12958
|
-
if (isBytes(i) && (equalBytes(i, pubKey) || equalBytes(i, pubKeyHash)))
|
|
12959
|
-
hasPubkey = true;
|
|
12960
|
-
}
|
|
12961
|
-
if (!hasPubkey)
|
|
12962
|
-
throw new Error(`Input script doesn't have pubKey: ${inputType.lastScript}`);
|
|
12963
|
-
let hash;
|
|
12964
|
-
if (inputType.txType === 'legacy') {
|
|
12965
|
-
hash = this.preimageLegacy(idx, inputType.lastScript, sighash);
|
|
12966
|
-
}
|
|
12967
|
-
else if (inputType.txType === 'segwit') {
|
|
12968
|
-
let script = inputType.lastScript;
|
|
12969
|
-
// If wpkh OR sh-wpkh, wsh-wpkh is impossible, so looks ok
|
|
12970
|
-
if (inputType.last.type === 'wpkh')
|
|
12971
|
-
script = OutScript.encode({ type: 'pkh', hash: inputType.last.hash });
|
|
12972
|
-
hash = this.preimageWitnessV0(idx, script, sighash, prevOut.amount);
|
|
12973
|
-
}
|
|
12974
|
-
else
|
|
12975
|
-
throw new Error(`Transaction/sign: unknown tx type: ${inputType.txType}`);
|
|
12976
|
-
const sig = signECDSA(hash, privateKey, this.opts.lowR);
|
|
12977
|
-
this.updateInput(idx, {
|
|
12978
|
-
partialSig: [[pubKey, concatBytes(sig, new Uint8Array([sighash]))]],
|
|
12979
|
-
}, true);
|
|
12980
|
-
}
|
|
12981
|
-
return true;
|
|
12982
|
-
}
|
|
12983
|
-
// This is bad API. Will work if user creates and signs tx, but if
|
|
12984
|
-
// there is some complex workflow with exchanging PSBT and signing them,
|
|
12985
|
-
// then it is better to validate which output user signs. How could a better API look like?
|
|
12986
|
-
// Example: user adds input, sends to another party, then signs received input (mixer etc),
|
|
12987
|
-
// another user can add different input for same key and user will sign it.
|
|
12988
|
-
// Even worse: another user can add bip32 derivation, and spend money from different address.
|
|
12989
|
-
// Better api: signIdx
|
|
12990
|
-
sign(privateKey, allowedSighash, _auxRand) {
|
|
12991
|
-
let num = 0;
|
|
12992
|
-
for (let i = 0; i < this.inputs.length; i++) {
|
|
12993
|
-
try {
|
|
12994
|
-
if (this.signIdx(privateKey, i, allowedSighash, _auxRand))
|
|
12995
|
-
num++;
|
|
12996
|
-
}
|
|
12997
|
-
catch (e) { }
|
|
12998
|
-
}
|
|
12999
|
-
if (!num)
|
|
13000
|
-
throw new Error('No inputs signed');
|
|
13001
|
-
return num;
|
|
13002
|
-
}
|
|
13003
|
-
finalizeIdx(idx) {
|
|
13004
|
-
this.checkInputIdx(idx);
|
|
13005
|
-
if (this.fee < 0n)
|
|
13006
|
-
throw new Error('Outputs spends more than inputs amount');
|
|
13007
|
-
const input = this.inputs[idx];
|
|
13008
|
-
const inputType = getInputType(input, this.opts.allowLegacyWitnessUtxo);
|
|
13009
|
-
// Taproot finalize
|
|
13010
|
-
if (inputType.txType === 'taproot') {
|
|
13011
|
-
if (input.tapKeySig)
|
|
13012
|
-
input.finalScriptWitness = [input.tapKeySig];
|
|
13013
|
-
else if (input.tapLeafScript && input.tapScriptSig) {
|
|
13014
|
-
// Sort leafs by control block length.
|
|
13015
|
-
const leafs = input.tapLeafScript.sort((a, b) => TaprootControlBlock.encode(a[0]).length -
|
|
13016
|
-
TaprootControlBlock.encode(b[0]).length);
|
|
13017
|
-
for (const [cb, _script] of leafs) {
|
|
13018
|
-
// Last byte is version
|
|
13019
|
-
const script = _script.slice(0, -1);
|
|
13020
|
-
const ver = _script[_script.length - 1];
|
|
13021
|
-
const outScript = OutScript.decode(script);
|
|
13022
|
-
const hash = tapLeafHash(script, ver);
|
|
13023
|
-
const scriptSig = input.tapScriptSig.filter((i) => equalBytes(i[0].leafHash, hash));
|
|
13024
|
-
let signatures = [];
|
|
13025
|
-
if (outScript.type === 'tr_ms') {
|
|
13026
|
-
const m = outScript.m;
|
|
13027
|
-
const pubkeys = outScript.pubkeys;
|
|
13028
|
-
let added = 0;
|
|
13029
|
-
for (const pub of pubkeys) {
|
|
13030
|
-
const sigIdx = scriptSig.findIndex((i) => equalBytes(i[0].pubKey, pub));
|
|
13031
|
-
// Should have exact amount of signatures (more -- will fail)
|
|
13032
|
-
if (added === m || sigIdx === -1) {
|
|
13033
|
-
signatures.push(EMPTY);
|
|
13034
|
-
continue;
|
|
13035
|
-
}
|
|
13036
|
-
signatures.push(scriptSig[sigIdx][1]);
|
|
13037
|
-
added++;
|
|
13038
|
-
}
|
|
13039
|
-
// Should be exact same as m
|
|
13040
|
-
if (added !== m)
|
|
13041
|
-
continue;
|
|
13042
|
-
}
|
|
13043
|
-
else if (outScript.type === 'tr_ns') {
|
|
13044
|
-
for (const pub of outScript.pubkeys) {
|
|
13045
|
-
const sigIdx = scriptSig.findIndex((i) => equalBytes(i[0].pubKey, pub));
|
|
13046
|
-
if (sigIdx === -1)
|
|
13047
|
-
continue;
|
|
13048
|
-
signatures.push(scriptSig[sigIdx][1]);
|
|
13049
|
-
}
|
|
13050
|
-
if (signatures.length !== outScript.pubkeys.length)
|
|
13051
|
-
continue;
|
|
13052
|
-
}
|
|
13053
|
-
else if (outScript.type === 'unknown' && this.opts.allowUnknownInputs) {
|
|
13054
|
-
// Trying our best to sign what we can
|
|
13055
|
-
const scriptDecoded = Script.decode(script);
|
|
13056
|
-
signatures = scriptSig
|
|
13057
|
-
.map(([{ pubKey }, signature]) => {
|
|
13058
|
-
const pos = scriptDecoded.findIndex((i) => isBytes(i) && equalBytes(i, pubKey));
|
|
13059
|
-
if (pos === -1)
|
|
13060
|
-
throw new Error('finalize/taproot: cannot find position of pubkey in script');
|
|
13061
|
-
return { signature, pos };
|
|
13062
|
-
})
|
|
13063
|
-
// Reverse order (because witness is stack and we take last element first from it)
|
|
13064
|
-
.sort((a, b) => a.pos - b.pos)
|
|
13065
|
-
.map((i) => i.signature);
|
|
13066
|
-
if (!signatures.length)
|
|
13067
|
-
continue;
|
|
13068
|
-
}
|
|
13069
|
-
else {
|
|
13070
|
-
const custom = this.opts.customScripts;
|
|
13071
|
-
if (custom) {
|
|
13072
|
-
for (const c of custom) {
|
|
13073
|
-
if (!c.finalizeTaproot)
|
|
13074
|
-
continue;
|
|
13075
|
-
const scriptDecoded = Script.decode(script);
|
|
13076
|
-
const csEncoded = c.encode(scriptDecoded);
|
|
13077
|
-
if (csEncoded === undefined)
|
|
13078
|
-
continue;
|
|
13079
|
-
const finalized = c.finalizeTaproot(script, csEncoded, scriptSig);
|
|
13080
|
-
if (!finalized)
|
|
13081
|
-
continue;
|
|
13082
|
-
input.finalScriptWitness = finalized.concat(TaprootControlBlock.encode(cb));
|
|
13083
|
-
input.finalScriptSig = EMPTY;
|
|
13084
|
-
cleanFinalInput(input);
|
|
13085
|
-
return;
|
|
13086
|
-
}
|
|
13087
|
-
}
|
|
13088
|
-
throw new Error('Finalize: Unknown tapLeafScript');
|
|
13089
|
-
}
|
|
13090
|
-
// Witness is stack, so last element will be used first
|
|
13091
|
-
input.finalScriptWitness = signatures
|
|
13092
|
-
.reverse()
|
|
13093
|
-
.concat([script, TaprootControlBlock.encode(cb)]);
|
|
13094
|
-
break;
|
|
13095
|
-
}
|
|
13096
|
-
if (!input.finalScriptWitness)
|
|
13097
|
-
throw new Error('finalize/taproot: empty witness');
|
|
13098
|
-
}
|
|
13099
|
-
else
|
|
13100
|
-
throw new Error('finalize/taproot: unknown input');
|
|
13101
|
-
input.finalScriptSig = EMPTY;
|
|
13102
|
-
cleanFinalInput(input);
|
|
13103
|
-
return;
|
|
13104
|
-
}
|
|
13105
|
-
if (!input.partialSig || !input.partialSig.length)
|
|
13106
|
-
throw new Error('Not enough partial sign');
|
|
13107
|
-
let inputScript = EMPTY;
|
|
13108
|
-
let witness = [];
|
|
13109
|
-
// TODO: move input scripts closer to payments/output scripts
|
|
13110
|
-
// Multisig
|
|
13111
|
-
if (inputType.last.type === 'ms') {
|
|
13112
|
-
const m = inputType.last.m;
|
|
13113
|
-
const pubkeys = inputType.last.pubkeys;
|
|
13114
|
-
let signatures = [];
|
|
13115
|
-
// partial: [pubkey, sign]
|
|
13116
|
-
for (const pub of pubkeys) {
|
|
13117
|
-
const sign = input.partialSig.find((s) => equalBytes(pub, s[0]));
|
|
13118
|
-
if (!sign)
|
|
13119
|
-
continue;
|
|
13120
|
-
signatures.push(sign[1]);
|
|
13121
|
-
}
|
|
13122
|
-
signatures = signatures.slice(0, m);
|
|
13123
|
-
if (signatures.length !== m) {
|
|
13124
|
-
throw new Error(`Multisig: wrong signatures count, m=${m} n=${pubkeys.length} signatures=${signatures.length}`);
|
|
13125
|
-
}
|
|
13126
|
-
inputScript = Script.encode([0, ...signatures]);
|
|
13127
|
-
}
|
|
13128
|
-
else if (inputType.last.type === 'pk') {
|
|
13129
|
-
inputScript = Script.encode([input.partialSig[0][1]]);
|
|
13130
|
-
}
|
|
13131
|
-
else if (inputType.last.type === 'pkh') {
|
|
13132
|
-
inputScript = Script.encode([input.partialSig[0][1], input.partialSig[0][0]]);
|
|
13133
|
-
}
|
|
13134
|
-
else if (inputType.last.type === 'wpkh') {
|
|
13135
|
-
inputScript = EMPTY;
|
|
13136
|
-
witness = [input.partialSig[0][1], input.partialSig[0][0]];
|
|
13137
|
-
}
|
|
13138
|
-
else if (inputType.last.type === 'unknown' && !this.opts.allowUnknownInputs)
|
|
13139
|
-
throw new Error('Unknown inputs not allowed');
|
|
13140
|
-
// Create final scripts (generic part)
|
|
13141
|
-
let finalScriptSig, finalScriptWitness;
|
|
13142
|
-
if (inputType.type.includes('wsh-')) {
|
|
13143
|
-
// P2WSH
|
|
13144
|
-
if (inputScript.length && inputType.lastScript.length) {
|
|
13145
|
-
witness = Script.decode(inputScript).map((i) => {
|
|
13146
|
-
if (i === 0)
|
|
13147
|
-
return EMPTY;
|
|
13148
|
-
if (isBytes(i))
|
|
13149
|
-
return i;
|
|
13150
|
-
throw new Error(`Wrong witness op=${i}`);
|
|
13151
|
-
});
|
|
13152
|
-
}
|
|
13153
|
-
witness = witness.concat(inputType.lastScript);
|
|
13154
|
-
}
|
|
13155
|
-
if (inputType.txType === 'segwit')
|
|
13156
|
-
finalScriptWitness = witness;
|
|
13157
|
-
if (inputType.type.startsWith('sh-wsh-')) {
|
|
13158
|
-
finalScriptSig = Script.encode([Script.encode([0, sha256$1(inputType.lastScript)])]);
|
|
13159
|
-
}
|
|
13160
|
-
else if (inputType.type.startsWith('sh-')) {
|
|
13161
|
-
finalScriptSig = Script.encode([...Script.decode(inputScript), inputType.lastScript]);
|
|
13162
|
-
}
|
|
13163
|
-
else if (inputType.type.startsWith('wsh-')) ;
|
|
13164
|
-
else if (inputType.txType !== 'segwit')
|
|
13165
|
-
finalScriptSig = inputScript;
|
|
13166
|
-
if (!finalScriptSig && !finalScriptWitness)
|
|
13167
|
-
throw new Error('Unknown error finalizing input');
|
|
13168
|
-
if (finalScriptSig)
|
|
13169
|
-
input.finalScriptSig = finalScriptSig;
|
|
13170
|
-
if (finalScriptWitness)
|
|
13171
|
-
input.finalScriptWitness = finalScriptWitness;
|
|
13172
|
-
cleanFinalInput(input);
|
|
13173
|
-
}
|
|
13174
|
-
finalize() {
|
|
13175
|
-
for (let i = 0; i < this.inputs.length; i++)
|
|
13176
|
-
this.finalizeIdx(i);
|
|
13177
|
-
}
|
|
13178
|
-
extract() {
|
|
13179
|
-
if (!this.isFinal)
|
|
13180
|
-
throw new Error('Transaction has unfinalized inputs');
|
|
13181
|
-
if (!this.outputs.length)
|
|
13182
|
-
throw new Error('Transaction has no outputs');
|
|
13183
|
-
if (this.fee < 0n)
|
|
13184
|
-
throw new Error('Outputs spends more than inputs amount');
|
|
13185
|
-
return this.toBytes(true, true);
|
|
13186
|
-
}
|
|
13187
|
-
combine(other) {
|
|
13188
|
-
for (const k of ['PSBTVersion', 'version', 'lockTime']) {
|
|
13189
|
-
if (this.opts[k] !== other.opts[k]) {
|
|
13190
|
-
throw new Error(`Transaction/combine: different ${k} this=${this.opts[k]} other=${other.opts[k]}`);
|
|
13191
|
-
}
|
|
13192
|
-
}
|
|
13193
|
-
for (const k of ['inputs', 'outputs']) {
|
|
13194
|
-
if (this[k].length !== other[k].length) {
|
|
13195
|
-
throw new Error(`Transaction/combine: different ${k} length this=${this[k].length} other=${other[k].length}`);
|
|
13196
|
-
}
|
|
13197
|
-
}
|
|
13198
|
-
const thisUnsigned = this.global.unsignedTx ? RawOldTx.encode(this.global.unsignedTx) : EMPTY;
|
|
13199
|
-
const otherUnsigned = other.global.unsignedTx
|
|
13200
|
-
? RawOldTx.encode(other.global.unsignedTx)
|
|
13201
|
-
: EMPTY;
|
|
13202
|
-
if (!equalBytes(thisUnsigned, otherUnsigned))
|
|
13203
|
-
throw new Error(`Transaction/combine: different unsigned tx`);
|
|
13204
|
-
this.global = mergeKeyMap(PSBTGlobal, this.global, other.global, undefined, this.opts.allowUnknown);
|
|
13205
|
-
for (let i = 0; i < this.inputs.length; i++)
|
|
13206
|
-
this.updateInput(i, other.inputs[i], true);
|
|
13207
|
-
for (let i = 0; i < this.outputs.length; i++)
|
|
13208
|
-
this.updateOutput(i, other.outputs[i], true);
|
|
13209
|
-
return this;
|
|
13210
|
-
}
|
|
13211
|
-
clone() {
|
|
13212
|
-
// deepClone probably faster, but this enforces that encoding is valid
|
|
13213
|
-
return Transaction.fromPSBT(this.toPSBT(this.opts.PSBTVersion), this.opts);
|
|
13214
|
-
}
|
|
13215
|
-
}
|
|
13216
|
-
|
|
13217
|
-
function decode_psbt(b64str) {
|
|
13218
|
-
const psbt = Base64.decode(b64str);
|
|
13219
|
-
return Transaction.fromPSBT(psbt, { allowUnknownOutputs: true });
|
|
13220
|
-
}
|
|
13221
|
-
function encode_psbt(psbt) {
|
|
13222
|
-
const psbt_bytes = psbt.toPSBT(0);
|
|
13223
|
-
return Base64.encode(psbt_bytes);
|
|
13224
|
-
}
|
|
13225
|
-
function parse_psbt(psbt) {
|
|
13226
|
-
if (psbt instanceof Transaction) {
|
|
13227
|
-
return psbt;
|
|
13228
|
-
}
|
|
13229
|
-
else if (typeof psbt === 'string') {
|
|
13230
|
-
return decode_psbt(psbt);
|
|
13231
|
-
}
|
|
13232
|
-
else {
|
|
13233
|
-
throw new Error('invalid psbt input: ' + psbt);
|
|
13234
|
-
}
|
|
13235
|
-
}
|
|
13236
|
-
|
|
13237
|
-
function collect_vins(psbt) {
|
|
13238
|
-
const pdata = parse_psbt(psbt);
|
|
13239
|
-
const count = pdata.inputsLength;
|
|
13240
|
-
const vins = [];
|
|
13241
|
-
for (let i = 0; i < count; i++) {
|
|
13242
|
-
const vin = pdata.getInput(i);
|
|
13243
|
-
vins.push(vin);
|
|
13244
|
-
}
|
|
13245
|
-
return vins;
|
|
13246
|
-
}
|
|
13247
|
-
function collect_vouts(psbt) {
|
|
13248
|
-
const pdata = parse_psbt(psbt);
|
|
13249
|
-
const count = pdata.outputsLength;
|
|
13250
|
-
const vouts = [];
|
|
13251
|
-
for (let i = 0; i < count; i++) {
|
|
13252
|
-
const vout = pdata.getOutput(i);
|
|
13253
|
-
vouts.push(vout);
|
|
13254
|
-
}
|
|
13255
|
-
return vouts;
|
|
13256
|
-
}
|
|
13257
|
-
function collect_prevouts(psbt) {
|
|
13258
|
-
const amounts = [], scripts = [];
|
|
13259
|
-
const pdata = parse_psbt(psbt);
|
|
13260
|
-
for (let i = 0; i < pdata.inputsLength; i++) {
|
|
13261
|
-
const txin = pdata.getInput(i);
|
|
13262
|
-
Assert.exists(txin.witnessUtxo, `witness utxo does not exist for input ${i}`);
|
|
13263
|
-
amounts.push(txin.witnessUtxo.amount);
|
|
13264
|
-
scripts.push(txin.witnessUtxo.script);
|
|
13265
|
-
}
|
|
13266
|
-
return { amounts, scripts };
|
|
13267
|
-
}
|
|
13268
|
-
function finalize_legacy_inputs(pdata) {
|
|
13269
|
-
for (let i = 0; i < pdata.inputsLength; i++) {
|
|
13270
|
-
const pvin = pdata.getInput(i);
|
|
13271
|
-
const script = pvin.redeemScript;
|
|
13272
|
-
const psig = pvin.partialSig?.at(0);
|
|
13273
|
-
if (script !== undefined && psig !== undefined) {
|
|
13274
|
-
pdata.finalizeIdx(i);
|
|
13275
|
-
}
|
|
13276
|
-
}
|
|
13277
|
-
return pdata;
|
|
13278
|
-
}
|
|
13279
|
-
|
|
13280
|
-
function get_vsize$1(psbt) {
|
|
13281
|
-
const pdata = parse_psbt(psbt);
|
|
13282
|
-
return pdata.vsize;
|
|
13283
|
-
}
|
|
13284
|
-
function get_txhex(psbt) {
|
|
13285
|
-
let pdata = parse_psbt(psbt);
|
|
13286
|
-
pdata = finalize_legacy_inputs(pdata);
|
|
13287
|
-
return pdata.hex;
|
|
13288
|
-
}
|
|
13289
|
-
|
|
13290
|
-
function assert_psbt_is_funded(psbt) {
|
|
13291
|
-
const pdata = parse_psbt(psbt);
|
|
13292
|
-
const pvouts = collect_vins(pdata);
|
|
13293
|
-
const txouts = collect_vouts(pdata);
|
|
13294
|
-
const vin_amt = pvouts.reduce((p, n) => p + Number(n.witnessUtxo?.amount ?? 0), 0);
|
|
13295
|
-
const out_amt = txouts.reduce((p, n) => p + Number(n.amount ?? 0), 0);
|
|
13296
|
-
Assert.ok(vin_amt >= out_amt, `value in (${vin_amt}) < value out (${out_amt})`);
|
|
13297
|
-
}
|
|
13298
|
-
|
|
13299
|
-
var index$7 = /*#__PURE__*/Object.freeze({
|
|
13300
|
-
__proto__: null,
|
|
13301
|
-
assert_psbt_is_funded: assert_psbt_is_funded,
|
|
13302
|
-
collect_prevouts: collect_prevouts,
|
|
13303
|
-
collect_vins: collect_vins,
|
|
13304
|
-
collect_vouts: collect_vouts,
|
|
13305
|
-
decode_psbt: decode_psbt,
|
|
13306
|
-
encode_psbt: encode_psbt,
|
|
13307
|
-
finalize_legacy_inputs: finalize_legacy_inputs,
|
|
13308
|
-
get_txhex: get_txhex,
|
|
13309
|
-
get_vsize: get_vsize$1,
|
|
13310
|
-
parse_psbt: parse_psbt
|
|
13311
|
-
});
|
|
13312
|
-
|
|
13313
9377
|
function prefix_script_size(script) {
|
|
13314
9378
|
return Buff.bytes(script).prefix_varint('le').hex;
|
|
13315
9379
|
}
|
|
@@ -13445,16 +9509,16 @@ var taproot = /*#__PURE__*/Object.freeze({
|
|
|
13445
9509
|
const sats = bigIntType().min(0n).max(2100000000000000n);
|
|
13446
9510
|
const tx_output = objectType({
|
|
13447
9511
|
value: sats,
|
|
13448
|
-
script_pk: hex
|
|
9512
|
+
script_pk: hex,
|
|
13449
9513
|
});
|
|
13450
9514
|
const tx_input = objectType({
|
|
13451
|
-
coinbase: hex
|
|
9515
|
+
coinbase: hex.nullable(),
|
|
13452
9516
|
txid: hex32,
|
|
13453
9517
|
vout: uint,
|
|
13454
9518
|
prevout: tx_output.nullable(),
|
|
13455
|
-
script_sig: hex
|
|
9519
|
+
script_sig: hex.nullable(),
|
|
13456
9520
|
sequence: uint,
|
|
13457
|
-
witness: arrayType(hex
|
|
9521
|
+
witness: arrayType(hex)
|
|
13458
9522
|
});
|
|
13459
9523
|
const tx_data = objectType({
|
|
13460
9524
|
version: uint,
|
|
@@ -13466,11 +9530,11 @@ const vout_template = tx_output.extend({
|
|
|
13466
9530
|
value: unionType([uint, sats])
|
|
13467
9531
|
});
|
|
13468
9532
|
const vin_template = tx_input.extend({
|
|
13469
|
-
coinbase: hex
|
|
9533
|
+
coinbase: hex.nullable().optional(),
|
|
13470
9534
|
prevout: vout_template.nullable().optional(),
|
|
13471
|
-
script_sig: hex
|
|
13472
|
-
sequence: unionType([hex
|
|
13473
|
-
witness: arrayType(hex
|
|
9535
|
+
script_sig: hex.nullable().optional(),
|
|
9536
|
+
sequence: unionType([hex, uint]).optional(),
|
|
9537
|
+
witness: arrayType(hex).optional(),
|
|
13474
9538
|
});
|
|
13475
9539
|
const tx_template = objectType({
|
|
13476
9540
|
version: uint.optional(),
|
|
@@ -13974,7 +10038,7 @@ function hash_segwit_tx(txdata, options = {}) {
|
|
|
13974
10038
|
}
|
|
13975
10039
|
let { pubkey, script } = options;
|
|
13976
10040
|
if (script === undefined && pubkey !== undefined) {
|
|
13977
|
-
const pkhash = hash160
|
|
10041
|
+
const pkhash = hash160(pubkey).hex;
|
|
13978
10042
|
script = `76a914${String(pkhash)}88ac`;
|
|
13979
10043
|
}
|
|
13980
10044
|
if (script === undefined) {
|
|
@@ -14405,6 +10469,7 @@ function create_taproot(config$1) {
|
|
|
14405
10469
|
const cblock = Buff.join(block);
|
|
14406
10470
|
return {
|
|
14407
10471
|
int_key: Buff.bytes(pubkey).hex,
|
|
10472
|
+
path,
|
|
14408
10473
|
parity,
|
|
14409
10474
|
taproot: taproot ?? null,
|
|
14410
10475
|
cblock: cblock.hex,
|
|
@@ -14461,10 +10526,9 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
14461
10526
|
parse_witness: parse_witness
|
|
14462
10527
|
});
|
|
14463
10528
|
|
|
14464
|
-
exports.ADDRESS = index$
|
|
10529
|
+
exports.ADDRESS = index$8;
|
|
14465
10530
|
exports.CONST = _const;
|
|
14466
|
-
exports.META = index$
|
|
14467
|
-
exports.PSBT = index$7;
|
|
10531
|
+
exports.META = index$7;
|
|
14468
10532
|
exports.SCHEMA = index$5;
|
|
14469
10533
|
exports.SCRIPT = index$6;
|
|
14470
10534
|
exports.SIGHASH = index$3;
|