/****** * START OF BASE SECTION * * ******/ /*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */ var base = {}; var taproot = {}; var secp = {}; var hashmini = {}; (function () { base.bytes = base.stringToBytes = base.str = base.bytesToString = base.hex = base.utf8 = base.bech32m = base.bech32 = base.base58check = base.base58xmr = base.base58xrp = base.base58flickr = base.base58 = base.base64url = base.base64 = base.base32crockford = base.base32hex = base.base32 = base.base16 = base.utils = base.assertNumber = void 0; // Utilities function assertNumber(n) { if (!Number.isSafeInteger(n)) throw new Error("Wrong integer: ".concat(n)); } base.assertNumber = assertNumber; function chain() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } // Wrap call in closure so JIT can inline calls var wrap = function (a, b) { return function (c) { return a(b(c)); }; }; // Construct chain of args[-1].encode(args[-2].encode([...])) var encode = Array.from(args) .reverse() .reduce(function (acc, i) { return (acc ? wrap(acc, i.encode) : i.encode); }, undefined); // Construct chain of args[0].decode(args[1].decode(...)) var decode = args.reduce(function (acc, i) { return (acc ? wrap(acc, i.decode) : i.decode); }, undefined); return { encode: encode, decode: decode }; } // Encodes integer radix representation to array of strings using alphabet and back function alphabet(alphabet) { return { encode: function (digits) { if (!Array.isArray(digits) || (digits.length && typeof digits[0] !== 'number')) throw new Error('alphabet.encode input should be an array of numbers'); return digits.map(function (i) { assertNumber(i); if (i < 0 || i >= alphabet.length) throw new Error("Digit index outside alphabet: ".concat(i, " (alphabet: ").concat(alphabet.length, ")")); return alphabet[i]; }); }, decode: function (input) { if (!Array.isArray(input) || (input.length && typeof input[0] !== 'string')) throw new Error('alphabet.decode input should be array of strings'); return input.map(function (letter) { if (typeof letter !== 'string') throw new Error("alphabet.decode: not string element=".concat(letter)); var index = alphabet.indexOf(letter); if (index === -1) throw new Error("Unknown letter: \"".concat(letter, "\". Allowed: ").concat(alphabet)); return index; }); }, }; } function join(separator) { if (separator === void 0) { separator = ''; } if (typeof separator !== 'string') throw new Error('join separator should be string'); return { encode: function (from) { if (!Array.isArray(from) || (from.length && typeof from[0] !== 'string')) throw new Error('join.encode input should be array of strings'); for (var _i = 0, from_1 = from; _i < from_1.length; _i++) { var i = from_1[_i]; if (typeof i !== 'string') throw new Error("join.encode: non-string input=".concat(i)); } return from.join(separator); }, decode: function (to) { if (typeof to !== 'string') throw new Error('join.decode input should be string'); return to.split(separator); }, }; } // Pad strings array so it has integer number of bits function padding(bits, chr) { if (chr === void 0) { chr = '='; } assertNumber(bits); if (typeof chr !== 'string') throw new Error('padding chr should be string'); return { encode: function (data) { if (!Array.isArray(data) || (data.length && typeof data[0] !== 'string')) throw new Error('padding.encode input should be array of strings'); for (var _i = 0, data_1 = data; _i < data_1.length; _i++) { var i = data_1[_i]; if (typeof i !== 'string') throw new Error("padding.encode: non-string input=".concat(i)); } while ((data.length * bits) % 8) data.push(chr); return data; }, decode: function (input) { if (!Array.isArray(input) || (input.length && typeof input[0] !== 'string')) throw new Error('padding.encode input should be array of strings'); for (var _i = 0, input_1 = input; _i < input_1.length; _i++) { var i = input_1[_i]; if (typeof i !== 'string') throw new Error("padding.decode: non-string input=".concat(i)); } var end = input.length; if ((end * bits) % 8) throw new Error('Invalid padding: string should have whole number of bytes'); for (; end > 0 && input[end - 1] === chr; end--) { if (!(((end - 1) * bits) % 8)) throw new Error('Invalid padding: string has too much padding'); } return input.slice(0, end); }, }; } function normalize(fn) { if (typeof fn !== 'function') throw new Error('normalize fn should be function'); return { encode: function (from) { return from; }, decode: function (to) { return fn(to); } }; } // NOTE: it has quadratic time complexity function convertRadix(data, from, to) { // base 1 is impossible if (from < 2) throw new Error("convertRadix: wrong from=".concat(from, ", base cannot be less than 2")); if (to < 2) throw new Error("convertRadix: wrong to=".concat(to, ", base cannot be less than 2")); if (!Array.isArray(data)) throw new Error('convertRadix: data should be array'); if (!data.length) return []; var pos = 0; var res = []; var digits = Array.from(data); digits.forEach(function (d) { assertNumber(d); if (d < 0 || d >= from) throw new Error("Wrong integer: ".concat(d)); }); while (true) { var carry = 0; var done = true; for (var i = pos; i < digits.length; i++) { var digit = digits[i]; var digitBase = from * carry + digit; if (!Number.isSafeInteger(digitBase) || (from * carry) / from !== carry || digitBase - digit !== from * carry) { throw new Error('convertRadix: carry overflow'); } carry = digitBase % to; digits[i] = Math.floor(digitBase / to); if (!Number.isSafeInteger(digits[i]) || digits[i] * to + carry !== digitBase) throw new Error('convertRadix: carry overflow'); if (!done) continue; else if (!digits[i]) pos = i; else done = false; } res.push(carry); if (done) break; } for (var i = 0; i < data.length - 1 && data[i] === 0; i++) res.push(0); return res.reverse(); } var gcd = function (a, b) { return (!b ? a : gcd(b, a % b)); }; var radix2carry = function (from, to) { return from + (to - gcd(from, to)); }; // BigInt is 5x slower function convertRadix2(data, from, to, padding) { if (!Array.isArray(data)) throw new Error('convertRadix2: data should be array'); if (from <= 0 || from > 32) throw new Error("convertRadix2: wrong from=".concat(from)); if (to <= 0 || to > 32) throw new Error("convertRadix2: wrong to=".concat(to)); if (radix2carry(from, to) > 32) { throw new Error("convertRadix2: carry overflow from=".concat(from, " to=").concat(to, " carryBits=").concat(radix2carry(from, to))); } var carry = 0; var pos = 0; // bitwise position in current element var mask = Math.pow(2, to) - 1; var res = []; for (var _i = 0, data_2 = data; _i < data_2.length; _i++) { var n = data_2[_i]; assertNumber(n); if (n >= Math.pow(2, from)) throw new Error("convertRadix2: invalid data word=".concat(n, " from=").concat(from)); carry = (carry << from) | n; if (pos + from > 32) throw new Error("convertRadix2: carry overflow pos=".concat(pos, " from=").concat(from)); pos += from; for (; pos >= to; pos -= to) res.push(((carry >> (pos - to)) & mask) >>> 0); carry &= Math.pow(2, pos) - 1; // clean carry, otherwise it will cause overflow } carry = (carry << (to - pos)) & mask; if (!padding && pos >= from) throw new Error('Excess padding'); if (!padding && carry) throw new Error("Non-zero padding: ".concat(carry)); if (padding && pos > 0) res.push(carry >>> 0); return res; } function radix(num) { assertNumber(num); return { encode: function (bytes) { if (!(bytes instanceof Uint8Array)) throw new Error('radix.encode input should be Uint8Array'); return convertRadix(Array.from(bytes), Math.pow(2, 8), num); }, decode: function (digits) { if (!Array.isArray(digits) || (digits.length && typeof digits[0] !== 'number')) throw new Error('radix.decode input should be array of strings'); return Uint8Array.from(convertRadix(digits, num, Math.pow(2, 8))); }, }; } // If both bases are power of same number (like `2**8 <-> 2**64`), // there is a linear algorithm. For now we have implementation for power-of-two bases only function radix2(bits, revPadding) { if (revPadding === void 0) { revPadding = false; } assertNumber(bits); if (bits <= 0 || bits > 32) throw new Error('radix2: bits should be in (0..32]'); if (radix2carry(8, bits) > 32 || radix2carry(bits, 8) > 32) throw new Error('radix2: carry overflow'); return { encode: function (bytes) { if (!(bytes instanceof Uint8Array)) throw new Error('radix2.encode input should be Uint8Array'); return convertRadix2(Array.from(bytes), 8, bits, !revPadding); }, decode: function (digits) { if (!Array.isArray(digits) || (digits.length && typeof digits[0] !== 'number')) throw new Error('radix2.decode input should be array of strings'); return Uint8Array.from(convertRadix2(digits, bits, 8, revPadding)); }, }; } function unsafeWrapper(fn) { if (typeof fn !== 'function') throw new Error('unsafeWrapper fn should be function'); return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } try { return fn.apply(null, args); } catch (e) { } }; } function checksum(len, fn) { assertNumber(len); if (typeof fn !== 'function') throw new Error('checksum fn should be function'); return { encode: function (data) { if (!(data instanceof Uint8Array)) throw new Error('checksum.encode: input should be Uint8Array'); var checksum = fn(data).slice(0, len); var res = new Uint8Array(data.length + len); res.set(data); res.set(checksum, data.length); return res; }, decode: function (data) { if (!(data instanceof Uint8Array)) throw new Error('checksum.decode: input should be Uint8Array'); var payload = data.slice(0, -len); var newChecksum = fn(payload).slice(0, len); var oldChecksum = data.slice(-len); for (var i = 0; i < len; i++) if (newChecksum[i] !== oldChecksum[i]) throw new Error('Invalid checksum'); return payload; }, }; } base.utils = { alphabet: alphabet, chain: chain, checksum: checksum, radix: radix, radix2: radix2, join: join, padding: padding }; // RFC 4648 aka RFC 3548 // --------------------- base.base16 = chain(radix2(4), alphabet('0123456789ABCDEF'), join('')); base.base32 = chain(radix2(5), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'), padding(5), join('')); base.base32hex = chain(radix2(5), alphabet('0123456789ABCDEFGHIJKLMNOPQRSTUV'), padding(5), join('')); base.base32crockford = chain(radix2(5), alphabet('0123456789ABCDEFGHJKMNPQRSTVWXYZ'), join(''), normalize(function (s) { return s.toUpperCase().replace(/O/g, '0').replace(/[IL]/g, '1'); })); base.base64 = chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'), padding(6), join('')); base.base64url = chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'), padding(6), join('')); // base58 code // ----------- var genBase58 = function (abc) { return chain(radix(58), alphabet(abc), join('')); }; base.base58 = genBase58('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'); base.base58flickr = genBase58('123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'); base.base58xrp = genBase58('rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz'); // xmr ver is done in 8-byte blocks (which equals 11 chars in decoding). Last (non-full) block padded with '1' to size in XMR_BLOCK_LEN. // Block encoding significantly reduces quadratic complexity of base58. // Data len (index) -> encoded block len var XMR_BLOCK_LEN = [0, 2, 3, 5, 6, 7, 9, 10, 11]; base.base58xmr = { encode: function (data) { var res = ''; for (var i = 0; i < data.length; i += 8) { var block = data.subarray(i, i + 8); res += base.base58.encode(block).padStart(XMR_BLOCK_LEN[block.length], '1'); } return res; }, decode: function (str) { var res = []; for (var i = 0; i < str.length; i += 11) { var slice = str.slice(i, i + 11); var blockLen = XMR_BLOCK_LEN.indexOf(slice.length); var block = base.base58.decode(slice); for (var j = 0; j < block.length - blockLen; j++) { if (block[j] !== 0) throw new Error('base58xmr: wrong padding'); } res = res.concat(Array.from(block.slice(block.length - blockLen))); } return Uint8Array.from(res); }, }; var base58check = function (sha256) { return chain(checksum(4, function (data) { return sha256(sha256(data)); }), base.base58); }; base.base58check = base58check; var BECH_ALPHABET = chain(alphabet('qpzry9x8gf2tvdw0s3jn54khce6mua7l'), join('')); var POLYMOD_GENERATORS = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]; function bech32Polymod(pre) { var b = pre >> 25; var chk = (pre & 0x1ffffff) << 5; for (var i = 0; i < POLYMOD_GENERATORS.length; i++) { if (((b >> i) & 1) === 1) chk ^= POLYMOD_GENERATORS[i]; } return chk; } function bechChecksum(prefix, words, encodingConst) { if (encodingConst === void 0) { encodingConst = 1; } var len = prefix.length; var chk = 1; for (var i = 0; i < len; i++) { var c = prefix.charCodeAt(i); if (c < 33 || c > 126) throw new Error("Invalid prefix (".concat(prefix, ")")); chk = bech32Polymod(chk) ^ (c >> 5); } chk = bech32Polymod(chk); for (var i = 0; i < len; i++) chk = bech32Polymod(chk) ^ (prefix.charCodeAt(i) & 0x1f); for (var _i = 0, words_1 = words; _i < words_1.length; _i++) { var v = words_1[_i]; chk = bech32Polymod(chk) ^ v; } for (var i = 0; i < 6; i++) chk = bech32Polymod(chk); chk ^= encodingConst; return BECH_ALPHABET.encode(convertRadix2([chk % Math.pow(2, 30)], 30, 5, false)); } function genBech32(encoding) { var ENCODING_CONST = encoding === 'bech32' ? 1 : 0x2bc830a3; var _words = radix2(5); var fromWords = _words.decode; var toWords = _words.encode; var fromWordsUnsafe = unsafeWrapper(fromWords); function encode(prefix, words, limit) { if (limit === void 0) { limit = 90; } if (typeof prefix !== 'string') throw new Error("bech32.encode prefix should be string, not ".concat(typeof prefix)); if (!Array.isArray(words) || (words.length && typeof words[0] !== 'number')) throw new Error("bech32.encode words should be array of numbers, not ".concat(typeof words)); var actualLength = prefix.length + 7 + words.length; if (limit !== false && actualLength > limit) throw new TypeError("Length ".concat(actualLength, " exceeds limit ").concat(limit)); prefix = prefix.toLowerCase(); return "".concat(prefix, "1").concat(BECH_ALPHABET.encode(words)).concat(bechChecksum(prefix, words, ENCODING_CONST)); } function decode(str, limit) { if (limit === void 0) { limit = 90; } if (typeof str !== 'string') throw new Error("bech32.decode input should be string, not ".concat(typeof str)); if (str.length < 8 || (limit !== false && str.length > limit)) throw new TypeError("Wrong string length: ".concat(str.length, " (").concat(str, "). Expected (8..").concat(limit, ")")); // don't allow mixed case var lowered = str.toLowerCase(); if (str !== lowered && str !== str.toUpperCase()) throw new Error("String must be lowercase or uppercase"); str = lowered; var sepIndex = str.lastIndexOf('1'); if (sepIndex === 0 || sepIndex === -1) throw new Error("Letter \"1\" must be present between prefix and data only"); var prefix = str.slice(0, sepIndex); var _words = str.slice(sepIndex + 1); if (_words.length < 6) throw new Error('Data must be at least 6 characters long'); var words = BECH_ALPHABET.decode(_words).slice(0, -6); var sum = bechChecksum(prefix, words, ENCODING_CONST); if (!_words.endsWith(sum)) throw new Error("Invalid checksum in ".concat(str, ": expected \"").concat(sum, "\"")); return { prefix: prefix, words: words }; } var decodeUnsafe = unsafeWrapper(decode); function decodeToBytes(str) { var _a = decode(str, false), prefix = _a.prefix, words = _a.words; return { prefix: prefix, words: words, bytes: fromWords(words) }; } return { encode: encode, decode: decode, decodeToBytes: decodeToBytes, decodeUnsafe: decodeUnsafe, fromWords: fromWords, fromWordsUnsafe: fromWordsUnsafe, toWords: toWords }; } base.bech32 = genBech32('bech32'); base.bech32m = genBech32('bech32m'); base.utf8 = { encode: function (data) { return new TextDecoder().decode(data); }, decode: function (str) { return new TextEncoder().encode(str); }, }; base.hex = chain(radix2(4), alphabet('0123456789abcdef'), join(''), normalize(function (s) { if (typeof s !== 'string' || s.length % 2) throw new TypeError("hex.decode: expected string, got ".concat(typeof s, " with length ").concat(s.length)); return s.toLowerCase(); })); // prettier-ignore var CODERS = { utf8: base.utf8, hex: base.hex, base16: base.base16, base32: base.base32, base64: base.base64, base64url: base.base64url, base58: base.base58, base58xmr: base.base58xmr }; var coderTypeError = "Invalid encoding type. Available types: ".concat(Object.keys(CODERS).join(', ')); var bytesToString = function (type, bytes) { if (typeof type !== 'string' || !CODERS.hasOwnProperty(type)) throw new TypeError(coderTypeError); if (!(bytes instanceof Uint8Array)) throw new TypeError('bytesToString() expects Uint8Array'); return CODERS[type].encode(bytes); }; base.bytesToString = bytesToString; base.str = base.bytesToString; // as in python, but for bytes only var stringToBytes = function (type, str) { if (!CODERS.hasOwnProperty(type)) throw new TypeError(coderTypeError); if (typeof str !== 'string') throw new TypeError('stringToBytes() expects string'); return CODERS[type].decode(str); }; base.stringToBytes = stringToBytes; base.bytes = base.stringToBytes; /**** * * START OF SECP AND SCHNORR SECTION * * *****/ const _nodeResolve_empty = {}; const nodeCrypto = /*#__PURE__*/Object.freeze({ __proto__: null, 'default': _nodeResolve_empty }); /*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */ var _0n = BigInt(0); var _1n = BigInt(1); var _2n = BigInt(2); var _3n = BigInt(3); var _8n = BigInt(8); const CURVE = Object.freeze({ a: _0n, b: BigInt(7), P: BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f'), n: BigInt('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141'), h: _1n, Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'), Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'), beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'), }); function weistrass(x) { const { a, b } = CURVE; const x2 = mod(x * x); const x3 = mod(x2 * x); return mod(x3 + a * x + b); } const USE_ENDOMORPHISM = CURVE.a === _0n; class ShaError extends Error { constructor(message) { super(message); } } class JacobianPoint { constructor(x, y, z) { this.x = x; this.y = y; this.z = z; } static fromAffine(p) { if (!(p instanceof Point)) { throw new TypeError('JacobianPoint#fromAffine: expected Point'); } return new JacobianPoint(p.x, p.y, _1n); } static toAffineBatch(points) { const toInv = invertBatch(points.map((p) => p.z)); return points.map((p, i) => p.toAffine(toInv[i])); } static normalizeZ(points) { return JacobianPoint.toAffineBatch(points).map(JacobianPoint.fromAffine); } equals(other) { if (!(other instanceof JacobianPoint)) throw new TypeError('JacobianPoint expected'); const { x: X1, y: Y1, z: Z1 } = this; const { x: X2, y: Y2, z: Z2 } = other; const Z1Z1 = mod(Z1 * Z1); const Z2Z2 = mod(Z2 * Z2); const U1 = mod(X1 * Z2Z2); const U2 = mod(X2 * Z1Z1); const S1 = mod(mod(Y1 * Z2) * Z2Z2); const S2 = mod(mod(Y2 * Z1) * Z1Z1); return U1 === U2 && S1 === S2; } negate() { return new JacobianPoint(this.x, mod(-this.y), this.z); } double() { const { x: X1, y: Y1, z: Z1 } = this; const A = mod(X1 * X1); const B = mod(Y1 * Y1); const C = mod(B * B); const x1b = X1 + B; const D = mod(_2n * (mod(x1b * x1b) - A - C)); const E = mod(_3n * A); const F = mod(E * E); const X3 = mod(F - _2n * D); const Y3 = mod(E * (D - X3) - _8n * C); const Z3 = mod(_2n * Y1 * Z1); return new JacobianPoint(X3, Y3, Z3); } add(other) { if (!(other instanceof JacobianPoint)) throw new TypeError('JacobianPoint expected'); const { x: X1, y: Y1, z: Z1 } = this; const { x: X2, y: Y2, z: Z2 } = other; if (X2 === _0n || Y2 === _0n) return this; if (X1 === _0n || Y1 === _0n) return other; const Z1Z1 = mod(Z1 * Z1); const Z2Z2 = mod(Z2 * Z2); const U1 = mod(X1 * Z2Z2); const U2 = mod(X2 * Z1Z1); const S1 = mod(mod(Y1 * Z2) * Z2Z2); const S2 = mod(mod(Y2 * Z1) * Z1Z1); const H = mod(U2 - U1); const r = mod(S2 - S1); if (H === _0n) { if (r === _0n) { return this.double(); } else { return JacobianPoint.ZERO; } } const HH = mod(H * H); const HHH = mod(H * HH); const V = mod(U1 * HH); const X3 = mod(r * r - HHH - _2n * V); const Y3 = mod(r * (V - X3) - S1 * HHH); const Z3 = mod(Z1 * Z2 * H); return new JacobianPoint(X3, Y3, Z3); } subtract(other) { return this.add(other.negate()); } multiplyUnsafe(scalar) { const P0 = JacobianPoint.ZERO; if (typeof scalar === 'bigint' && scalar === _0n) return P0; let n = normalizeScalar(scalar); if (n === _1n) return this; if (!USE_ENDOMORPHISM) { let p = P0; let d = this; while (n > _0n) { if (n & _1n) p = p.add(d); d = d.double(); n >>= _1n; } return p; } let { k1neg, k1, k2neg, k2 } = splitScalarEndo(n); let k1p = P0; let k2p = P0; let d = this; while (k1 > _0n || k2 > _0n) { if (k1 & _1n) k1p = k1p.add(d); if (k2 & _1n) k2p = k2p.add(d); d = d.double(); k1 >>= _1n; k2 >>= _1n; } if (k1neg) k1p = k1p.negate(); if (k2neg) k2p = k2p.negate(); k2p = new JacobianPoint(mod(k2p.x * CURVE.beta), k2p.y, k2p.z); return k1p.add(k2p); } precomputeWindow(W) { const windows = USE_ENDOMORPHISM ? 128 / W + 1 : 256 / W + 1; const points = []; let p = this; let base = p; for (let window = 0; window < windows; window++) { base = p; points.push(base); for (let i = 1; i < 2 ** (W - 1); i++) { base = base.add(p); points.push(base); } p = base.double(); } return points; } wNAF(n, affinePoint) { if (!affinePoint && this.equals(JacobianPoint.BASE)) affinePoint = Point.BASE; const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1; if (256 % W) { throw new Error('Point#wNAF: Invalid precomputation window, must be power of 2'); } let precomputes = affinePoint && pointPrecomputes.get(affinePoint); if (!precomputes) { precomputes = this.precomputeWindow(W); if (affinePoint && W !== 1) { precomputes = JacobianPoint.normalizeZ(precomputes); pointPrecomputes.set(affinePoint, precomputes); } } let p = JacobianPoint.ZERO; let f = JacobianPoint.ZERO; const windows = 1 + (USE_ENDOMORPHISM ? 128 / W : 256 / W); const windowSize = 2 ** (W - 1); const mask = BigInt(2 ** W - 1); const maxNumber = 2 ** W; const shiftBy = BigInt(W); for (let window = 0; window < windows; window++) { const offset = window * windowSize; let wbits = Number(n & mask); n >>= shiftBy; if (wbits > windowSize) { wbits -= maxNumber; n += _1n; } if (wbits === 0) { let pr = precomputes[offset]; if (window % 2) pr = pr.negate(); f = f.add(pr); } else { let cached = precomputes[offset + Math.abs(wbits) - 1]; if (wbits < 0) cached = cached.negate(); p = p.add(cached); } } return { p, f }; } multiply(scalar, affinePoint) { let n = normalizeScalar(scalar); let point; let fake; if (USE_ENDOMORPHISM) { const { k1neg, k1, k2neg, k2 } = splitScalarEndo(n); let { p: k1p, f: f1p } = this.wNAF(k1, affinePoint); let { p: k2p, f: f2p } = this.wNAF(k2, affinePoint); if (k1neg) k1p = k1p.negate(); if (k2neg) k2p = k2p.negate(); k2p = new JacobianPoint(mod(k2p.x * CURVE.beta), k2p.y, k2p.z); point = k1p.add(k2p); fake = f1p.add(f2p); } else { const { p, f } = this.wNAF(n, affinePoint); point = p; fake = f; } return JacobianPoint.normalizeZ([point, fake])[0]; } toAffine(invZ = invert(this.z)) { const { x, y, z } = this; const iz1 = invZ; const iz2 = mod(iz1 * iz1); const iz3 = mod(iz2 * iz1); const ax = mod(x * iz2); const ay = mod(y * iz3); const zz = mod(z * iz1); if (zz !== _1n) throw new Error('invZ was invalid'); return new Point(ax, ay); } } JacobianPoint.BASE = new JacobianPoint(CURVE.Gx, CURVE.Gy, _1n); JacobianPoint.ZERO = new JacobianPoint(_0n, _1n, _0n); const pointPrecomputes = new WeakMap(); class Point { constructor(x, y) { this.x = x; this.y = y; } _setWindowSize(windowSize) { this._WINDOW_SIZE = windowSize; pointPrecomputes.delete(this); } hasEvenY() { return this.y % _2n === _0n; } static fromCompressedHex(bytes) { const isShort = bytes.length === 32; const x = bytesToNumber(isShort ? bytes : bytes.subarray(1)); if (!isValidFieldElement(x)) throw new Error('Point is not on curve'); const y2 = weistrass(x); let y = sqrtMod(y2); const isYOdd = (y & _1n) === _1n; if (isShort) { if (isYOdd) y = mod(-y); } else { const isFirstByteOdd = (bytes[0] & 1) === 1; if (isFirstByteOdd !== isYOdd) y = mod(-y); } const point = new Point(x, y); point.assertValidity(); return point; } static fromUncompressedHex(bytes) { const x = bytesToNumber(bytes.subarray(1, 33)); const y = bytesToNumber(bytes.subarray(33, 65)); const point = new Point(x, y); point.assertValidity(); return point; } static fromHex(hex) { const bytes = ensureBytes(hex); const len = bytes.length; const header = bytes[0]; if (len === 32 || (len === 33 && (header === 0x02 || header === 0x03))) { return this.fromCompressedHex(bytes); } if (len === 65 && header === 0x04) return this.fromUncompressedHex(bytes); throw new Error(`Point.fromHex: received invalid point. Expected 32-33 compressed bytes or 65 uncompressed bytes, not ${len}`); } static fromPrivateKey(privateKey) { return Point.BASE.multiply(normalizePrivateKey(privateKey)); } static fromSignature(msgHash, signature, recovery) { msgHash = ensureBytes(msgHash); const h = truncateHash(msgHash); const { r, s } = normalizeSignature(signature); if (recovery !== 0 && recovery !== 1) { throw new Error('Cannot recover signature: invalid recovery bit'); } const prefix = recovery & 1 ? '03' : '02'; const R = Point.fromHex(prefix + numTo32bStr(r)); const { n } = CURVE; const rinv = invert(r, n); const u1 = mod(-h * rinv, n); const u2 = mod(s * rinv, n); const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); if (!Q) throw new Error('Cannot recover signature: point at infinify'); Q.assertValidity(); return Q; } toRawBytes(isCompressed = false) { return hexToBytes(this.toHex(isCompressed)); } toHex(isCompressed = false) { const x = numTo32bStr(this.x); if (isCompressed) { const prefix = this.hasEvenY() ? '02' : '03'; return `${prefix}${x}`; } else { return `04${x}${numTo32bStr(this.y)}`; } } toHexX() { return this.toHex(true).slice(2); } toRawX() { return this.toRawBytes(true).slice(1); } assertValidity() { const msg = 'Point is not on elliptic curve'; const { x, y } = this; if (!isValidFieldElement(x) || !isValidFieldElement(y)) throw new Error(msg); const left = mod(y * y); const right = weistrass(x); if (mod(left - right) !== _0n) throw new Error(msg); } equals(other) { return this.x === other.x && this.y === other.y; } negate() { return new Point(this.x, mod(-this.y)); } double() { return JacobianPoint.fromAffine(this).double().toAffine(); } add(other) { return JacobianPoint.fromAffine(this).add(JacobianPoint.fromAffine(other)).toAffine(); } subtract(other) { return this.add(other.negate()); } multiply(scalar) { return JacobianPoint.fromAffine(this).multiply(scalar, this).toAffine(); } multiplyAndAddUnsafe(Q, a, b) { const P = JacobianPoint.fromAffine(this); const aP = a === _0n || a === _1n || this !== Point.BASE ? P.multiplyUnsafe(a) : P.multiply(a); const bQ = JacobianPoint.fromAffine(Q).multiplyUnsafe(b); const sum = aP.add(bQ); return sum.equals(JacobianPoint.ZERO) ? undefined : sum.toAffine(); } } Point.BASE = new Point(CURVE.Gx, CURVE.Gy); Point.ZERO = new Point(_0n, _0n); function sliceDER(s) { return Number.parseInt(s[0], 16) >= 8 ? '00' + s : s; } function parseDERInt(data) { if (data.length < 2 || data[0] !== 0x02) { throw new Error(`Invalid signature integer tag: ${bytesToHex(data)}`); } const len = data[1]; const res = data.subarray(2, len + 2); if (!len || res.length !== len) { throw new Error(`Invalid signature integer: wrong length`); } if (res[0] === 0x00 && res[1] <= 0x7f) { throw new Error('Invalid signature integer: trailing length'); } return { data: bytesToNumber(res), left: data.subarray(len + 2) }; } function parseDERSignature(data) { if (data.length < 2 || data[0] != 0x30) { throw new Error(`Invalid signature tag: ${bytesToHex(data)}`); } if (data[1] !== data.length - 2) { throw new Error('Invalid signature: incorrect length'); } const { data: r, left: sBytes } = parseDERInt(data.subarray(2)); const { data: s, left: rBytesLeft } = parseDERInt(sBytes); if (rBytesLeft.length) { throw new Error(`Invalid signature: left bytes after parsing: ${bytesToHex(rBytesLeft)}`); } return { r, s }; } class Signature { constructor(r, s) { this.r = r; this.s = s; this.assertValidity(); } static fromCompact(hex) { const arr = hex instanceof Uint8Array; const name = 'Signature.fromCompact'; if (typeof hex !== 'string' && !arr) throw new TypeError(`${name}: Expected string or Uint8Array`); const str = arr ? bytesToHex(hex) : hex; if (str.length !== 128) throw new Error(`${name}: Expected 64-byte hex`); return new Signature(hexToNumber(str.slice(0, 64)), hexToNumber(str.slice(64, 128))); } static fromDER(hex) { const arr = hex instanceof Uint8Array; if (typeof hex !== 'string' && !arr) throw new TypeError(`Signature.fromDER: Expected string or Uint8Array`); const { r, s } = parseDERSignature(arr ? hex : hexToBytes(hex)); return new Signature(r, s); } static fromHex(hex) { return this.fromDER(hex); } assertValidity() { const { r, s } = this; if (!isWithinCurveOrder(r)) throw new Error('Invalid Signature: r must be 0 < r < n'); if (!isWithinCurveOrder(s)) throw new Error('Invalid Signature: s must be 0 < s < n'); } hasHighS() { const HALF = CURVE.n >> _1n; return this.s > HALF; } normalizeS() { return this.hasHighS() ? new Signature(this.r, CURVE.n - this.s) : this; } toDERRawBytes(isCompressed = false) { return hexToBytes(this.toDERHex(isCompressed)); } toDERHex(isCompressed = false) { const sHex = sliceDER(numberToHexUnpadded(this.s)); if (isCompressed) return sHex; const rHex = sliceDER(numberToHexUnpadded(this.r)); const rLen = numberToHexUnpadded(rHex.length / 2); const sLen = numberToHexUnpadded(sHex.length / 2); const length = numberToHexUnpadded(rHex.length / 2 + sHex.length / 2 + 4); return `30${length}02${rLen}${rHex}02${sLen}${sHex}`; } toRawBytes() { return this.toDERRawBytes(); } toHex() { return this.toDERHex(); } toCompactRawBytes() { return hexToBytes(this.toCompactHex()); } toCompactHex() { return numTo32bStr(this.r) + numTo32bStr(this.s); } } function concatBytes(...arrays) { if (!arrays.every((b) => b instanceof Uint8Array)) throw new Error('Uint8Array list expected'); if (arrays.length === 1) return arrays[0]; const length = arrays.reduce((a, arr) => a + arr.length, 0); const result = new Uint8Array(length); for (let i = 0, pad = 0; i < arrays.length; i++) { const arr = arrays[i]; result.set(arr, pad); pad += arr.length; } return result; } var hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0')); function bytesToHex(uint8a) { if (!(uint8a instanceof Uint8Array)) throw new Error('Expected Uint8Array'); let hex = ''; for (let i = 0; i < uint8a.length; i++) { hex += hexes[uint8a[i]]; } return hex; } secp.bytesToHex = bytesToHex const POW_2_256 = BigInt('0x10000000000000000000000000000000000000000000000000000000000000000'); function numTo32bStr(num) { if (typeof num !== 'bigint') throw new Error('Expected bigint'); if (!(_0n <= num && num < POW_2_256)) throw new Error('Expected number < 2^256'); return num.toString(16).padStart(64, '0'); } function numTo32b(num) { const b = hexToBytes(numTo32bStr(num)); if (b.length !== 32) throw new Error('Error: expected 32 bytes'); return b; } function numberToHexUnpadded(num) { const hex = num.toString(16); return hex.length & 1 ? `0${hex}` : hex; } function hexToNumber(hex) { if (typeof hex !== 'string') { throw new TypeError('hexToNumber: expected string, got ' + typeof hex); } return BigInt(`0x${hex}`); } secp.hexToNumber = hexToNumber; function hexToBytes(hex) { if (typeof hex !== 'string') { throw new TypeError('hexToBytes: expected string, got ' + typeof hex); } if (hex.length % 2) throw new Error('hexToBytes: received invalid unpadded hex' + hex.length); const array = new Uint8Array(hex.length / 2); for (let i = 0; i < array.length; i++) { const j = i * 2; const hexByte = hex.slice(j, j + 2); const byte = Number.parseInt(hexByte, 16); if (Number.isNaN(byte) || byte < 0) throw new Error('Invalid byte sequence'); array[i] = byte; } return array; } secp.hexToBytes = hexToBytes; function bytesToNumber(bytes) { return hexToNumber(bytesToHex(bytes)); } secp.bytesToNumber = bytesToNumber; function ensureBytes(hex) { return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex); } function normalizeScalar(num) { if (typeof num === 'number' && Number.isSafeInteger(num) && num > 0) return BigInt(num); if (typeof num === 'bigint' && isWithinCurveOrder(num)) return num; throw new TypeError('Expected valid private scalar: 0 < scalar < curve.n'); } function mod(a, b = CURVE.P) { const result = a % b; return result >= _0n ? result : b + result; } function pow2(x, power) { const { P } = CURVE; let res = x; while (power-- > _0n) { res *= res; res %= P; } return res; } function sqrtMod(x) { const { P } = CURVE; const _6n = BigInt(6); const _11n = BigInt(11); const _22n = BigInt(22); const _23n = BigInt(23); const _44n = BigInt(44); const _88n = BigInt(88); const b2 = (x * x * x) % P; const b3 = (b2 * b2 * x) % P; const b6 = (pow2(b3, _3n) * b3) % P; const b9 = (pow2(b6, _3n) * b3) % P; const b11 = (pow2(b9, _2n) * b2) % P; const b22 = (pow2(b11, _11n) * b11) % P; const b44 = (pow2(b22, _22n) * b22) % P; const b88 = (pow2(b44, _44n) * b44) % P; const b176 = (pow2(b88, _88n) * b88) % P; const b220 = (pow2(b176, _44n) * b44) % P; const b223 = (pow2(b220, _3n) * b3) % P; const t1 = (pow2(b223, _23n) * b22) % P; const t2 = (pow2(t1, _6n) * b2) % P; return pow2(t2, _2n); } function invert(number, modulo = CURVE.P) { if (number === _0n || modulo <= _0n) { throw new Error(`invert: expected positive integers, got n=${number} mod=${modulo}`); } let a = mod(number, modulo); let b = modulo; let x = _0n, u = _1n; while (a !== _0n) { const q = b / a; const r = b % a; const m = x - u * q; b = a, a = r, x = u, u = m; } const gcd = b; if (gcd !== _1n) throw new Error('invert: does not exist'); return mod(x, modulo); } function invertBatch(nums, p = CURVE.P) { const scratch = new Array(nums.length); const lastMultiplied = nums.reduce((acc, num, i) => { if (num === _0n) return acc; scratch[i] = acc; return mod(acc * num, p); }, _1n); const inverted = invert(lastMultiplied, p); nums.reduceRight((acc, num, i) => { if (num === _0n) return acc; scratch[i] = mod(acc * scratch[i], p); return mod(acc * num, p); }, inverted); return scratch; } const divNearest = (a, b) => (a + b / _2n) / b; const ENDO = { a1: BigInt('0x3086d221a7d46bcde86c90e49284eb15'), b1: -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3'), a2: BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8'), b2: BigInt('0x3086d221a7d46bcde86c90e49284eb15'), POW_2_128: BigInt('0x100000000000000000000000000000000'), }; function splitScalarEndo(k) { const { n } = CURVE; const { a1, b1, a2, b2, POW_2_128 } = ENDO; const c1 = divNearest(b2 * k, n); const c2 = divNearest(-b1 * k, n); let k1 = mod(k - c1 * a1 - c2 * a2, n); let k2 = mod(-c1 * b1 - c2 * b2, n); const k1neg = k1 > POW_2_128; const k2neg = k2 > POW_2_128; if (k1neg) k1 = n - k1; if (k2neg) k2 = n - k2; if (k1 > POW_2_128 || k2 > POW_2_128) { throw new Error('splitScalarEndo: Endomorphism failed, k=' + k); } return { k1neg, k1, k2neg, k2 }; } function truncateHash(hash) { const { n } = CURVE; const byteLength = hash.length; const delta = byteLength * 8 - 256; let h = bytesToNumber(hash); if (delta > 0) h = h >> BigInt(delta); if (h >= n) h -= n; return h; } let _sha256Sync; let _hmacSha256Sync; class HmacDrbg { constructor() { this.v = new Uint8Array(32).fill(1); this.k = new Uint8Array(32).fill(0); this.counter = 0; } hmac(...values) { return utils.hmacSha256(this.k, ...values); } hmacSync(...values) { return _hmacSha256Sync(this.k, ...values); } checkSync() { if (typeof _hmacSha256Sync !== 'function') throw new ShaError('hmacSha256Sync needs to be set'); } incr() { if (this.counter >= 1000) throw new Error('Tried 1,000 k values for sign(), all were invalid'); this.counter += 1; } async reseed(seed = new Uint8Array()) { this.k = await this.hmac(this.v, Uint8Array.from([0x00]), seed); this.v = await this.hmac(this.v); if (seed.length === 0) return; this.k = await this.hmac(this.v, Uint8Array.from([0x01]), seed); this.v = await this.hmac(this.v); } reseedSync(seed = new Uint8Array()) { this.checkSync(); this.k = this.hmacSync(this.v, Uint8Array.from([0x00]), seed); this.v = this.hmacSync(this.v); if (seed.length === 0) return; this.k = this.hmacSync(this.v, Uint8Array.from([0x01]), seed); this.v = this.hmacSync(this.v); } async generate() { this.incr(); this.v = await this.hmac(this.v); return this.v; } generateSync() { this.checkSync(); this.incr(); this.v = this.hmacSync(this.v); return this.v; } } function isWithinCurveOrder(num) { return _0n < num && num < CURVE.n; } function isValidFieldElement(num) { return _0n < num && num < CURVE.P; } function kmdToSig(kBytes, m, d) { const k = bytesToNumber(kBytes); if (!isWithinCurveOrder(k)) return; const { n } = CURVE; const q = Point.BASE.multiply(k); const r = mod(q.x, n); if (r === _0n) return; const s = mod(invert(k, n) * mod(m + d * r, n), n); if (s === _0n) return; const sig = new Signature(r, s); const recovery = (q.x === sig.r ? 0 : 2) | Number(q.y & _1n); return { sig, recovery }; } function normalizePrivateKey(key) { let num; if (typeof key === 'bigint') { num = key; } else if (typeof key === 'number' && Number.isSafeInteger(key) && key > 0) { num = BigInt(key); } else if (typeof key === 'string') { if (key.length !== 64) throw new Error('Expected 32 bytes of private key'); num = hexToNumber(key); } else if (key instanceof Uint8Array) { if (key.length !== 32) throw new Error('Expected 32 bytes of private key'); num = bytesToNumber(key); } else { throw new TypeError('Expected valid private key'); } if (!isWithinCurveOrder(num)) throw new Error('Expected private key: 0 < key < n'); return num; } function normalizePublicKey(publicKey) { if (publicKey instanceof Point) { publicKey.assertValidity(); return publicKey; } else { return Point.fromHex(publicKey); } } function normalizeSignature(signature) { if (signature instanceof Signature) { signature.assertValidity(); return signature; } try { return Signature.fromDER(signature); } catch (error) { return Signature.fromCompact(signature); } } function getPublicKey(privateKey, isCompressed = false) { return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed); } function recoverPublicKey(msgHash, signature, recovery, isCompressed = false) { return Point.fromSignature(msgHash, signature, recovery).toRawBytes(isCompressed); } function isProbPub(item) { const arr = item instanceof Uint8Array; const str = typeof item === 'string'; const len = (arr || str) && item.length; if (arr) return len === 33 || len === 65; if (str) return len === 66 || len === 130; if (item instanceof Point) return true; return false; } function getSharedSecret(privateA, publicB, isCompressed = false) { if (isProbPub(privateA)) throw new TypeError('getSharedSecret: first arg must be private key'); if (!isProbPub(publicB)) throw new TypeError('getSharedSecret: second arg must be public key'); const b = normalizePublicKey(publicB); b.assertValidity(); return b.multiply(normalizePrivateKey(privateA)).toRawBytes(isCompressed); } function bits2int(bytes) { const slice = bytes.length > 32 ? bytes.slice(0, 32) : bytes; return bytesToNumber(slice); } function bits2octets(bytes) { const z1 = bits2int(bytes); const z2 = mod(z1, CURVE.n); return int2octets(z2 < _0n ? z1 : z2); } function int2octets(num) { return numTo32b(num); } function initSigArgs(msgHash, privateKey, extraEntropy) { if (msgHash == null) throw new Error(`sign: expected valid message hash, not "${msgHash}"`); const h1 = ensureBytes(msgHash); const d = normalizePrivateKey(privateKey); const seedArgs = [int2octets(d), bits2octets(h1)]; if (extraEntropy != null) { if (extraEntropy === true) extraEntropy = utils.randomBytes(32); const e = ensureBytes(extraEntropy); if (e.length !== 32) throw new Error('sign: Expected 32 bytes of extra data'); seedArgs.push(e); } const seed = concatBytes(...seedArgs); const m = bits2int(h1); return { seed, m, d }; } function finalizeSig(recSig, opts) { let { sig, recovery } = recSig; const { canonical, der, recovered } = Object.assign({ canonical: true, der: true }, opts); if (canonical && sig.hasHighS()) { sig = sig.normalizeS(); recovery ^= 1; } const hashed = der ? sig.toDERRawBytes() : sig.toCompactRawBytes(); return recovered ? [hashed, recovery] : hashed; } async function sign(msgHash, privKey, opts = {}) { const { seed, m, d } = initSigArgs(msgHash, privKey, opts.extraEntropy); let sig; const drbg = new HmacDrbg(); await drbg.reseed(seed); while (!(sig = kmdToSig(await drbg.generate(), m, d))) await drbg.reseed(); return finalizeSig(sig, opts); } function signSync(msgHash, privKey, opts = {}) { const { seed, m, d } = initSigArgs(msgHash, privKey, opts.extraEntropy); let sig; const drbg = new HmacDrbg(); drbg.reseedSync(seed); while (!(sig = kmdToSig(drbg.generateSync(), m, d))) drbg.reseedSync(); return finalizeSig(sig, opts); } const vopts = { strict: true }; function verify(signature, msgHash, publicKey, opts = vopts) { let sig; try { sig = normalizeSignature(signature); msgHash = ensureBytes(msgHash); } catch (error) { return false; } const { r, s } = sig; if (opts.strict && sig.hasHighS()) return false; const h = truncateHash(msgHash); let P; try { P = normalizePublicKey(publicKey); } catch (error) { return false; } const { n } = CURVE; const sinv = invert(s, n); const u1 = mod(h * sinv, n); const u2 = mod(r * sinv, n); const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2); if (!R) return false; const v = mod(R.x, n); return v === r; } function schnorrChallengeFinalize(ch) { return mod(bytesToNumber(ch), CURVE.n); } class SchnorrSignature { constructor(r, s) { this.r = r; this.s = s; this.assertValidity(); } static fromHex(hex) { const bytes = ensureBytes(hex); if (bytes.length !== 64) throw new TypeError(`SchnorrSignature.fromHex: expected 64 bytes, not ${bytes.length}`); const r = bytesToNumber(bytes.subarray(0, 32)); const s = bytesToNumber(bytes.subarray(32, 64)); return new SchnorrSignature(r, s); } assertValidity() { const { r, s } = this; if (!isValidFieldElement(r) || !isWithinCurveOrder(s)) throw new Error('Invalid signature'); } toHex() { return numTo32bStr(this.r) + numTo32bStr(this.s); } toRawBytes() { return hexToBytes(this.toHex()); } } function schnorrGetPublicKey(privateKey) { return Point.fromPrivateKey(privateKey).toRawX(); } class InternalSchnorrSignature { constructor(message, privateKey, auxRand = utils.randomBytes()) { if (message == null) throw new TypeError(`sign: Expected valid message, not "${message}"`); this.m = ensureBytes(message); const { x, scalar } = this.getScalar(normalizePrivateKey(privateKey)); this.px = x; this.d = scalar; this.rand = ensureBytes(auxRand); if (this.rand.length !== 32) throw new TypeError('sign: Expected 32 bytes of aux randomness'); } getScalar(priv) { const point = Point.fromPrivateKey(priv); const scalar = point.hasEvenY() ? priv : CURVE.n - priv; return { point, scalar, x: point.toRawX() }; } initNonce(d, t0h) { return numTo32b(d ^ bytesToNumber(t0h)); } finalizeNonce(k0h) { const k0 = mod(bytesToNumber(k0h), CURVE.n); if (k0 === _0n) throw new Error('sign: Creation of signature failed. k is zero'); const { point: R, x: rx, scalar: k } = this.getScalar(k0); return { R, rx, k }; } finalizeSig(R, k, e, d) { return new SchnorrSignature(R.x, mod(k + e * d, CURVE.n)).toRawBytes(); } error() { throw new Error('sign: Invalid signature produced'); } async calc() { const { m, d, px, rand } = this; const tag = utils.taggedHash; const t = this.initNonce(d, await tag(TAGS.aux, rand)); const { R, rx, k } = this.finalizeNonce(await tag(TAGS.nonce, t, px, m)); const e = schnorrChallengeFinalize(await tag(TAGS.challenge, rx, px, m)); const sig = this.finalizeSig(R, k, e, d); if (!(await schnorrVerify(sig, m, px))) this.error(); return sig; } calcSync() { const { m, d, px, rand } = this; const tag = utils.taggedHashSync; const t = this.initNonce(d, tag(TAGS.aux, rand)); const { R, rx, k } = this.finalizeNonce(tag(TAGS.nonce, t, px, m)); const e = schnorrChallengeFinalize(tag(TAGS.challenge, rx, px, m)); const sig = this.finalizeSig(R, k, e, d); if (!schnorrVerifySync(sig, m, px)) this.error(); return sig; } } async function schnorrSign(msg, privKey, auxRand) { return new InternalSchnorrSignature(msg, privKey, auxRand).calc(); } function schnorrSignSync(msg, privKey, auxRand) { return new InternalSchnorrSignature(msg, privKey, auxRand).calcSync(); } function initSchnorrVerify(signature, message, publicKey) { const raw = signature instanceof SchnorrSignature; const sig = raw ? signature : SchnorrSignature.fromHex(signature); if (raw) sig.assertValidity(); return { ...sig, m: ensureBytes(message), P: normalizePublicKey(publicKey), }; } function finalizeSchnorrVerify(r, P, s, e) { const R = Point.BASE.multiplyAndAddUnsafe(P, normalizePrivateKey(s), mod(-e, CURVE.n)); if (!R || !R.hasEvenY() || R.x !== r) return false; return true; } async function schnorrVerify(signature, message, publicKey) { try { const { r, s, m, P } = initSchnorrVerify(signature, message, publicKey); const e = schnorrChallengeFinalize(await utils.taggedHash(TAGS.challenge, numTo32b(r), P.toRawX(), m)); return finalizeSchnorrVerify(r, P, s, e); } catch (error) { return false; } } function schnorrVerifySync(signature, message, publicKey) { try { const { r, s, m, P } = initSchnorrVerify(signature, message, publicKey); const e = schnorrChallengeFinalize(utils.taggedHashSync(TAGS.challenge, numTo32b(r), P.toRawX(), m)); return finalizeSchnorrVerify(r, P, s, e); } catch (error) { if (error instanceof ShaError) throw error; return false; } } const schnorr = { Signature: SchnorrSignature, getPublicKey: schnorrGetPublicKey, sign: schnorrSign, verify: schnorrVerify, signSync: schnorrSignSync, verifySync: schnorrVerifySync, }; Point.BASE._setWindowSize(8); const crypto = { node: nodeCrypto, web: typeof self === 'object' && 'crypto' in self ? self.crypto : undefined, }; const TAGS = { challenge: 'BIP0340/challenge', aux: 'BIP0340/aux', nonce: 'BIP0340/nonce', }; const TAGGED_HASH_PREFIXES = {}; var utils = { bytesToHex, hexToBytes, randomBytes, concatBytes, mod, invert, isValidPrivateKey(privateKey) { try { normalizePrivateKey(privateKey); return true; } catch (error) { return false; } }, _bigintTo32Bytes: numTo32b, _normalizePrivateKey: normalizePrivateKey, hashToPrivateKey: (hash) => { hash = ensureBytes(hash); if (hash.length < 40 || hash.length > 1024) throw new Error('Expected 40-1024 bytes of private key as per FIPS 186'); const num = mod(bytesToNumber(hash), CURVE.n - _1n) + _1n; return numTo32b(num); }, randomBytes: (bytesLength = 32) => { if (crypto.web) { return crypto.web.getRandomValues(new Uint8Array(bytesLength)); } else if (crypto.node) { const { randomBytes } = crypto.node; return Uint8Array.from(randomBytes(bytesLength)); } else { throw new Error("The environment doesn't have randomBytes function"); } }, randomPrivateKey: () => { return utils.hashToPrivateKey(utils.randomBytes(40)); }, sha256: async (...messages) => { if (crypto.web) { const buffer = await crypto.web.subtle.digest('SHA-256', concatBytes(...messages)); return new Uint8Array(buffer); } else if (crypto.node) { const { createHash } = crypto.node; const hash = createHash('sha256'); messages.forEach((m) => hash.update(m)); return Uint8Array.from(hash.digest()); } else { throw new Error("The environment doesn't have sha256 function"); } }, hmacSha256: async (key, ...messages) => { if (crypto.web) { const ckey = await crypto.web.subtle.importKey('raw', key, { name: 'HMAC', hash: { name: 'SHA-256' } }, false, ['sign']); const message = concatBytes(...messages); const buffer = await crypto.web.subtle.sign('HMAC', ckey, message); return new Uint8Array(buffer); } else if (crypto.node) { const { createHmac } = crypto.node; const hash = createHmac('sha256', key); messages.forEach((m) => hash.update(m)); return Uint8Array.from(hash.digest()); } else { throw new Error("The environment doesn't have hmac-sha256 function"); } }, sha256Sync: undefined, hmacSha256Sync: undefined, taggedHash: async (tag, ...messages) => { let tagP = TAGGED_HASH_PREFIXES[tag]; if (tagP === undefined) { const tagH = await utils.sha256(Uint8Array.from(tag, (c) => c.charCodeAt(0))); tagP = concatBytes(tagH, tagH); TAGGED_HASH_PREFIXES[tag] = tagP; } return utils.sha256(tagP, ...messages); }, taggedHashSync: (tag, ...messages) => { if (typeof _sha256Sync !== 'function') throw new ShaError('sha256Sync is undefined, you need to set it'); let tagP = TAGGED_HASH_PREFIXES[tag]; if (tagP === undefined) { const tagH = _sha256Sync(Uint8Array.from(tag, (c) => c.charCodeAt(0))); tagP = concatBytes(tagH, tagH); TAGGED_HASH_PREFIXES[tag] = tagP; } return _sha256Sync(tagP, ...messages); }, precompute(windowSize = 8, point = Point.BASE) { const cached = point === Point.BASE ? point : new Point(point.x, point.y); cached._setWindowSize(windowSize); cached.multiply(_3n); return cached; }, }; Object.defineProperties(utils, { sha256Sync: { configurable: false, get() { return _sha256Sync; }, set(val) { if (!_sha256Sync) _sha256Sync = val; }, }, hmacSha256Sync: { configurable: false, get() { return _hmacSha256Sync; }, set(val) { if (!_hmacSha256Sync) _hmacSha256Sync = val; }, }, }); //var secp = {}; secp.CURVE = CURVE; secp.Point = Point; secp.Signature = Signature; secp.getPublicKey = getPublicKey; secp.getSharedSecret = getSharedSecret; secp.recoverPublicKey = recoverPublicKey; secp.schnorr = schnorr; secp.sign = sign; secp.signSync = signSync; secp.utils = utils; secp.verify = verify; /****** * START OF HASH SECTION * * ******/ //hashmini = {}; /*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */ // Cast array to different type const u8 = (arr) => new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength); const u32 = (arr) => new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4)); // Cast array to view const createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength); // The rotate right (circular right shift) operation for uint32 const rotr = (word, shift) => (word << (32 - shift)) | (word >>> shift); const isLE = new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44; // There is almost no big endian hardware, but js typed arrays uses platform specific endianness. // So, just to be sure not to corrupt anything. if (!isLE) throw new Error('Non little-endian hardware is not supported'); var hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0')); /** * @example bytesToHex(Uint8Array.from([0xde, 0xad, 0xbe, 0xef])) */ function bytesToHex(uint8a) { // pre-caching improves the speed 6x if (!(uint8a instanceof Uint8Array)) throw new Error('Uint8Array expected'); let hex = ''; for (let i = 0; i < uint8a.length; i++) { hex += hexes[uint8a[i]]; } return hex; } /** * @example hexToBytes('deadbeef') */ function hexToBytes(hex) { if (typeof hex !== 'string') { throw new TypeError('hexToBytes: expected string, got ' + typeof hex); } if (hex.length % 2) throw new Error('hexToBytes: received invalid unpadded hex'); const array = new Uint8Array(hex.length / 2); for (let i = 0; i < array.length; i++) { const j = i * 2; const hexByte = hex.slice(j, j + 2); const byte = Number.parseInt(hexByte, 16); if (Number.isNaN(byte) || byte < 0) throw new Error('Invalid byte sequence'); array[i] = byte; } return array; } // There is no setImmediate in browser and setTimeout is slow. However, call to async function will return Promise // which will be fullfiled only on next scheduler queue processing step and this is exactly what we need. const nextTick = async () => { }; // Returns control to thread each 'tick' ms to avoid blocking async function asyncLoop(iters, tick, cb) { let ts = Date.now(); for (let i = 0; i < iters; i++) { cb(i); // Date.now() is not monotonic, so in case if clock goes backwards we return return control too const diff = Date.now() - ts; if (diff >= 0 && diff < tick) continue; await nextTick(); ts += diff; } } function utf8ToBytes(str) { if (typeof str !== 'string') { throw new TypeError(`utf8ToBytes expected string, got ${typeof str}`); } return new TextEncoder().encode(str); } function toBytes(data) { if (typeof data === 'string') data = utf8ToBytes(data); if (!(data instanceof Uint8Array)) throw new TypeError(`Expected input type is Uint8Array (got ${typeof data})`); return data; } // For runtime check if class implements interface class Hash { // Safe version that clones internal state clone() { return this._cloneInto(); } } // Check if object doens't have custom constructor (like Uint8Array/Array) var isPlainObject = (obj) => Object.prototype.toString.call(obj) === '[object Object]' && obj.constructor === Object; function checkOpts(defaults, opts) { if (opts !== undefined && (typeof opts !== 'object' || !isPlainObject(opts))) throw new TypeError('Options should be object or undefined'); const merged = Object.assign(defaults, opts); return merged; } function wrapConstructor(hashConstructor) { const hashC = (message) => hashConstructor().update(toBytes(message)).digest(); const tmp = hashConstructor(); hashC.outputLen = tmp.outputLen; hashC.blockLen = tmp.blockLen; hashC.create = () => hashConstructor(); return hashC; } function wrapConstructorWithOpts(hashCons) { const hashC = (msg, opts) => hashCons(opts).update(toBytes(msg)).digest(); const tmp = hashCons({}); hashC.outputLen = tmp.outputLen; hashC.blockLen = tmp.blockLen; hashC.create = (opts) => hashCons(opts); return hashC; } /** * Secure PRNG */ function randomBytes(bytesLength = 32) { if (crypto.crypto.web) { return crypto.crypto.web.getRandomValues(new Uint8Array(bytesLength)); } else if (crypto.crypto.node) { return new Uint8Array(crypto.crypto.node.randomBytes(bytesLength).buffer); } else { throw new Error("The environment doesn't have randomBytes function"); } } function number(n) { if (!Number.isSafeInteger(n) || n < 0) throw new Error(`Wrong positive integer: ${n}`); } function bool(b) { if (typeof b !== 'boolean') throw new Error(`Expected boolean, not ${b}`); } function bytes(b, ...lengths) { if (!(b instanceof Uint8Array)) throw new TypeError('Expected Uint8Array'); if (lengths.length > 0 && !lengths.includes(b.length)) throw new TypeError(`Expected Uint8Array of length ${lengths}, not of length=${b.length}`); } function isNumber(inputValue) { return !isNaN(parseFloat(inputValue)) && isFinite(inputValue); } function hash(hash) { if (typeof hash !== 'function' || typeof hash.create !== 'function') throw new Error('Hash should be wrapped by utils.wrapConstructor'); //ROHIT I put it inTry catch block because it was crashing the Transaction Signature try { isNumber(hash.outputLen); isNumber(hash.blockLen); } catch (error) { return; } } /* //ORIGINAL FUNCTION function hash(hash) { if (typeof hash !== 'function' || typeof hash.create !== 'function') throw new Error('Hash should be wrapped by utils.wrapConstructor'); number(hash.outputLen); number(hash.blockLen); }*/ function exists(instance, checkFinished = true) { if (instance.destroyed) throw new Error('Hash instance has been destroyed'); if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called'); } function output(out, instance) { bytes(out); const min = instance.outputLen; if (out.length < min) { throw new Error(`digestInto() expects output buffer of length at least ${min}`); } } const assert = { number, bool, bytes, hash, exists, output, }; // prettier-ignore const SIGMA$1 = new Uint8Array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4, 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8, 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13, 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9, 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11, 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10, 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5, 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0, // For BLAKE2b, the two extra permutations for rounds 10 and 11 are SIGMA[10..11] = SIGMA[0..1]. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, ]); class BLAKE2 extends Hash { constructor(blockLen, outputLen, opts = {}, keyLen, saltLen, persLen) { super(); this.blockLen = blockLen; this.outputLen = outputLen; this.length = 0; this.pos = 0; this.finished = false; this.destroyed = false; assert.number(blockLen); assert.number(outputLen); assert.number(keyLen); if (outputLen < 0 || outputLen > keyLen) throw new Error('Blake2: outputLen bigger than keyLen'); if (opts.key !== undefined && (opts.key.length < 1 || opts.key.length > keyLen)) throw new Error(`Key should be up 1..${keyLen} byte long or undefined`); if (opts.salt !== undefined && opts.salt.length !== saltLen) throw new Error(`Salt should be ${saltLen} byte long or undefined`); if (opts.personalization !== undefined && opts.personalization.length !== persLen) throw new Error(`Personalization should be ${persLen} byte long or undefined`); this.buffer32 = u32((this.buffer = new Uint8Array(blockLen))); } update(data) { assert.exists(this); // Main difference with other hashes: there is flag for last block, // so we cannot process current block before we know that there // is the next one. This significantly complicates logic and reduces ability // to do zero-copy processing const { blockLen, buffer, buffer32 } = this; data = toBytes(data); const len = data.length; for (let pos = 0; pos < len;) { // If buffer is full and we still have input (don't process last block, same as blake2s) if (this.pos === blockLen) { this.compress(buffer32, 0, false); this.pos = 0; } const take = Math.min(blockLen - this.pos, len - pos); const dataOffset = data.byteOffset + pos; // full block && aligned to 4 bytes && not last in input if (take === blockLen && !(dataOffset % 4) && pos + take < len) { const data32 = new Uint32Array(data.buffer, dataOffset, Math.floor((len - pos) / 4)); for (let pos32 = 0; pos + blockLen < len; pos32 += buffer32.length, pos += blockLen) { this.length += blockLen; this.compress(data32, pos32, false); } continue; } buffer.set(data.subarray(pos, pos + take), this.pos); this.pos += take; this.length += take; pos += take; } return this; } digestInto(out) { assert.exists(this); assert.output(out, this); const { pos, buffer32 } = this; this.finished = true; // Padding this.buffer.subarray(pos).fill(0); this.compress(buffer32, 0, true); const out32 = u32(out); this.get().forEach((v, i) => (out32[i] = v)); } digest() { const { buffer, outputLen } = this; this.digestInto(buffer); const res = buffer.slice(0, outputLen); this.destroy(); return res; } _cloneInto(to) { const { buffer, length, finished, destroyed, outputLen, pos } = this; to || (to = new this.constructor({ dkLen: outputLen })); to.set(...this.get()); to.length = length; to.finished = finished; to.destroyed = destroyed; to.outputLen = outputLen; to.buffer.set(buffer); to.pos = pos; return to; } } const U32_MASK64 = BigInt(2 ** 32 - 1); const _32n = BigInt(32); // We are not using BigUint64Array, because they are extremely slow as per 2022 function fromBig(n, le = false) { if (le) return { h: Number(n & U32_MASK64), l: Number((n >> _32n) & U32_MASK64) }; return { h: Number((n >> _32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 }; } function split(lst, le = false) { let Ah = new Uint32Array(lst.length); let Al = new Uint32Array(lst.length); for (let i = 0; i < lst.length; i++) { const { h, l } = fromBig(lst[i], le); [Ah[i], Al[i]] = [h, l]; } return [Ah, Al]; } const toBig = (h, l) => (BigInt(h >>> 0) << _32n) | BigInt(l >>> 0); // for Shift in [0, 32) const shrSH = (h, l, s) => h >>> s; const shrSL = (h, l, s) => (h << (32 - s)) | (l >>> s); // Right rotate for Shift in [1, 32) const rotrSH = (h, l, s) => (h >>> s) | (l << (32 - s)); const rotrSL = (h, l, s) => (h << (32 - s)) | (l >>> s); // Right rotate for Shift in (32, 64), NOTE: 32 is special case. const rotrBH = (h, l, s) => (h << (64 - s)) | (l >>> (s - 32)); const rotrBL = (h, l, s) => (h >>> (s - 32)) | (l << (64 - s)); // Right rotate for shift===32 (just swaps l&h) const rotr32H = (h, l) => l; const rotr32L = (h, l) => h; // Left rotate for Shift in [1, 32) const rotlSH = (h, l, s) => (h << s) | (l >>> (32 - s)); const rotlSL = (h, l, s) => (l << s) | (h >>> (32 - s)); // Left rotate for Shift in (32, 64), NOTE: 32 is special case. const rotlBH = (h, l, s) => (l << (s - 32)) | (h >>> (64 - s)); const rotlBL = (h, l, s) => (h << (s - 32)) | (l >>> (64 - s)); // JS uses 32-bit signed integers for bitwise operations which means we cannot // simple take carry out of low bit sum by shift, we need to use division. // Removing "export" has 5% perf penalty -_- function add(Ah, Al, Bh, Bl) { const l = (Al >>> 0) + (Bl >>> 0); return { h: (Ah + Bh + ((l / 2 ** 32) | 0)) | 0, l: l | 0 }; } // Addition with more than 2 elements const add3L = (Al, Bl, Cl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0); const add3H = (low, Ah, Bh, Ch) => (Ah + Bh + Ch + ((low / 2 ** 32) | 0)) | 0; const add4L = (Al, Bl, Cl, Dl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0); const add4H = (low, Ah, Bh, Ch, Dh) => (Ah + Bh + Ch + Dh + ((low / 2 ** 32) | 0)) | 0; const add5L = (Al, Bl, Cl, Dl, El) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0); const add5H = (low, Ah, Bh, Ch, Dh, Eh) => (Ah + Bh + Ch + Dh + Eh + ((low / 2 ** 32) | 0)) | 0; // prettier-ignore const u64 = { fromBig, split, toBig, shrSH, shrSL, rotrSH, rotrSL, rotrBH, rotrBL, rotr32H, rotr32L, rotlSH, rotlSL, rotlBH, rotlBL, add, add3L, add3H, add4L, add4H, add5H, add5L, }; // Same as SHA-512 but LE // prettier-ignore const IV$2 = new Uint32Array([ 0xf3bcc908, 0x6a09e667, 0x84caa73b, 0xbb67ae85, 0xfe94f82b, 0x3c6ef372, 0x5f1d36f1, 0xa54ff53a, 0xade682d1, 0x510e527f, 0x2b3e6c1f, 0x9b05688c, 0xfb41bd6b, 0x1f83d9ab, 0x137e2179, 0x5be0cd19 ]); // Temporary buffer const BUF$1 = new Uint32Array(32); // Mixing function G splitted in two halfs function G1$1(a, b, c, d, msg, x) { // NOTE: V is LE here const Xl = msg[x], Xh = msg[x + 1]; // prettier-ignore let Al = BUF$1[2 * a], Ah = BUF$1[2 * a + 1]; // prettier-ignore let Bl = BUF$1[2 * b], Bh = BUF$1[2 * b + 1]; // prettier-ignore let Cl = BUF$1[2 * c], Ch = BUF$1[2 * c + 1]; // prettier-ignore let Dl = BUF$1[2 * d], Dh = BUF$1[2 * d + 1]; // prettier-ignore // v[a] = (v[a] + v[b] + x) | 0; let ll = u64.add3L(Al, Bl, Xl); Ah = u64.add3H(ll, Ah, Bh, Xh); Al = ll | 0; // v[d] = rotr(v[d] ^ v[a], 32) ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al }); ({ Dh, Dl } = { Dh: u64.rotr32H(Dh, Dl), Dl: u64.rotr32L(Dh, Dl) }); // v[c] = (v[c] + v[d]) | 0; ({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl)); // v[b] = rotr(v[b] ^ v[c], 24) ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl }); ({ Bh, Bl } = { Bh: u64.rotrSH(Bh, Bl, 24), Bl: u64.rotrSL(Bh, Bl, 24) }); (BUF$1[2 * a] = Al), (BUF$1[2 * a + 1] = Ah); (BUF$1[2 * b] = Bl), (BUF$1[2 * b + 1] = Bh); (BUF$1[2 * c] = Cl), (BUF$1[2 * c + 1] = Ch); (BUF$1[2 * d] = Dl), (BUF$1[2 * d + 1] = Dh); } function G2$1(a, b, c, d, msg, x) { // NOTE: V is LE here const Xl = msg[x], Xh = msg[x + 1]; // prettier-ignore let Al = BUF$1[2 * a], Ah = BUF$1[2 * a + 1]; // prettier-ignore let Bl = BUF$1[2 * b], Bh = BUF$1[2 * b + 1]; // prettier-ignore let Cl = BUF$1[2 * c], Ch = BUF$1[2 * c + 1]; // prettier-ignore let Dl = BUF$1[2 * d], Dh = BUF$1[2 * d + 1]; // prettier-ignore // v[a] = (v[a] + v[b] + x) | 0; let ll = u64.add3L(Al, Bl, Xl); Ah = u64.add3H(ll, Ah, Bh, Xh); Al = ll | 0; // v[d] = rotr(v[d] ^ v[a], 16) ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al }); ({ Dh, Dl } = { Dh: u64.rotrSH(Dh, Dl, 16), Dl: u64.rotrSL(Dh, Dl, 16) }); // v[c] = (v[c] + v[d]) | 0; ({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl)); // v[b] = rotr(v[b] ^ v[c], 63) ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl }); ({ Bh, Bl } = { Bh: u64.rotrBH(Bh, Bl, 63), Bl: u64.rotrBL(Bh, Bl, 63) }); (BUF$1[2 * a] = Al), (BUF$1[2 * a + 1] = Ah); (BUF$1[2 * b] = Bl), (BUF$1[2 * b + 1] = Bh); (BUF$1[2 * c] = Cl), (BUF$1[2 * c + 1] = Ch); (BUF$1[2 * d] = Dl), (BUF$1[2 * d + 1] = Dh); } class BLAKE2b extends BLAKE2 { constructor(opts = {}) { super(128, opts.dkLen === undefined ? 64 : opts.dkLen, opts, 64, 16, 16); // Same as SHA-512, but LE this.v0l = IV$2[0] | 0; this.v0h = IV$2[1] | 0; this.v1l = IV$2[2] | 0; this.v1h = IV$2[3] | 0; this.v2l = IV$2[4] | 0; this.v2h = IV$2[5] | 0; this.v3l = IV$2[6] | 0; this.v3h = IV$2[7] | 0; this.v4l = IV$2[8] | 0; this.v4h = IV$2[9] | 0; this.v5l = IV$2[10] | 0; this.v5h = IV$2[11] | 0; this.v6l = IV$2[12] | 0; this.v6h = IV$2[13] | 0; this.v7l = IV$2[14] | 0; this.v7h = IV$2[15] | 0; const keyLength = opts.key ? opts.key.length : 0; this.v0l ^= this.outputLen | (keyLength << 8) | (0x01 << 16) | (0x01 << 24); if (opts.salt) { const salt = u32(toBytes(opts.salt)); this.v4l ^= salt[0]; this.v4h ^= salt[1]; this.v5l ^= salt[2]; this.v5h ^= salt[3]; } if (opts.personalization) { const pers = u32(toBytes(opts.personalization)); this.v6l ^= pers[0]; this.v6h ^= pers[1]; this.v7l ^= pers[2]; this.v7h ^= pers[3]; } if (opts.key) { // Pad to blockLen and update const tmp = new Uint8Array(this.blockLen); tmp.set(toBytes(opts.key)); this.update(tmp); } } // prettier-ignore get() { let { v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h } = this; return [v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h]; } // prettier-ignore set(v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h) { this.v0l = v0l | 0; this.v0h = v0h | 0; this.v1l = v1l | 0; this.v1h = v1h | 0; this.v2l = v2l | 0; this.v2h = v2h | 0; this.v3l = v3l | 0; this.v3h = v3h | 0; this.v4l = v4l | 0; this.v4h = v4h | 0; this.v5l = v5l | 0; this.v5h = v5h | 0; this.v6l = v6l | 0; this.v6h = v6h | 0; this.v7l = v7l | 0; this.v7h = v7h | 0; } compress(msg, offset, isLast) { this.get().forEach((v, i) => (BUF$1[i] = v)); // First half from state. BUF$1.set(IV$2, 16); // Second half from IV. let { h, l } = u64.fromBig(BigInt(this.length)); BUF$1[24] = IV$2[8] ^ l; // Low word of the offset. BUF$1[25] = IV$2[9] ^ h; // High word. // Invert all bits for last block if (isLast) { BUF$1[28] = ~BUF$1[28]; BUF$1[29] = ~BUF$1[29]; } let j = 0; const s = SIGMA$1; for (let i = 0; i < 12; i++) { G1$1(0, 4, 8, 12, msg, offset + 2 * s[j++]); G2$1(0, 4, 8, 12, msg, offset + 2 * s[j++]); G1$1(1, 5, 9, 13, msg, offset + 2 * s[j++]); G2$1(1, 5, 9, 13, msg, offset + 2 * s[j++]); G1$1(2, 6, 10, 14, msg, offset + 2 * s[j++]); G2$1(2, 6, 10, 14, msg, offset + 2 * s[j++]); G1$1(3, 7, 11, 15, msg, offset + 2 * s[j++]); G2$1(3, 7, 11, 15, msg, offset + 2 * s[j++]); G1$1(0, 5, 10, 15, msg, offset + 2 * s[j++]); G2$1(0, 5, 10, 15, msg, offset + 2 * s[j++]); G1$1(1, 6, 11, 12, msg, offset + 2 * s[j++]); G2$1(1, 6, 11, 12, msg, offset + 2 * s[j++]); G1$1(2, 7, 8, 13, msg, offset + 2 * s[j++]); G2$1(2, 7, 8, 13, msg, offset + 2 * s[j++]); G1$1(3, 4, 9, 14, msg, offset + 2 * s[j++]); G2$1(3, 4, 9, 14, msg, offset + 2 * s[j++]); } this.v0l ^= BUF$1[0] ^ BUF$1[16]; this.v0h ^= BUF$1[1] ^ BUF$1[17]; this.v1l ^= BUF$1[2] ^ BUF$1[18]; this.v1h ^= BUF$1[3] ^ BUF$1[19]; this.v2l ^= BUF$1[4] ^ BUF$1[20]; this.v2h ^= BUF$1[5] ^ BUF$1[21]; this.v3l ^= BUF$1[6] ^ BUF$1[22]; this.v3h ^= BUF$1[7] ^ BUF$1[23]; this.v4l ^= BUF$1[8] ^ BUF$1[24]; this.v4h ^= BUF$1[9] ^ BUF$1[25]; this.v5l ^= BUF$1[10] ^ BUF$1[26]; this.v5h ^= BUF$1[11] ^ BUF$1[27]; this.v6l ^= BUF$1[12] ^ BUF$1[28]; this.v6h ^= BUF$1[13] ^ BUF$1[29]; this.v7l ^= BUF$1[14] ^ BUF$1[30]; this.v7h ^= BUF$1[15] ^ BUF$1[31]; BUF$1.fill(0); } destroy() { this.destroyed = true; this.buffer32.fill(0); this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } } /** * BLAKE2b - optimized for 64-bit platforms. JS doesn't have uint64, so it's slower than BLAKE2s. * @param msg - message that would be hashed * @param opts - dkLen, key, salt, personalization */ const blake2b = wrapConstructorWithOpts((opts) => new BLAKE2b(opts)); // Initial state: // first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19) // same as SHA-256 // prettier-ignore const IV$1 = new Uint32Array([ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ]); // Mixing function G splitted in two halfs function G1(a, b, c, d, x) { a = (a + b + x) | 0; d = rotr(d ^ a, 16); c = (c + d) | 0; b = rotr(b ^ c, 12); return { a, b, c, d }; } function G2(a, b, c, d, x) { a = (a + b + x) | 0; d = rotr(d ^ a, 8); c = (c + d) | 0; b = rotr(b ^ c, 7); return { a, b, c, d }; } // prettier-ignore function compress(s, offset, msg, rounds, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) { let j = 0; for (let i = 0; i < rounds; i++) { ({ a: v0, b: v4, c: v8, d: v12 } = G1(v0, v4, v8, v12, msg[offset + s[j++]])); ({ a: v0, b: v4, c: v8, d: v12 } = G2(v0, v4, v8, v12, msg[offset + s[j++]])); ({ a: v1, b: v5, c: v9, d: v13 } = G1(v1, v5, v9, v13, msg[offset + s[j++]])); ({ a: v1, b: v5, c: v9, d: v13 } = G2(v1, v5, v9, v13, msg[offset + s[j++]])); ({ a: v2, b: v6, c: v10, d: v14 } = G1(v2, v6, v10, v14, msg[offset + s[j++]])); ({ a: v2, b: v6, c: v10, d: v14 } = G2(v2, v6, v10, v14, msg[offset + s[j++]])); ({ a: v3, b: v7, c: v11, d: v15 } = G1(v3, v7, v11, v15, msg[offset + s[j++]])); ({ a: v3, b: v7, c: v11, d: v15 } = G2(v3, v7, v11, v15, msg[offset + s[j++]])); ({ a: v0, b: v5, c: v10, d: v15 } = G1(v0, v5, v10, v15, msg[offset + s[j++]])); ({ a: v0, b: v5, c: v10, d: v15 } = G2(v0, v5, v10, v15, msg[offset + s[j++]])); ({ a: v1, b: v6, c: v11, d: v12 } = G1(v1, v6, v11, v12, msg[offset + s[j++]])); ({ a: v1, b: v6, c: v11, d: v12 } = G2(v1, v6, v11, v12, msg[offset + s[j++]])); ({ a: v2, b: v7, c: v8, d: v13 } = G1(v2, v7, v8, v13, msg[offset + s[j++]])); ({ a: v2, b: v7, c: v8, d: v13 } = G2(v2, v7, v8, v13, msg[offset + s[j++]])); ({ a: v3, b: v4, c: v9, d: v14 } = G1(v3, v4, v9, v14, msg[offset + s[j++]])); ({ a: v3, b: v4, c: v9, d: v14 } = G2(v3, v4, v9, v14, msg[offset + s[j++]])); } return { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 }; } class BLAKE2s extends BLAKE2 { constructor(opts = {}) { super(64, opts.dkLen === undefined ? 32 : opts.dkLen, opts, 32, 8, 8); // Internal state, same as SHA-256 this.v0 = IV$1[0] | 0; this.v1 = IV$1[1] | 0; this.v2 = IV$1[2] | 0; this.v3 = IV$1[3] | 0; this.v4 = IV$1[4] | 0; this.v5 = IV$1[5] | 0; this.v6 = IV$1[6] | 0; this.v7 = IV$1[7] | 0; const keyLength = opts.key ? opts.key.length : 0; this.v0 ^= this.outputLen | (keyLength << 8) | (0x01 << 16) | (0x01 << 24); if (opts.salt) { const salt = u32(toBytes(opts.salt)); this.v4 ^= salt[0]; this.v5 ^= salt[1]; } if (opts.personalization) { const pers = u32(toBytes(opts.personalization)); this.v6 ^= pers[0]; this.v7 ^= pers[1]; } if (opts.key) { // Pad to blockLen and update const tmp = new Uint8Array(this.blockLen); tmp.set(toBytes(opts.key)); this.update(tmp); } } get() { const { v0, v1, v2, v3, v4, v5, v6, v7 } = this; return [v0, v1, v2, v3, v4, v5, v6, v7]; } // prettier-ignore set(v0, v1, v2, v3, v4, v5, v6, v7) { this.v0 = v0 | 0; this.v1 = v1 | 0; this.v2 = v2 | 0; this.v3 = v3 | 0; this.v4 = v4 | 0; this.v5 = v5 | 0; this.v6 = v6 | 0; this.v7 = v7 | 0; } compress(msg, offset, isLast) { const { h, l } = u64.fromBig(BigInt(this.length)); // prettier-ignore const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } = compress(SIGMA$1, offset, msg, 10, this.v0, this.v1, this.v2, this.v3, this.v4, this.v5, this.v6, this.v7, IV$1[0], IV$1[1], IV$1[2], IV$1[3], l ^ IV$1[4], h ^ IV$1[5], isLast ? ~IV$1[6] : IV$1[6], IV$1[7]); this.v0 ^= v0 ^ v8; this.v1 ^= v1 ^ v9; this.v2 ^= v2 ^ v10; this.v3 ^= v3 ^ v11; this.v4 ^= v4 ^ v12; this.v5 ^= v5 ^ v13; this.v6 ^= v6 ^ v14; this.v7 ^= v7 ^ v15; } destroy() { this.destroyed = true; this.buffer32.fill(0); this.set(0, 0, 0, 0, 0, 0, 0, 0); } } /** * BLAKE2s - optimized for 32-bit platforms. JS doesn't have uint64, so it's faster than BLAKE2b. * @param msg - message that would be hashed * @param opts - dkLen, key, salt, personalization */ const blake2s = wrapConstructorWithOpts((opts) => new BLAKE2s(opts)); // Flag bitset var Flags; (function (Flags) { Flags[Flags["CHUNK_START"] = 1] = "CHUNK_START"; Flags[Flags["CHUNK_END"] = 2] = "CHUNK_END"; Flags[Flags["PARENT"] = 4] = "PARENT"; Flags[Flags["ROOT"] = 8] = "ROOT"; Flags[Flags["KEYED_HASH"] = 16] = "KEYED_HASH"; Flags[Flags["DERIVE_KEY_CONTEXT"] = 32] = "DERIVE_KEY_CONTEXT"; Flags[Flags["DERIVE_KEY_MATERIAL"] = 64] = "DERIVE_KEY_MATERIAL"; })(Flags || (Flags = {})); const SIGMA = (() => { const Id = Array.from({ length: 16 }, (_, i) => i); const permute = (arr) => [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8].map((i) => arr[i]); const res = []; for (let i = 0, v = Id; i < 7; i++, v = permute(v)) res.push(...v); return Uint8Array.from(res); })(); // Why is this so slow? It should be 6x faster than blake2b. // - There is only 30% reduction in number of rounds from blake2s // - This function uses tree mode to achive parallelisation via SIMD and threading, // however in JS we don't have threads and SIMD, so we get only overhead from tree structure // - It is possible to speed it up via Web Workers, hovewer it will make code singnificantly more // complicated, which we are trying to avoid, since this library is intended to be used // for cryptographic purposes. Also, parallelization happens only on chunk level (1024 bytes), // which won't really benefit small inputs. class BLAKE3 extends BLAKE2 { constructor(opts = {}, flags = 0) { super(64, opts.dkLen === undefined ? 32 : opts.dkLen, {}, Number.MAX_SAFE_INTEGER, 0, 0); this.flags = 0 | 0; this.chunkPos = 0; // Position of current block in chunk this.chunksDone = 0; // How many chunks we already have this.stack = []; // Output this.posOut = 0; this.bufferOut32 = new Uint32Array(16); this.chunkOut = 0; // index of output chunk this.enableXOF = true; this.outputLen = opts.dkLen === undefined ? 32 : opts.dkLen; assert.number(this.outputLen); if (opts.key !== undefined && opts.context !== undefined) throw new Error('Blake3: only key or context can be specified at same time'); else if (opts.key !== undefined) { const key = toBytes(opts.key); if (key.length !== 32) throw new Error('Blake3: key should be 32 byte'); this.IV = u32(key); this.flags = flags | Flags.KEYED_HASH; } else if (opts.context !== undefined) { const context_key = new BLAKE3({ dkLen: 32 }, Flags.DERIVE_KEY_CONTEXT) .update(opts.context) .digest(); this.IV = u32(context_key); this.flags = flags | Flags.DERIVE_KEY_MATERIAL; } else { this.IV = IV$1.slice(); this.flags = flags; } this.state = this.IV.slice(); this.bufferOut = u8(this.bufferOut32); } // Unused get() { return []; } set() { } b2Compress(counter, flags, buf, bufPos = 0) { const { state: s, pos } = this; const { h, l } = u64.fromBig(BigInt(counter), true); // prettier-ignore const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } = compress(SIGMA, bufPos, buf, 7, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], IV$1[0], IV$1[1], IV$1[2], IV$1[3], h, l, pos, flags); s[0] = v0 ^ v8; s[1] = v1 ^ v9; s[2] = v2 ^ v10; s[3] = v3 ^ v11; s[4] = v4 ^ v12; s[5] = v5 ^ v13; s[6] = v6 ^ v14; s[7] = v7 ^ v15; } compress(buf, bufPos = 0, isLast = false) { // Compress last block let flags = this.flags; if (!this.chunkPos) flags |= Flags.CHUNK_START; if (this.chunkPos === 15 || isLast) flags |= Flags.CHUNK_END; if (!isLast) this.pos = this.blockLen; this.b2Compress(this.chunksDone, flags, buf, bufPos); this.chunkPos += 1; // If current block is last in chunk (16 blocks), then compress chunks if (this.chunkPos === 16 || isLast) { let chunk = this.state; this.state = this.IV.slice(); // If not the last one, compress only when there are trailing zeros in chunk counter // chunks used as binary tree where current stack is path. Zero means current leaf is finished and can be compressed. // 1 (001) - leaf not finished (just push current chunk to stack) // 2 (010) - leaf finished at depth=1 (merge with last elm on stack and push back) // 3 (011) - last leaf not finished // 4 (100) - leafs finished at depth=1 and depth=2 for (let last, chunks = this.chunksDone + 1; isLast || !(chunks & 1); chunks >>= 1) { if (!(last = this.stack.pop())) break; this.buffer32.set(last, 0); this.buffer32.set(chunk, 8); this.pos = this.blockLen; this.b2Compress(0, this.flags | Flags.PARENT, this.buffer32, 0); chunk = this.state; this.state = this.IV.slice(); } this.chunksDone++; this.chunkPos = 0; this.stack.push(chunk); } this.pos = 0; } _cloneInto(to) { to = super._cloneInto(to); const { IV, flags, state, chunkPos, posOut, chunkOut, stack, chunksDone } = this; to.state.set(state.slice()); to.stack = stack.map((i) => Uint32Array.from(i)); to.IV.set(IV); to.flags = flags; to.chunkPos = chunkPos; to.chunksDone = chunksDone; to.posOut = posOut; to.chunkOut = chunkOut; to.enableXOF = this.enableXOF; to.bufferOut32.set(this.bufferOut32); return to; } destroy() { this.destroyed = true; this.state.fill(0); this.buffer32.fill(0); this.IV.fill(0); this.bufferOut32.fill(0); for (let i of this.stack) i.fill(0); } // Same as b2Compress, but doesn't modify state and returns 16 u32 array (instead of 8) b2CompressOut() { const { state: s, pos, flags, buffer32, bufferOut32: out32 } = this; const { h, l } = u64.fromBig(BigInt(this.chunkOut++)); // prettier-ignore const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } = compress(SIGMA, 0, buffer32, 7, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], IV$1[0], IV$1[1], IV$1[2], IV$1[3], l, h, pos, flags); out32[0] = v0 ^ v8; out32[1] = v1 ^ v9; out32[2] = v2 ^ v10; out32[3] = v3 ^ v11; out32[4] = v4 ^ v12; out32[5] = v5 ^ v13; out32[6] = v6 ^ v14; out32[7] = v7 ^ v15; out32[8] = s[0] ^ v8; out32[9] = s[1] ^ v9; out32[10] = s[2] ^ v10; out32[11] = s[3] ^ v11; out32[12] = s[4] ^ v12; out32[13] = s[5] ^ v13; out32[14] = s[6] ^ v14; out32[15] = s[7] ^ v15; this.posOut = 0; } finish() { if (this.finished) return; this.finished = true; // Padding this.buffer.fill(0, this.pos); // Process last chunk let flags = this.flags | Flags.ROOT; if (this.stack.length) { flags |= Flags.PARENT; this.compress(this.buffer32, 0, true); this.chunksDone = 0; this.pos = this.blockLen; } else { flags |= (!this.chunkPos ? Flags.CHUNK_START : 0) | Flags.CHUNK_END; } this.flags = flags; this.b2CompressOut(); } writeInto(out) { assert.exists(this, false); assert.bytes(out); this.finish(); const { blockLen, bufferOut } = this; for (let pos = 0, len = out.length; pos < len;) { if (this.posOut >= blockLen) this.b2CompressOut(); const take = Math.min(blockLen - this.posOut, len - pos); out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos); this.posOut += take; pos += take; } return out; } xofInto(out) { if (!this.enableXOF) throw new Error('XOF is not possible after digest call'); return this.writeInto(out); } xof(bytes) { assert.number(bytes); return this.xofInto(new Uint8Array(bytes)); } digestInto(out) { assert.output(out, this); if (this.finished) throw new Error('digest() was already called'); this.enableXOF = false; this.writeInto(out); this.destroy(); return out; } digest() { return this.digestInto(new Uint8Array(this.outputLen)); } } /** * BLAKE3 hash function. * @param msg - message that would be hashed * @param opts - dkLen, key, context */ const blake3 = wrapConstructorWithOpts((opts) => new BLAKE3(opts)); // HMAC (RFC 2104) class HMAC extends Hash { constructor(hash, _key) { super(); this.finished = false; this.destroyed = false; assert.hash(hash); const key = toBytes(_key); this.iHash = hash.create(); if (typeof this.iHash.update !== 'function') throw new TypeError('Expected instance of class which extends utils.Hash'); this.blockLen = this.iHash.blockLen; this.outputLen = this.iHash.outputLen; const blockLen = this.blockLen; const pad = new Uint8Array(blockLen); // blockLen can be bigger than outputLen pad.set(key.length > blockLen ? hash.create().update(key).digest() : key); for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36; this.iHash.update(pad); // By doing update (processing of first block) of outer hash here we can re-use it between multiple calls via clone this.oHash = hash.create(); // Undo internal XOR && apply outer XOR for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36 ^ 0x5c; this.oHash.update(pad); pad.fill(0); } update(buf) { assert.exists(this); this.iHash.update(buf); return this; } digestInto(out) { assert.exists(this); assert.bytes(out, this.outputLen); this.finished = true; this.iHash.digestInto(out); this.oHash.update(out); this.oHash.digestInto(out); this.destroy(); } digest() { const out = new Uint8Array(this.oHash.outputLen); this.digestInto(out); return out; } _cloneInto(to) { // Create new instance without calling constructor since key already in state and we don't know it. to || (to = Object.create(Object.getPrototypeOf(this), {})); const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this; to = to; to.finished = finished; to.destroyed = destroyed; to.blockLen = blockLen; to.outputLen = outputLen; to.oHash = oHash._cloneInto(to.oHash); to.iHash = iHash._cloneInto(to.iHash); return to; } destroy() { this.destroyed = true; this.oHash.destroy(); this.iHash.destroy(); } } /** * HMAC: RFC2104 message authentication code. * @param hash - function that would be used e.g. sha256 * @param key - message key * @param message - message data */ const hmac = (hash, key, message) => new HMAC(hash, key).update(message).digest(); hmac.create = (hash, key) => new HMAC(hash, key); // HKDF (RFC 5869) // https://soatok.blog/2021/11/17/understanding-hkdf/ /** * HKDF-Extract(IKM, salt) -> PRK * Arguments position differs from spec (IKM is first one, since it is not optional) * @param hash * @param ikm * @param salt * @returns */ function extract(hash, ikm, salt) { assert.hash(hash); // NOTE: some libraries treat zero-length array as 'not provided'; // we don't, since we have undefined as 'not provided' // https://github.com/RustCrypto/KDFs/issues/15 if (salt === undefined) salt = new Uint8Array(hash.outputLen); // if not provided, it is set to a string of HashLen zeros return hmac(hash, toBytes(salt), toBytes(ikm)); } // HKDF-Expand(PRK, info, L) -> OKM const HKDF_COUNTER = new Uint8Array([0]); const EMPTY_BUFFER = new Uint8Array(); /** * HKDF-expand from the spec. * @param prk - a pseudorandom key of at least HashLen octets (usually, the output from the extract step) * @param info - optional context and application specific information (can be a zero-length string) * @param length - length of output keying material in octets */ function expand(hash, prk, info, length = 32) { assert.hash(hash); assert.number(length); if (length > 255 * hash.outputLen) throw new Error('Length should be <= 255*HashLen'); const blocks = Math.ceil(length / hash.outputLen); if (info === undefined) info = EMPTY_BUFFER; // first L(ength) octets of T const okm = new Uint8Array(blocks * hash.outputLen); // Re-use HMAC instance between blocks const HMAC = hmac.create(hash, prk); const HMACTmp = HMAC._cloneInto(); const T = new Uint8Array(HMAC.outputLen); for (let counter = 0; counter < blocks; counter++) { HKDF_COUNTER[0] = counter + 1; // T(0) = empty string (zero length) // T(N) = HMAC-Hash(PRK, T(N-1) | info | N) HMACTmp.update(counter === 0 ? EMPTY_BUFFER : T) .update(info) .update(HKDF_COUNTER) .digestInto(T); okm.set(T, hash.outputLen * counter); HMAC._cloneInto(HMACTmp); } HMAC.destroy(); HMACTmp.destroy(); T.fill(0); HKDF_COUNTER.fill(0); return okm.slice(0, length); } /** * HKDF (RFC 5869): extract + expand in one step. * @param hash - hash function that would be used (e.g. sha256) * @param ikm - input keying material, the initial key * @param salt - optional salt value (a non-secret random value) * @param info - optional context and application specific information * @param length - length of output keying material in octets */ const hkdf = (hash, ikm, salt, info, length) => expand(hash, extract(hash, ikm, salt), info, length); // Common prologue and epilogue for sync/async functions function pbkdf2Init(hash, _password, _salt, _opts) { assert.hash(hash); const opts = checkOpts({ dkLen: 32, asyncTick: 10 }, _opts); const { c, dkLen, asyncTick } = opts; assert.number(c); assert.number(dkLen); assert.number(asyncTick); if (c < 1) throw new Error('PBKDF2: iterations (c) should be >= 1'); const password = toBytes(_password); const salt = toBytes(_salt); // DK = PBKDF2(PRF, Password, Salt, c, dkLen); const DK = new Uint8Array(dkLen); // U1 = PRF(Password, Salt + INT_32_BE(i)) const PRF = hmac.create(hash, password); const PRFSalt = PRF._cloneInto().update(salt); return { c, dkLen, asyncTick, DK, PRF, PRFSalt }; } function pbkdf2Output(PRF, PRFSalt, DK, prfW, u) { PRF.destroy(); PRFSalt.destroy(); if (prfW) prfW.destroy(); u.fill(0); return DK; } /** * PBKDF2-HMAC: RFC 2898 key derivation function * @param hash - hash function that would be used e.g. sha256 * @param password - password from which a derived key is generated * @param salt - cryptographic salt * @param opts - {c, dkLen} where c is work factor and dkLen is output message size */ function pbkdf2$1(hash, password, salt, opts) { const { c, dkLen, DK, PRF, PRFSalt } = pbkdf2Init(hash, password, salt, opts); let prfW; // Working copy const arr = new Uint8Array(4); const view = createView(arr); const u = new Uint8Array(PRF.outputLen); // DK = T1 + T2 + ⋯ + Tdklen/hlen for (let ti = 1, pos = 0; pos < dkLen; ti++, pos += PRF.outputLen) { // Ti = F(Password, Salt, c, i) const Ti = DK.subarray(pos, pos + PRF.outputLen); view.setInt32(0, ti, false); // F(Password, Salt, c, i) = U1 ^ U2 ^ ⋯ ^ Uc // U1 = PRF(Password, Salt + INT_32_BE(i)) (prfW = PRFSalt._cloneInto(prfW)).update(arr).digestInto(u); Ti.set(u.subarray(0, Ti.length)); for (let ui = 1; ui < c; ui++) { // Uc = PRF(Password, Uc−1) PRF._cloneInto(prfW).update(u).digestInto(u); for (let i = 0; i < Ti.length; i++) Ti[i] ^= u[i]; } } return pbkdf2Output(PRF, PRFSalt, DK, prfW, u); } async function pbkdf2Async(hash, password, salt, opts) { const { c, dkLen, asyncTick, DK, PRF, PRFSalt } = pbkdf2Init(hash, password, salt, opts); let prfW; // Working copy const arr = new Uint8Array(4); const view = createView(arr); const u = new Uint8Array(PRF.outputLen); // DK = T1 + T2 + ⋯ + Tdklen/hlen for (let ti = 1, pos = 0; pos < dkLen; ti++, pos += PRF.outputLen) { // Ti = F(Password, Salt, c, i) const Ti = DK.subarray(pos, pos + PRF.outputLen); view.setInt32(0, ti, false); // F(Password, Salt, c, i) = U1 ^ U2 ^ ⋯ ^ Uc // U1 = PRF(Password, Salt + INT_32_BE(i)) (prfW = PRFSalt._cloneInto(prfW)).update(arr).digestInto(u); Ti.set(u.subarray(0, Ti.length)); await asyncLoop(c - 1, asyncTick, (i) => { // Uc = PRF(Password, Uc−1) PRF._cloneInto(prfW).update(u).digestInto(u); for (let i = 0; i < Ti.length; i++) Ti[i] ^= u[i]; }); } return pbkdf2Output(PRF, PRFSalt, DK, prfW, u); } // Polyfill for Safari 14 function setBigUint64(view, byteOffset, value, isLE) { if (typeof view.setBigUint64 === 'function') return view.setBigUint64(byteOffset, value, isLE); const _32n = BigInt(32); const _u32_max = BigInt(0xffffffff); const wh = Number((value >> _32n) & _u32_max); const wl = Number(value & _u32_max); const h = isLE ? 4 : 0; const l = isLE ? 0 : 4; view.setUint32(byteOffset + h, wh, isLE); view.setUint32(byteOffset + l, wl, isLE); } // Base SHA2 class (RFC 6234) class SHA2 extends Hash { constructor(blockLen, outputLen, padOffset, isLE) { super(); this.blockLen = blockLen; this.outputLen = outputLen; this.padOffset = padOffset; this.isLE = isLE; this.finished = false; this.length = 0; this.pos = 0; this.destroyed = false; this.buffer = new Uint8Array(blockLen); this.view = createView(this.buffer); } update(data) { assert.exists(this); const { view, buffer, blockLen } = this; data = toBytes(data); const len = data.length; for (let pos = 0; pos < len;) { const take = Math.min(blockLen - this.pos, len - pos); // Fast path: we have at least one block in input, cast it to view and process if (take === blockLen) { const dataView = createView(data); for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos); continue; } buffer.set(data.subarray(pos, pos + take), this.pos); this.pos += take; pos += take; if (this.pos === blockLen) { this.process(view, 0); this.pos = 0; } } this.length += data.length; this.roundClean(); return this; } digestInto(out) { assert.exists(this); assert.output(out, this); this.finished = true; // Padding // We can avoid allocation of buffer for padding completely if it // was previously not allocated here. But it won't change performance. const { buffer, view, blockLen, isLE } = this; let { pos } = this; // append the bit '1' to the message buffer[pos++] = 0b10000000; this.buffer.subarray(pos).fill(0); // we have less than padOffset left in buffer, so we cannot put length in current block, need process it and pad again if (this.padOffset > blockLen - pos) { this.process(view, 0); pos = 0; } // Pad until full block byte with zeros for (let i = pos; i < blockLen; i++) buffer[i] = 0; // Note: sha512 requires length to be 128bit integer, but length in JS will overflow before that // You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen. // So we just write lowest 64 bits of that value. setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE); this.process(view, 0); const oview = createView(out); this.get().forEach((v, i) => oview.setUint32(4 * i, v, isLE)); } digest() { const { buffer, outputLen } = this; this.digestInto(buffer); const res = buffer.slice(0, outputLen); this.destroy(); return res; } _cloneInto(to) { to || (to = new this.constructor()); to.set(...this.get()); const { blockLen, buffer, length, finished, destroyed, pos } = this; to.length = length; to.pos = pos; to.finished = finished; to.destroyed = destroyed; if (length % blockLen) to.buffer.set(buffer); return to; } } // https://homes.esat.kuleuven.be/~bosselae/ripemd160.html // https://homes.esat.kuleuven.be/~bosselae/ripemd160/pdf/AB-9601/AB-9601.pdf const Rho = new Uint8Array([7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]); const Id = Uint8Array.from({ length: 16 }, (_, i) => i); const Pi = Id.map((i) => (9 * i + 5) % 16); let idxL = [Id]; let idxR = [Pi]; for (let i = 0; i < 4; i++) for (let j of [idxL, idxR]) j.push(j[i].map((k) => Rho[k])); const shifts = [ [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8], [12, 13, 11, 15, 6, 9, 9, 7, 12, 15, 11, 13, 7, 8, 7, 7], [13, 15, 14, 11, 7, 7, 6, 8, 13, 14, 13, 12, 5, 5, 6, 9], [14, 11, 12, 14, 8, 6, 5, 5, 15, 12, 15, 14, 9, 9, 8, 6], [15, 12, 13, 13, 9, 5, 8, 6, 14, 11, 12, 11, 8, 6, 5, 5], ].map((i) => new Uint8Array(i)); const shiftsL = idxL.map((idx, i) => idx.map((j) => shifts[i][j])); const shiftsR = idxR.map((idx, i) => idx.map((j) => shifts[i][j])); const Kl = new Uint32Array([0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e]); const Kr = new Uint32Array([0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000]); // The rotate left (circular left shift) operation for uint32 const rotl$1 = (word, shift) => (word << shift) | (word >>> (32 - shift)); // It's called f() in spec. function f(group, x, y, z) { if (group === 0) return x ^ y ^ z; else if (group === 1) return (x & y) | (~x & z); else if (group === 2) return (x | ~y) ^ z; else if (group === 3) return (x & z) | (y & ~z); else return x ^ (y | ~z); } // Temporary buffer, not used to store anything between runs const BUF = new Uint32Array(16); class RIPEMD160 extends SHA2 { constructor() { super(64, 20, 8, true); this.h0 = 0x67452301 | 0; this.h1 = 0xefcdab89 | 0; this.h2 = 0x98badcfe | 0; this.h3 = 0x10325476 | 0; this.h4 = 0xc3d2e1f0 | 0; } get() { const { h0, h1, h2, h3, h4 } = this; return [h0, h1, h2, h3, h4]; } set(h0, h1, h2, h3, h4) { this.h0 = h0 | 0; this.h1 = h1 | 0; this.h2 = h2 | 0; this.h3 = h3 | 0; this.h4 = h4 | 0; } process(view, offset) { for (let i = 0; i < 16; i++, offset += 4) BUF[i] = view.getUint32(offset, true); // prettier-ignore let al = this.h0 | 0, ar = al, bl = this.h1 | 0, br = bl, cl = this.h2 | 0, cr = cl, dl = this.h3 | 0, dr = dl, el = this.h4 | 0, er = el; // Instead of iterating 0 to 80, we split it into 5 groups // And use the groups in constants, functions, etc. Much simpler for (let group = 0; group < 5; group++) { const rGroup = 4 - group; const hbl = Kl[group], hbr = Kr[group]; // prettier-ignore const rl = idxL[group], rr = idxR[group]; // prettier-ignore const sl = shiftsL[group], sr = shiftsR[group]; // prettier-ignore for (let i = 0; i < 16; i++) { const tl = (rotl$1(al + f(group, bl, cl, dl) + BUF[rl[i]] + hbl, sl[i]) + el) | 0; al = el, el = dl, dl = rotl$1(cl, 10) | 0, cl = bl, bl = tl; // prettier-ignore } // 2 loops are 10% faster for (let i = 0; i < 16; i++) { const tr = (rotl$1(ar + f(rGroup, br, cr, dr) + BUF[rr[i]] + hbr, sr[i]) + er) | 0; ar = er, er = dr, dr = rotl$1(cr, 10) | 0, cr = br, br = tr; // prettier-ignore } } // Add the compressed chunk to the current hash value this.set((this.h1 + cl + dr) | 0, (this.h2 + dl + er) | 0, (this.h3 + el + ar) | 0, (this.h4 + al + br) | 0, (this.h0 + bl + cr) | 0); } roundClean() { BUF.fill(0); } destroy() { this.destroyed = true; this.buffer.fill(0); this.set(0, 0, 0, 0, 0); } } /** * RIPEMD-160 - a hash function from 1990s. * @param message - msg that would be hashed */ const ripemd160 = wrapConstructor(() => new RIPEMD160()); // Choice: a ? b : c const Chi = (a, b, c) => (a & b) ^ (~a & c); // Majority function, true if any two inpust is true const Maj = (a, b, c) => (a & b) ^ (a & c) ^ (b & c); // Round constants: // first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311) // prettier-ignore const SHA256_K = new Uint32Array([ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 ]); // Initial state (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19): // prettier-ignore const IV = new Uint32Array([ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ]); // Temporary buffer, not used to store anything between runs // Named this way because it matches specification. const SHA256_W = new Uint32Array(64); class SHA256 extends SHA2 { constructor() { super(64, 32, 8, false); // We cannot use array here since array allows indexing by variable // which means optimizer/compiler cannot use registers. this.A = IV[0] | 0; this.B = IV[1] | 0; this.C = IV[2] | 0; this.D = IV[3] | 0; this.E = IV[4] | 0; this.F = IV[5] | 0; this.G = IV[6] | 0; this.H = IV[7] | 0; } get() { const { A, B, C, D, E, F, G, H } = this; return [A, B, C, D, E, F, G, H]; } // prettier-ignore set(A, B, C, D, E, F, G, H) { this.A = A | 0; this.B = B | 0; this.C = C | 0; this.D = D | 0; this.E = E | 0; this.F = F | 0; this.G = G | 0; this.H = H | 0; } process(view, offset) { // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false); for (let i = 16; i < 64; i++) { const W15 = SHA256_W[i - 15]; const W2 = SHA256_W[i - 2]; const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3); const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10); SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0; } // Compression function main loop, 64 rounds let { A, B, C, D, E, F, G, H } = this; for (let i = 0; i < 64; i++) { const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25); const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0; const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22); const T2 = (sigma0 + Maj(A, B, C)) | 0; H = G; G = F; F = E; E = (D + T1) | 0; D = C; C = B; B = A; A = (T1 + T2) | 0; } // Add the compressed chunk to the current hash value A = (A + this.A) | 0; B = (B + this.B) | 0; C = (C + this.C) | 0; D = (D + this.D) | 0; E = (E + this.E) | 0; F = (F + this.F) | 0; G = (G + this.G) | 0; H = (H + this.H) | 0; this.set(A, B, C, D, E, F, G, H); } roundClean() { SHA256_W.fill(0); } destroy() { this.set(0, 0, 0, 0, 0, 0, 0, 0); this.buffer.fill(0); } } /** * SHA2-256 hash function * @param message - data that would be hashed */ const sha256 = wrapConstructor(() => new SHA256()); // RFC 7914 Scrypt KDF // Left rotate for uint32 const rotl = (a, b) => (a << b) | (a >>> (32 - b)); // The main Scrypt loop: uses Salsa extensively. // Six versions of the function were tried, this is the fastest one. // prettier-ignore function XorAndSalsa(prev, pi, input, ii, out, oi) { // Based on https://cr.yp.to/salsa20.html // Xor blocks let y00 = prev[pi++] ^ input[ii++], y01 = prev[pi++] ^ input[ii++]; let y02 = prev[pi++] ^ input[ii++], y03 = prev[pi++] ^ input[ii++]; let y04 = prev[pi++] ^ input[ii++], y05 = prev[pi++] ^ input[ii++]; let y06 = prev[pi++] ^ input[ii++], y07 = prev[pi++] ^ input[ii++]; let y08 = prev[pi++] ^ input[ii++], y09 = prev[pi++] ^ input[ii++]; let y10 = prev[pi++] ^ input[ii++], y11 = prev[pi++] ^ input[ii++]; let y12 = prev[pi++] ^ input[ii++], y13 = prev[pi++] ^ input[ii++]; let y14 = prev[pi++] ^ input[ii++], y15 = prev[pi++] ^ input[ii++]; // Save state to temporary variables (salsa) let x00 = y00, x01 = y01, x02 = y02, x03 = y03, x04 = y04, x05 = y05, x06 = y06, x07 = y07, x08 = y08, x09 = y09, x10 = y10, x11 = y11, x12 = y12, x13 = y13, x14 = y14, x15 = y15; // Main loop (salsa) for (let i = 0; i < 8; i += 2) { x04 ^= rotl(x00 + x12 | 0, 7); x08 ^= rotl(x04 + x00 | 0, 9); x12 ^= rotl(x08 + x04 | 0, 13); x00 ^= rotl(x12 + x08 | 0, 18); x09 ^= rotl(x05 + x01 | 0, 7); x13 ^= rotl(x09 + x05 | 0, 9); x01 ^= rotl(x13 + x09 | 0, 13); x05 ^= rotl(x01 + x13 | 0, 18); x14 ^= rotl(x10 + x06 | 0, 7); x02 ^= rotl(x14 + x10 | 0, 9); x06 ^= rotl(x02 + x14 | 0, 13); x10 ^= rotl(x06 + x02 | 0, 18); x03 ^= rotl(x15 + x11 | 0, 7); x07 ^= rotl(x03 + x15 | 0, 9); x11 ^= rotl(x07 + x03 | 0, 13); x15 ^= rotl(x11 + x07 | 0, 18); x01 ^= rotl(x00 + x03 | 0, 7); x02 ^= rotl(x01 + x00 | 0, 9); x03 ^= rotl(x02 + x01 | 0, 13); x00 ^= rotl(x03 + x02 | 0, 18); x06 ^= rotl(x05 + x04 | 0, 7); x07 ^= rotl(x06 + x05 | 0, 9); x04 ^= rotl(x07 + x06 | 0, 13); x05 ^= rotl(x04 + x07 | 0, 18); x11 ^= rotl(x10 + x09 | 0, 7); x08 ^= rotl(x11 + x10 | 0, 9); x09 ^= rotl(x08 + x11 | 0, 13); x10 ^= rotl(x09 + x08 | 0, 18); x12 ^= rotl(x15 + x14 | 0, 7); x13 ^= rotl(x12 + x15 | 0, 9); x14 ^= rotl(x13 + x12 | 0, 13); x15 ^= rotl(x14 + x13 | 0, 18); } // Write output (salsa) out[oi++] = (y00 + x00) | 0; out[oi++] = (y01 + x01) | 0; out[oi++] = (y02 + x02) | 0; out[oi++] = (y03 + x03) | 0; out[oi++] = (y04 + x04) | 0; out[oi++] = (y05 + x05) | 0; out[oi++] = (y06 + x06) | 0; out[oi++] = (y07 + x07) | 0; out[oi++] = (y08 + x08) | 0; out[oi++] = (y09 + x09) | 0; out[oi++] = (y10 + x10) | 0; out[oi++] = (y11 + x11) | 0; out[oi++] = (y12 + x12) | 0; out[oi++] = (y13 + x13) | 0; out[oi++] = (y14 + x14) | 0; out[oi++] = (y15 + x15) | 0; } function BlockMix(input, ii, out, oi, r) { // The block B is r 128-byte chunks (which is equivalent of 2r 64-byte chunks) let head = oi + 0; let tail = oi + 16 * r; for (let i = 0; i < 16; i++) out[tail + i] = input[ii + (2 * r - 1) * 16 + i]; // X ← B[2r−1] for (let i = 0; i < r; i++, head += 16, ii += 16) { // We write odd & even Yi at same time. Even: 0bXXXXX0 Odd: 0bXXXXX1 XorAndSalsa(out, tail, input, ii, out, head); // head[i] = Salsa(blockIn[2*i] ^ tail[i-1]) if (i > 0) tail += 16; // First iteration overwrites tmp value in tail XorAndSalsa(out, head, input, (ii += 16), out, tail); // tail[i] = Salsa(blockIn[2*i+1] ^ head[i]) } } // Common prologue and epilogue for sync/async functions function scryptInit(password, salt, _opts) { // Maxmem - 1GB+1KB by default const opts = checkOpts({ dkLen: 32, asyncTick: 10, maxmem: 1024 ** 3 + 1024, }, _opts); const { N, r, p, dkLen, asyncTick, maxmem, onProgress } = opts; assert.number(N); assert.number(r); assert.number(p); assert.number(dkLen); assert.number(asyncTick); assert.number(maxmem); if (onProgress !== undefined && typeof onProgress !== 'function') throw new Error('progressCb should be function'); const blockSize = 128 * r; const blockSize32 = blockSize / 4; if (N <= 1 || (N & (N - 1)) !== 0 || N >= 2 ** (blockSize / 8) || N > 2 ** 32) { // NOTE: we limit N to be less than 2**32 because of 32 bit variant of Integrify function // There is no JS engines that allows alocate more than 4GB per single Uint8Array for now, but can change in future. throw new Error('Scrypt: N must be larger than 1, a power of 2, less than 2^(128 * r / 8) and less than 2^32'); } if (p < 0 || p > ((2 ** 32 - 1) * 32) / blockSize) { throw new Error('Scrypt: p must be a positive integer less than or equal to ((2^32 - 1) * 32) / (128 * r)'); } if (dkLen < 0 || dkLen > (2 ** 32 - 1) * 32) { throw new Error('Scrypt: dkLen should be positive integer less than or equal to (2^32 - 1) * 32'); } const memUsed = blockSize * (N + p); if (memUsed > maxmem) { throw new Error(`Scrypt: parameters too large, ${memUsed} (128 * r * (N + p)) > ${maxmem} (maxmem)`); } // [B0...Bp−1] ← PBKDF2HMAC-SHA256(Passphrase, Salt, 1, blockSize*ParallelizationFactor) // Since it has only one iteration there is no reason to use async variant const B = pbkdf2$1(sha256, password, salt, { c: 1, dkLen: blockSize * p }); const B32 = u32(B); // Re-used between parallel iterations. Array(iterations) of B const V = u32(new Uint8Array(blockSize * N)); const tmp = u32(new Uint8Array(blockSize)); let blockMixCb = () => { }; if (onProgress) { const totalBlockMix = 2 * N * p; // Invoke callback if progress changes from 10.01 to 10.02 // Allows to draw smooth progress bar on up to 8K screen const callbackPer = Math.max(Math.floor(totalBlockMix / 10000), 1); let blockMixCnt = 0; blockMixCb = () => { blockMixCnt++; if (onProgress && (!(blockMixCnt % callbackPer) || blockMixCnt === totalBlockMix)) onProgress(blockMixCnt / totalBlockMix); }; } return { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb, asyncTick }; } function scryptOutput(password, dkLen, B, V, tmp) { const res = pbkdf2$1(sha256, password, B, { c: 1, dkLen }); B.fill(0); V.fill(0); tmp.fill(0); return res; } /** * Scrypt KDF from RFC 7914. * @param password - pass * @param salt - salt * @param opts - parameters * - `N` is cpu/mem work factor (power of 2 e.g. 2**18) * - `r` is block size (8 is common), fine-tunes sequential memory read size and performance * - `p` is parallelization factor (1 is common) * - `dkLen` is output key length in bytes e.g. 32. * - `asyncTick` - (default: 10) max time in ms for which async function can block execution * - `maxmem` - (default: `1024 ** 3 + 1024` aka 1GB+1KB). A limit that the app could use for scrypt * - `onProgress` - callback function that would be executed for progress report * @returns Derived key */ function scrypt$1(password, salt, opts) { const { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb } = scryptInit(password, salt, opts); for (let pi = 0; pi < p; pi++) { const Pi = blockSize32 * pi; for (let i = 0; i < blockSize32; i++) V[i] = B32[Pi + i]; // V[0] = B[i] for (let i = 0, pos = 0; i < N - 1; i++) { BlockMix(V, pos, V, (pos += blockSize32), r); // V[i] = BlockMix(V[i-1]); blockMixCb(); } BlockMix(V, (N - 1) * blockSize32, B32, Pi, r); // Process last element blockMixCb(); for (let i = 0; i < N; i++) { // First u32 of the last 64-byte block (u32 is LE) const j = B32[Pi + blockSize32 - 16] % N; // j = Integrify(X) % iterations for (let k = 0; k < blockSize32; k++) tmp[k] = B32[Pi + k] ^ V[j * blockSize32 + k]; // tmp = B ^ V[j] BlockMix(tmp, 0, B32, Pi, r); // B = BlockMix(B ^ V[j]) blockMixCb(); } } return scryptOutput(password, dkLen, B, V, tmp); } /** * Scrypt KDF from RFC 7914. */ async function scryptAsync(password, salt, opts) { const { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb, asyncTick } = scryptInit(password, salt, opts); for (let pi = 0; pi < p; pi++) { const Pi = blockSize32 * pi; for (let i = 0; i < blockSize32; i++) V[i] = B32[Pi + i]; // V[0] = B[i] let pos = 0; await asyncLoop(N - 1, asyncTick, (i) => { BlockMix(V, pos, V, (pos += blockSize32), r); // V[i] = BlockMix(V[i-1]); blockMixCb(); }); BlockMix(V, (N - 1) * blockSize32, B32, Pi, r); // Process last element blockMixCb(); await asyncLoop(N, asyncTick, (i) => { // First u32 of the last 64-byte block (u32 is LE) const j = B32[Pi + blockSize32 - 16] % N; // j = Integrify(X) % iterations for (let k = 0; k < blockSize32; k++) tmp[k] = B32[Pi + k] ^ V[j * blockSize32 + k]; // tmp = B ^ V[j] BlockMix(tmp, 0, B32, Pi, r); // B = BlockMix(B ^ V[j]) blockMixCb(); }); } return scryptOutput(password, dkLen, B, V, tmp); } // Round contants (first 32 bits of the fractional parts of the cube roots of the first 80 primes 2..409): // prettier-ignore const [SHA512_Kh, SHA512_Kl] = u64.split([ '0x428a2f98d728ae22', '0x7137449123ef65cd', '0xb5c0fbcfec4d3b2f', '0xe9b5dba58189dbbc', '0x3956c25bf348b538', '0x59f111f1b605d019', '0x923f82a4af194f9b', '0xab1c5ed5da6d8118', '0xd807aa98a3030242', '0x12835b0145706fbe', '0x243185be4ee4b28c', '0x550c7dc3d5ffb4e2', '0x72be5d74f27b896f', '0x80deb1fe3b1696b1', '0x9bdc06a725c71235', '0xc19bf174cf692694', '0xe49b69c19ef14ad2', '0xefbe4786384f25e3', '0x0fc19dc68b8cd5b5', '0x240ca1cc77ac9c65', '0x2de92c6f592b0275', '0x4a7484aa6ea6e483', '0x5cb0a9dcbd41fbd4', '0x76f988da831153b5', '0x983e5152ee66dfab', '0xa831c66d2db43210', '0xb00327c898fb213f', '0xbf597fc7beef0ee4', '0xc6e00bf33da88fc2', '0xd5a79147930aa725', '0x06ca6351e003826f', '0x142929670a0e6e70', '0x27b70a8546d22ffc', '0x2e1b21385c26c926', '0x4d2c6dfc5ac42aed', '0x53380d139d95b3df', '0x650a73548baf63de', '0x766a0abb3c77b2a8', '0x81c2c92e47edaee6', '0x92722c851482353b', '0xa2bfe8a14cf10364', '0xa81a664bbc423001', '0xc24b8b70d0f89791', '0xc76c51a30654be30', '0xd192e819d6ef5218', '0xd69906245565a910', '0xf40e35855771202a', '0x106aa07032bbd1b8', '0x19a4c116b8d2d0c8', '0x1e376c085141ab53', '0x2748774cdf8eeb99', '0x34b0bcb5e19b48a8', '0x391c0cb3c5c95a63', '0x4ed8aa4ae3418acb', '0x5b9cca4f7763e373', '0x682e6ff3d6b2b8a3', '0x748f82ee5defb2fc', '0x78a5636f43172f60', '0x84c87814a1f0ab72', '0x8cc702081a6439ec', '0x90befffa23631e28', '0xa4506cebde82bde9', '0xbef9a3f7b2c67915', '0xc67178f2e372532b', '0xca273eceea26619c', '0xd186b8c721c0c207', '0xeada7dd6cde0eb1e', '0xf57d4f7fee6ed178', '0x06f067aa72176fba', '0x0a637dc5a2c898a6', '0x113f9804bef90dae', '0x1b710b35131c471b', '0x28db77f523047d84', '0x32caab7b40c72493', '0x3c9ebe0a15c9bebc', '0x431d67c49c100d4c', '0x4cc5d4becb3e42b6', '0x597f299cfc657e2a', '0x5fcb6fab3ad6faec', '0x6c44198c4a475817' ].map(n => BigInt(n))); // Temporary buffer, not used to store anything between runs const SHA512_W_H = new Uint32Array(80); const SHA512_W_L = new Uint32Array(80); class SHA512 extends SHA2 { constructor() { super(128, 64, 16, false); // We cannot use array here since array allows indexing by variable which means optimizer/compiler cannot use registers. // Also looks cleaner and easier to verify with spec. // Initial state (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19): // h -- high 32 bits, l -- low 32 bits this.Ah = 0x6a09e667 | 0; this.Al = 0xf3bcc908 | 0; this.Bh = 0xbb67ae85 | 0; this.Bl = 0x84caa73b | 0; this.Ch = 0x3c6ef372 | 0; this.Cl = 0xfe94f82b | 0; this.Dh = 0xa54ff53a | 0; this.Dl = 0x5f1d36f1 | 0; this.Eh = 0x510e527f | 0; this.El = 0xade682d1 | 0; this.Fh = 0x9b05688c | 0; this.Fl = 0x2b3e6c1f | 0; this.Gh = 0x1f83d9ab | 0; this.Gl = 0xfb41bd6b | 0; this.Hh = 0x5be0cd19 | 0; this.Hl = 0x137e2179 | 0; } // prettier-ignore get() { const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this; return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl]; } // prettier-ignore set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl) { this.Ah = Ah | 0; this.Al = Al | 0; this.Bh = Bh | 0; this.Bl = Bl | 0; this.Ch = Ch | 0; this.Cl = Cl | 0; this.Dh = Dh | 0; this.Dl = Dl | 0; this.Eh = Eh | 0; this.El = El | 0; this.Fh = Fh | 0; this.Fl = Fl | 0; this.Gh = Gh | 0; this.Gl = Gl | 0; this.Hh = Hh | 0; this.Hl = Hl | 0; } process(view, offset) { // Extend the first 16 words into the remaining 64 words w[16..79] of the message schedule array for (let i = 0; i < 16; i++, offset += 4) { SHA512_W_H[i] = view.getUint32(offset); SHA512_W_L[i] = view.getUint32((offset += 4)); } for (let i = 16; i < 80; i++) { // s0 := (w[i-15] rightrotate 1) xor (w[i-15] rightrotate 8) xor (w[i-15] rightshift 7) const W15h = SHA512_W_H[i - 15] | 0; const W15l = SHA512_W_L[i - 15] | 0; const s0h = u64.rotrSH(W15h, W15l, 1) ^ u64.rotrSH(W15h, W15l, 8) ^ u64.shrSH(W15h, W15l, 7); const s0l = u64.rotrSL(W15h, W15l, 1) ^ u64.rotrSL(W15h, W15l, 8) ^ u64.shrSL(W15h, W15l, 7); // s1 := (w[i-2] rightrotate 19) xor (w[i-2] rightrotate 61) xor (w[i-2] rightshift 6) const W2h = SHA512_W_H[i - 2] | 0; const W2l = SHA512_W_L[i - 2] | 0; const s1h = u64.rotrSH(W2h, W2l, 19) ^ u64.rotrBH(W2h, W2l, 61) ^ u64.shrSH(W2h, W2l, 6); const s1l = u64.rotrSL(W2h, W2l, 19) ^ u64.rotrBL(W2h, W2l, 61) ^ u64.shrSL(W2h, W2l, 6); // SHA256_W[i] = s0 + s1 + SHA256_W[i - 7] + SHA256_W[i - 16]; const SUMl = u64.add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]); const SUMh = u64.add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]); SHA512_W_H[i] = SUMh | 0; SHA512_W_L[i] = SUMl | 0; } let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this; // Compression function main loop, 80 rounds for (let i = 0; i < 80; i++) { // S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41) const sigma1h = u64.rotrSH(Eh, El, 14) ^ u64.rotrSH(Eh, El, 18) ^ u64.rotrBH(Eh, El, 41); const sigma1l = u64.rotrSL(Eh, El, 14) ^ u64.rotrSL(Eh, El, 18) ^ u64.rotrBL(Eh, El, 41); //const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0; const CHIh = (Eh & Fh) ^ (~Eh & Gh); const CHIl = (El & Fl) ^ (~El & Gl); // T1 = H + sigma1 + Chi(E, F, G) + SHA512_K[i] + SHA512_W[i] // prettier-ignore const T1ll = u64.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]); const T1h = u64.add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]); const T1l = T1ll | 0; // S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39) const sigma0h = u64.rotrSH(Ah, Al, 28) ^ u64.rotrBH(Ah, Al, 34) ^ u64.rotrBH(Ah, Al, 39); const sigma0l = u64.rotrSL(Ah, Al, 28) ^ u64.rotrBL(Ah, Al, 34) ^ u64.rotrBL(Ah, Al, 39); const MAJh = (Ah & Bh) ^ (Ah & Ch) ^ (Bh & Ch); const MAJl = (Al & Bl) ^ (Al & Cl) ^ (Bl & Cl); Hh = Gh | 0; Hl = Gl | 0; Gh = Fh | 0; Gl = Fl | 0; Fh = Eh | 0; Fl = El | 0; ({ h: Eh, l: El } = u64.add(Dh | 0, Dl | 0, T1h | 0, T1l | 0)); Dh = Ch | 0; Dl = Cl | 0; Ch = Bh | 0; Cl = Bl | 0; Bh = Ah | 0; Bl = Al | 0; const All = u64.add3L(T1l, sigma0l, MAJl); Ah = u64.add3H(All, T1h, sigma0h, MAJh); Al = All | 0; } // Add the compressed chunk to the current hash value ({ h: Ah, l: Al } = u64.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0)); ({ h: Bh, l: Bl } = u64.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0)); ({ h: Ch, l: Cl } = u64.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0)); ({ h: Dh, l: Dl } = u64.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0)); ({ h: Eh, l: El } = u64.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0)); ({ h: Fh, l: Fl } = u64.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0)); ({ h: Gh, l: Gl } = u64.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0)); ({ h: Hh, l: Hl } = u64.add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0)); this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl); } roundClean() { SHA512_W_H.fill(0); SHA512_W_L.fill(0); } destroy() { this.buffer.fill(0); this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } } class SHA512_256 extends SHA512 { constructor() { super(); // h -- high 32 bits, l -- low 32 bits this.Ah = 0x22312194 | 0; this.Al = 0xfc2bf72c | 0; this.Bh = 0x9f555fa3 | 0; this.Bl = 0xc84c64c2 | 0; this.Ch = 0x2393b86b | 0; this.Cl = 0x6f53b151 | 0; this.Dh = 0x96387719 | 0; this.Dl = 0x5940eabd | 0; this.Eh = 0x96283ee2 | 0; this.El = 0xa88effe3 | 0; this.Fh = 0xbe5e1e25 | 0; this.Fl = 0x53863992 | 0; this.Gh = 0x2b0199fc | 0; this.Gl = 0x2c85b8aa | 0; this.Hh = 0x0eb72ddc | 0; this.Hl = 0x81c52ca2 | 0; this.outputLen = 32; } } class SHA384 extends SHA512 { constructor() { super(); // h -- high 32 bits, l -- low 32 bits this.Ah = 0xcbbb9d5d | 0; this.Al = 0xc1059ed8 | 0; this.Bh = 0x629a292a | 0; this.Bl = 0x367cd507 | 0; this.Ch = 0x9159015a | 0; this.Cl = 0x3070dd17 | 0; this.Dh = 0x152fecd8 | 0; this.Dl = 0xf70e5939 | 0; this.Eh = 0x67332667 | 0; this.El = 0xffc00b31 | 0; this.Fh = 0x8eb44a87 | 0; this.Fl = 0x68581511 | 0; this.Gh = 0xdb0c2e0d | 0; this.Gl = 0x64f98fa7 | 0; this.Hh = 0x47b5481d | 0; this.Hl = 0xbefa4fa4 | 0; this.outputLen = 48; } } const sha512 = wrapConstructor(() => new SHA512()); wrapConstructor(() => new SHA512_256()); wrapConstructor(() => new SHA384()); // Various per round constants calculations const [SHA3_PI, SHA3_ROTL, _SHA3_IOTA] = [[], [], []]; var _0n = BigInt(0); var _1n = BigInt(1); var _2n = BigInt(2); var _7n = BigInt(7); const _256n = BigInt(256); const _0x71n = BigInt(0x71); for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) { // Pi [x, y] = [y, (2 * x + 3 * y) % 5]; SHA3_PI.push(2 * (5 * y + x)); // Rotational SHA3_ROTL.push((((round + 1) * (round + 2)) / 2) % 64); // Iota let t = _0n; for (let j = 0; j < 7; j++) { R = ((R << _1n) ^ ((R >> _7n) * _0x71n)) % _256n; if (R & _2n) t ^= _1n << ((_1n << BigInt(j)) - _1n); } _SHA3_IOTA.push(t); } const [SHA3_IOTA_H, SHA3_IOTA_L] = u64.split(_SHA3_IOTA, true); // Left rotation (without 0, 32, 64) const rotlH = (h, l, s) => s > 32 ? u64.rotlBH(h, l, s) : u64.rotlSH(h, l, s); const rotlL = (h, l, s) => s > 32 ? u64.rotlBL(h, l, s) : u64.rotlSL(h, l, s); // Same as keccakf1600, but allows to skip some rounds function keccakP(s, rounds = 24) { const B = new Uint32Array(5 * 2); // NOTE: all indices are x2 since we store state as u32 instead of u64 (bigints to slow in js) for (let round = 24 - rounds; round < 24; round++) { // Theta θ for (let x = 0; x < 10; x++) B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40]; for (let x = 0; x < 10; x += 2) { const idx1 = (x + 8) % 10; const idx0 = (x + 2) % 10; const B0 = B[idx0]; const B1 = B[idx0 + 1]; const Th = rotlH(B0, B1, 1) ^ B[idx1]; const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1]; for (let y = 0; y < 50; y += 10) { s[x + y] ^= Th; s[x + y + 1] ^= Tl; } } // Rho (ρ) and Pi (π) let curH = s[2]; let curL = s[3]; for (let t = 0; t < 24; t++) { const shift = SHA3_ROTL[t]; const Th = rotlH(curH, curL, shift); const Tl = rotlL(curH, curL, shift); const PI = SHA3_PI[t]; curH = s[PI]; curL = s[PI + 1]; s[PI] = Th; s[PI + 1] = Tl; } // Chi (χ) for (let y = 0; y < 50; y += 10) { for (let x = 0; x < 10; x++) B[x] = s[y + x]; for (let x = 0; x < 10; x++) s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10]; } // Iota (ι) s[0] ^= SHA3_IOTA_H[round]; s[1] ^= SHA3_IOTA_L[round]; } B.fill(0); } class Keccak extends Hash { // NOTE: we accept arguments in bytes instead of bits here. constructor(blockLen, suffix, outputLen, enableXOF = false, rounds = 24) { super(); this.blockLen = blockLen; this.suffix = suffix; this.outputLen = outputLen; this.enableXOF = enableXOF; this.rounds = rounds; this.pos = 0; this.posOut = 0; this.finished = false; this.destroyed = false; // Can be passed from user as dkLen assert.number(outputLen); // 1600 = 5x5 matrix of 64bit. 1600 bits === 200 bytes if (0 >= this.blockLen || this.blockLen >= 200) throw new Error('Sha3 supports only keccak-f1600 function'); this.state = new Uint8Array(200); this.state32 = u32(this.state); } keccak() { keccakP(this.state32, this.rounds); this.posOut = 0; this.pos = 0; } update(data) { assert.exists(this); const { blockLen, state } = this; data = toBytes(data); const len = data.length; for (let pos = 0; pos < len;) { const take = Math.min(blockLen - this.pos, len - pos); for (let i = 0; i < take; i++) state[this.pos++] ^= data[pos++]; if (this.pos === blockLen) this.keccak(); } return this; } finish() { if (this.finished) return; this.finished = true; const { state, suffix, pos, blockLen } = this; // Do the padding state[pos] ^= suffix; if ((suffix & 0x80) !== 0 && pos === blockLen - 1) this.keccak(); state[blockLen - 1] ^= 0x80; this.keccak(); } writeInto(out) { assert.exists(this, false); assert.bytes(out); this.finish(); const bufferOut = this.state; const { blockLen } = this; for (let pos = 0, len = out.length; pos < len;) { if (this.posOut >= blockLen) this.keccak(); const take = Math.min(blockLen - this.posOut, len - pos); out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos); this.posOut += take; pos += take; } return out; } xofInto(out) { // Sha3/Keccak usage with XOF is probably mistake, only SHAKE instances can do XOF if (!this.enableXOF) throw new Error('XOF is not possible for this instance'); return this.writeInto(out); } xof(bytes) { assert.number(bytes); return this.xofInto(new Uint8Array(bytes)); } digestInto(out) { assert.output(out, this); if (this.finished) throw new Error('digest() was already called'); this.writeInto(out); this.destroy(); return out; } digest() { return this.digestInto(new Uint8Array(this.outputLen)); } destroy() { this.destroyed = true; this.state.fill(0); } _cloneInto(to) { const { blockLen, suffix, outputLen, rounds, enableXOF } = this; to || (to = new Keccak(blockLen, suffix, outputLen, enableXOF, rounds)); to.state32.set(this.state32); to.pos = this.pos; to.posOut = this.posOut; to.finished = this.finished; to.rounds = rounds; // Suffix can change in cSHAKE to.suffix = suffix; to.outputLen = outputLen; to.enableXOF = enableXOF; to.destroyed = this.destroyed; return to; } } const gen = (suffix, blockLen, outputLen) => wrapConstructor(() => new Keccak(blockLen, suffix, outputLen)); const sha3_224 = gen(0x06, 144, 224 / 8); /** * SHA3-256 hash function * @param message - that would be hashed */ const sha3_256 = gen(0x06, 136, 256 / 8); const sha3_384 = gen(0x06, 104, 384 / 8); const sha3_512 = gen(0x06, 72, 512 / 8); const keccak_224 = gen(0x01, 144, 224 / 8); /** * keccak-256 hash function. Different from SHA3-256. * @param message - that would be hashed */ const keccak_256 = gen(0x01, 136, 256 / 8); const keccak_384 = gen(0x01, 104, 384 / 8); const keccak_512 = gen(0x01, 72, 512 / 8); const genShake = (suffix, blockLen, outputLen) => wrapConstructorWithOpts((opts = {}) => new Keccak(blockLen, suffix, opts.dkLen === undefined ? outputLen : opts.dkLen, true)); genShake(0x1f, 168, 128 / 8); genShake(0x1f, 136, 256 / 8); // cSHAKE && KMAC (NIST SP800-185) function leftEncode(n) { const res = [n & 0xff]; n >>= 8; for (; n > 0; n >>= 8) res.unshift(n & 0xff); res.unshift(res.length); return new Uint8Array(res); } function rightEncode(n) { const res = [n & 0xff]; n >>= 8; for (; n > 0; n >>= 8) res.unshift(n & 0xff); res.push(res.length); return new Uint8Array(res); } function chooseLen(opts, outputLen) { return opts.dkLen === undefined ? outputLen : opts.dkLen; } const toBytesOptional = (buf) => (buf !== undefined ? toBytes(buf) : new Uint8Array([])); // NOTE: second modulo is necessary since we don't need to add padding if current element takes whole block const getPadding = (len, block) => new Uint8Array((block - (len % block)) % block); // Personalization function cshakePers(hash, opts = {}) { if (!opts || (!opts.personalization && !opts.NISTfn)) return hash; // Encode and pad inplace to avoid unneccesary memory copies/slices (so we don't need to zero them later) // bytepad(encode_string(N) || encode_string(S), 168) const blockLenBytes = leftEncode(hash.blockLen); const fn = toBytesOptional(opts.NISTfn); const fnLen = leftEncode(8 * fn.length); // length in bits const pers = toBytesOptional(opts.personalization); const persLen = leftEncode(8 * pers.length); // length in bits if (!fn.length && !pers.length) return hash; hash.suffix = 0x04; hash.update(blockLenBytes).update(fnLen).update(fn).update(persLen).update(pers); let totalLen = blockLenBytes.length + fnLen.length + fn.length + persLen.length + pers.length; hash.update(getPadding(totalLen, hash.blockLen)); return hash; } const gencShake = (suffix, blockLen, outputLen) => wrapConstructorWithOpts((opts = {}) => cshakePers(new Keccak(blockLen, suffix, chooseLen(opts, outputLen), true), opts)); const cshake128 = gencShake(0x1f, 168, 128 / 8); const cshake256 = gencShake(0x1f, 136, 256 / 8); class KMAC extends Keccak { constructor(blockLen, outputLen, enableXOF, key, opts = {}) { super(blockLen, 0x1f, outputLen, enableXOF); cshakePers(this, { NISTfn: 'KMAC', personalization: opts.personalization }); key = toBytes(key); // 1. newX = bytepad(encode_string(K), 168) || X || right_encode(L). const blockLenBytes = leftEncode(this.blockLen); const keyLen = leftEncode(8 * key.length); this.update(blockLenBytes).update(keyLen).update(key); const totalLen = blockLenBytes.length + keyLen.length + key.length; this.update(getPadding(totalLen, this.blockLen)); } finish() { if (!this.finished) this.update(rightEncode(this.enableXOF ? 0 : this.outputLen * 8)); // outputLen in bits super.finish(); } _cloneInto(to) { // Create new instance without calling constructor since key already in state and we don't know it. // Force "to" to be instance of KMAC instead of Sha3. if (!to) { to = Object.create(Object.getPrototypeOf(this), {}); to.state = this.state.slice(); to.blockLen = this.blockLen; to.state32 = u32(to.state); } return super._cloneInto(to); } clone() { return this._cloneInto(); } } function genKmac(blockLen, outputLen, xof = false) { const kmac = (key, message, opts) => kmac.create(key, opts).update(message).digest(); kmac.create = (key, opts = {}) => new KMAC(blockLen, chooseLen(opts, outputLen), xof, key, opts); return kmac; } const kmac128 = genKmac(168, 128 / 8); const kmac256 = genKmac(136, 256 / 8); genKmac(168, 128 / 8, true); genKmac(136, 256 / 8, true); // Kangaroo // Same as NIST rightEncode, but returns [0] for zero string function rightEncodeK12(n) { const res = []; for (; n > 0; n >>= 8) res.unshift(n & 0xff); res.push(res.length); return new Uint8Array(res); } const EMPTY = new Uint8Array([]); class KangarooTwelve extends Keccak { constructor(blockLen, leafLen, outputLen, rounds, opts) { super(blockLen, 0x07, outputLen, true, rounds); this.leafLen = leafLen; this.chunkLen = 8192; this.chunkPos = 0; // Position of current block in chunk this.chunksDone = 0; // How many chunks we already have const { personalization } = opts; this.personalization = toBytesOptional(personalization); } update(data) { data = toBytes(data); const { chunkLen, blockLen, leafLen, rounds } = this; for (let pos = 0, len = data.length; pos < len;) { if (this.chunkPos == chunkLen) { if (this.leafHash) super.update(this.leafHash.digest()); else { this.suffix = 0x06; // Its safe to change suffix here since its used only in digest() super.update(new Uint8Array([3, 0, 0, 0, 0, 0, 0, 0])); } this.leafHash = new Keccak(blockLen, 0x0b, leafLen, false, rounds); this.chunksDone++; this.chunkPos = 0; } const take = Math.min(chunkLen - this.chunkPos, len - pos); const chunk = data.subarray(pos, pos + take); if (this.leafHash) this.leafHash.update(chunk); else super.update(chunk); this.chunkPos += take; pos += take; } return this; } finish() { if (this.finished) return; const { personalization } = this; this.update(personalization).update(rightEncodeK12(personalization.length)); // Leaf hash if (this.leafHash) { super.update(this.leafHash.digest()); super.update(rightEncodeK12(this.chunksDone)); super.update(new Uint8Array([0xff, 0xff])); } super.finish.call(this); } destroy() { super.destroy.call(this); if (this.leafHash) this.leafHash.destroy(); // We cannot zero personalization buffer since it is user provided and we don't want to mutate user input this.personalization = EMPTY; } _cloneInto(to) { const { blockLen, leafLen, leafHash, outputLen, rounds } = this; to || (to = new KangarooTwelve(blockLen, leafLen, outputLen, rounds, {})); super._cloneInto(to); if (leafHash) to.leafHash = leafHash._cloneInto(to.leafHash); to.personalization.set(this.personalization); to.leafLen = this.leafLen; to.chunkPos = this.chunkPos; to.chunksDone = this.chunksDone; return to; } clone() { return this._cloneInto(); } } // Default to 32 bytes, so it can be used without opts const k12 = wrapConstructorWithOpts((opts = {}) => new KangarooTwelve(168, 32, chooseLen(opts, 32), 12, opts)); // MarsupilamiFourteen const m14 = wrapConstructorWithOpts((opts = {}) => new KangarooTwelve(136, 64, chooseLen(opts, 64), 14, opts)); // A tiny KDF for various applications like AES key-gen const SCRYPT_FACTOR = 2 ** 19; const PBKDF2_FACTOR = 2 ** 17; // Scrypt KDF function scrypt(password, salt) { return scrypt$1(password, salt, { N: SCRYPT_FACTOR, r: 8, p: 1, dkLen: 32 }); } // PBKDF2-HMAC-SHA256 function pbkdf2(password, salt) { return pbkdf2$1(sha256, password, salt, { c: PBKDF2_FACTOR, dkLen: 32 }); } // Combines two 32-byte byte arrays function xor32(a, b) { bytes(a, 32); bytes(b, 32); const arr = new Uint8Array(32); for (let i = 0; i < 32; i++) { arr[i] = a[i] ^ b[i]; } return arr; } function strHasLength(str, min, max) { return typeof str === 'string' && str.length >= min && str.length <= max; } /** * Derives main seed. Takes a lot of time. Prefer `eskdf` method instead. */ function deriveMainSeed(username, password) { if (!strHasLength(username, 8, 255)) throw new Error('invalid username'); if (!strHasLength(password, 8, 255)) throw new Error('invalid password'); const scr = scrypt(password + '\u{1}', username + '\u{1}'); const pbk = pbkdf2(password + '\u{2}', username + '\u{2}'); const res = xor32(scr, pbk); scr.fill(0); pbk.fill(0); return res; } /** * Converts protocol & accountId pair to HKDF salt & info params. */ function getSaltInfo(protocol, accountId = 0) { // Note that length here also repeats two lines below // We do an additional length check here to reduce the scope of DoS attacks if (!(strHasLength(protocol, 3, 15) && /^[a-z0-9]{3,15}$/.test(protocol))) { throw new Error('invalid protocol'); } // Allow string account ids for some protocols const allowsStr = /^password\d{0,3}|ssh|tor|file$/.test(protocol); let salt; // Extract salt. Default is undefined. if (typeof accountId === 'string') { if (!allowsStr) throw new Error('accountId must be a number'); if (!strHasLength(accountId, 1, 255)) throw new Error('accountId must be valid string'); salt = toBytes(accountId); } else if (Number.isSafeInteger(accountId)) { if (accountId < 0 || accountId > 2 ** 32 - 1) throw new Error('invalid accountId'); // Convert to Big Endian Uint32 salt = new Uint8Array(4); createView(salt).setUint32(0, accountId, false); } else { throw new Error(`accountId must be a number${allowsStr ? ' or string' : ''}`); } const info = toBytes(protocol); return { salt, info }; } function countBytes(num) { if (typeof num !== 'bigint' || num <= BigInt(128)) throw new Error('invalid number'); return Math.ceil(num.toString(2).length / 8); } /** * Parses keyLength and modulus options to extract length of result key. * If modulus is used, adds 64 bits to it as per FIPS 186 B.4.1 to combat modulo bias. */ function getKeyLength(options) { if (!options || typeof options !== 'object') return 32; const hasLen = 'keyLength' in options; const hasMod = 'modulus' in options; if (hasLen && hasMod) throw new Error('cannot combine keyLength and modulus options'); if (!hasLen && !hasMod) throw new Error('must have either keyLength or modulus option'); // FIPS 186 B.4.1 requires at least 64 more bits const l = hasMod ? countBytes(options.modulus) + 8 : options.keyLength; if (!(typeof l === 'number' && l >= 16 && l <= 8192)) throw new Error('invalid keyLength'); return l; } /** * Converts key to bigint and divides it by modulus. Big Endian. * Implements FIPS 186 B.4.1, which removes 0 and modulo bias from output. */ function modReduceKey(key, modulus) { const _1 = BigInt(1); const num = BigInt('0x' + bytesToHex(key)); // check for ui8a, then bytesToNumber() const res = (num % (modulus - _1)) + _1; // Remove 0 from output if (res < _1) throw new Error('expected positive number'); // Guard against bad values const len = key.length - 8; // FIPS requires 64 more bits = 8 bytes const hex = res.toString(16).padStart(len * 2, '0'); // numberToHex() const bytes = hexToBytes(hex); if (bytes.length !== len) throw new Error('invalid length of result key'); return bytes; } /** * ESKDF * @param username - username, email, or identifier, min: 8 characters, should have enough entropy * @param password - password, min: 8 characters, should have enough entropy * @example * const kdf = await eskdf('example-university', 'beginning-new-example'); * const key = kdf.deriveChildKey('aes', 0); * console.log(kdf.fingerprint); * kdf.expire(); */ async function eskdf(username, password) { // We are using closure + object instead of class because // we want to make `seed` non-accessible for any external function. let seed = deriveMainSeed(username, password); function deriveCK(protocol, accountId = 0, options) { bytes(seed, 32); // Validates protocol & accountId const { salt, info } = getSaltInfo(protocol, accountId); // Validates options const keyLength = getKeyLength(options); const key = hkdf(sha256, seed, salt, info, keyLength); // Modulus has already been validated return options && 'modulus' in options ? modReduceKey(key, options.modulus) : key; } function expire() { if (seed) seed.fill(1); seed = undefined; } // prettier-ignore const fingerprint = Array.from(deriveCK('fingerprint', 0)) .slice(0, 6) .map((char) => char.toString(16).padStart(2, '0').toUpperCase()) .join(':'); return Object.freeze({ deriveChildKey: deriveCK, expire, fingerprint }); } // var utils = { bytesToHex, randomBytes }; CHECK THIS var sha256_1 = {};//require("@noble/hashes/sha256"); sha256_1.sha256 = sha256; var hmac_1 = {}; // require("@noble/hashes/hmac"); hmac_1.hmac = hmac; var ripemd160_1 = {}; // require("@noble/hashes/ripemd160"); ripemd160_1.ripemd160 = ripemd160; hashmini.blake2b = blake2b; hashmini.blake2s = blake2s; hashmini.blake3 = blake3; hashmini.cshake128 = cshake128; hashmini.cshake256 = cshake256; hashmini.eskdf = eskdf; hashmini.hkdf = hkdf; hashmini.hmac = hmac; hashmini.k12 = k12; hashmini.keccak_224 = keccak_224; hashmini.keccak_256 = keccak_256; hashmini.keccak_384 = keccak_384; hashmini.keccak_512 = keccak_512; hashmini.kmac128 = kmac128; hashmini.kmac256 = kmac256; hashmini.m14 = m14; hashmini.pbkdf2 = pbkdf2$1; hashmini.pbkdf2Async = pbkdf2Async; hashmini.ripemd160 = ripemd160; hashmini.scrypt = scrypt$1; hashmini.scryptAsync = scryptAsync; hashmini.sha256 = sha256; hashmini.sha3_224 = sha3_224; hashmini.sha3_256 = sha3_256; hashmini.sha3_384 = sha3_384; hashmini.sha3_512 = sha3_512; hashmini.sha512 = sha512; hashmini.utils = utils; Object.defineProperty(hashmini, '__esModule', { value: true }); /****** * START OF MICRO PACKED SECTION * * ******/ var P = {}; /* //ROHIT THIS FUNCTION IS CREATING PROBLEMS var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); };*/ //Alternative implementation through ChatGPT var __assign = (this && this.__assign) || function () { return Object.assign.apply(Object, arguments); }; Object.defineProperty(P, "__esModule", { value: true }); P.magicBytes = P.magic = P.optional = P.flagged = P.flag = P.bytesFormatted = P.lazy = P.validate = P.apply = P.hex = P.cstring = P.string = P.bytes = P.bool = P.I8 = P.U8 = P.I16BE = P.I16LE = P.U16BE = P.U16LE = P.I32BE = P.I32LE = P.U32BE = P.U32LE = P.int = P.I64BE = P.I64LE = P.U64BE = P.U64LE = P.I128BE = P.I128LE = P.U128BE = P.U128LE = P.I256BE = P.I256LE = P.U256BE = P.U256LE = P.bigint = P.bits = P.coders = P.isCoder = P.wrap = P.checkBounds = P.Writer = P.Reader = P.isBytes = P.concatBytes = P.equalBytes = P.NULL = P.EMPTY = void 0; P.debug = P.nothing = P.base64armor = P.pointer = P.padRight = P.padLeft = P.ZeroPad = P.bitset = P.mappedTag = P.tag = P.map = P.array = P.prefix = P.tuple = P.struct = P.constant = void 0; //NOT NEEDED AFTTER INTEGRATION var base = require("@scure/base"); /** * TODO: * - Holes, simplify pointers. Hole is some sized element which is skipped at encoding, * but later other elements can write to it by path * - Composite / tuple keys for dict * - Web UI for easier debugging. We can wrap every coder to something that would write * start & end positions to; and we can colorize specific bytes used by specific coder */ // Useful default values P.EMPTY = new Uint8Array(); // Empty bytes array P.NULL = new Uint8Array([0]); // NULL function equalBytes(a, b) { if (a.length !== b.length) return false; for (var i = 0; i < a.length; i++) if (a[i] !== b[i]) return false; return true; } P.equalBytes = equalBytes; function concatBytes() { var arrays = []; for (var _i = 0; _i < arguments.length; _i++) { arrays[_i] = arguments[_i]; } if (arrays.length === 1) return arrays[0]; var length = arrays.reduce(function (a, arr) { return a + arr.length; }, 0); var result = new Uint8Array(length); for (var i = 0, pad = 0; i < arrays.length; i++) { var arr = arrays[i]; result.set(arr, pad); pad += arr.length; } return result; } P.concatBytes = concatBytes; var isBytes = function (b) { return b instanceof Uint8Array; }; P.isBytes = isBytes; // Utils var Reader = /** @class */ (function () { function Reader(data, path, fieldPath) { if (path === void 0) { path = []; } if (fieldPath === void 0) { fieldPath = []; } this.data = data; this.path = path; this.fieldPath = fieldPath; this.pos = 0; this.hasPtr = false; this.bitBuf = 0; this.bitPos = 0; } Reader.prototype.err = function (msg) { return new Error("Reader(".concat(this.fieldPath.join('/'), "): ").concat(msg)); }; // read bytes by absolute offset Reader.prototype.absBytes = function (n) { if (n > this.data.length) throw new Error('absBytes: Unexpected end of buffer'); return this.data.subarray(n); }; Reader.prototype.bytes = function (n, peek) { if (peek === void 0) { peek = false; } if (this.bitPos) throw this.err('readBytes: bitPos not empty'); if (!Number.isFinite(n)) throw this.err("readBytes: wrong length=".concat(n)); if (this.pos + n > this.data.length) throw this.err('readBytes: Unexpected end of buffer'); var slice = this.data.subarray(this.pos, this.pos + n); if (!peek) this.pos += n; return slice; }; Reader.prototype.byte = function (peek) { if (peek === void 0) { peek = false; } if (this.bitPos) throw this.err('readByte: bitPos not empty'); return this.data[peek ? this.pos : this.pos++]; }; Object.defineProperty(Reader.prototype, "leftBytes", { get: function () { return this.data.length - this.pos; }, enumerable: false, configurable: true }); Reader.prototype.isEnd = function () { return this.pos >= this.data.length && !this.bitPos; }; Reader.prototype.length = function (len) { var byteLen; if (isCoder(len)) byteLen = Number(len.decodeStream(this)); else if (typeof len === 'number') byteLen = len; else if (typeof len === 'string') byteLen = getPath(this.path, len.split('/')); if (typeof byteLen === 'bigint') byteLen = Number(byteLen); if (typeof byteLen !== 'number') throw this.err("Wrong length: ".concat(byteLen)); return byteLen; }; // Note: bits reads in BE (left to right) mode: (0b1000_0000).readBits(1) == 1 Reader.prototype.bits = function (bits) { if (bits > 32) throw this.err('BitReader: cannot read more than 32 bits in single call'); var out = 0; while (bits) { if (!this.bitPos) { this.bitBuf = this.data[this.pos++]; this.bitPos = 8; } var take = Math.min(bits, this.bitPos); this.bitPos -= take; // out = (out << take) | ((this.bitBuf >> this.bitPos) & (Math.pow(2, take) - 1)); out = (out << take) | ((this.bitBuf >> this.bitPos) & (BigInt(2) ** BigInt(take) - 1)); // this.bitBuf &= Math.pow(2, this.bitPos) - 1; this.bitBuf &= BigInt(2) ** BigInt(this.bitPos) - 1; bits -= take; } // Fix signed integers return out >>> 0; }; Reader.prototype.find = function (needle, pos) { if (pos === void 0) { pos = this.pos; } if (!(0, P.isBytes)(needle)) throw this.err("find: needle is not bytes! ".concat(needle)); if (this.bitPos) throw this.err('findByte: bitPos not empty'); if (!needle.length) throw this.err("find: needle is empty"); // indexOf should be faster than full equalBytes check for (var idx = pos; (idx = this.data.indexOf(needle[0], idx)) !== -1; idx++) { if (idx === -1) return; var leftBytes = this.data.length - idx; if (leftBytes < needle.length) return; if (equalBytes(needle, this.data.subarray(idx, idx + needle.length))) return idx; } }; Reader.prototype.finish = function () { if (this.isEnd() || this.hasPtr) return; throw this.err("".concat(this.leftBytes, " bytes ").concat(this.bitPos, " bits left after unpack: ").concat(base.hex.encode(this.data.slice(this.pos)))); }; Reader.prototype.fieldPathPush = function (s) { this.fieldPath.push(s); }; Reader.prototype.fieldPathPop = function () { this.fieldPath.pop(); }; return Reader; }()); P.Reader = Reader; var Writer = /** @class */ (function () { function Writer(path, fieldPath) { if (path === void 0) { path = []; } if (fieldPath === void 0) { fieldPath = []; } this.path = path; this.fieldPath = fieldPath; this.buffers = []; this.pos = 0; this.ptrs = []; this.bitBuf = 0; this.bitPos = 0; } Writer.prototype.err = function (msg) { return new Error("Writer(".concat(this.fieldPath.join('/'), "): ").concat(msg)); }; Writer.prototype.bytes = function (b) { if (this.bitPos) throw this.err('writeBytes: ends with non-empty bit buffer'); this.buffers.push(b); this.pos += b.length; }; Writer.prototype.byte = function (b) { if (this.bitPos) throw this.err('writeByte: ends with non-empty bit buffer'); this.buffers.push(new Uint8Array([b])); this.pos++; }; Object.defineProperty(Writer.prototype, "buffer", { get: function () { if (this.bitPos) throw this.err('buffer: ends with non-empty bit buffer'); var buf = concatBytes.apply(void 0, this.buffers); for (var _i = 0, _a = this.ptrs; _i < _a.length; _i++) { var ptr = _a[_i]; var pos = buf.length; buf = concatBytes(buf, ptr.buffer); var val = ptr.ptr.encode(pos); for (var i = 0; i < val.length; i++) buf[ptr.pos + i] = val[i]; } return buf; }, enumerable: false, configurable: true }); Writer.prototype.length = function (len, value) { if (len === null) return; if (isCoder(len)) return len.encodeStream(this, value); var byteLen; if (typeof len === 'number') byteLen = len; else if (typeof len === 'string') byteLen = getPath(this.path, len.split('/')); if (typeof byteLen === 'bigint') byteLen = Number(byteLen); if (byteLen === undefined || byteLen !== value) throw this.err("Wrong length: ".concat(byteLen, " len=").concat(len, " exp=").concat(value)); }; Writer.prototype.bits = function (value, bits) { if (bits > 32) throw this.err('writeBits: cannot write more than 32 bits in single call'); // if (value >= Math.pow(2, bits)) if (value >= BigInt(2) ** BigInt(bits)) throw this.err("writeBits: value (".concat(value, ") >= 2**bits (").concat(bits, ")")); while (bits) { var take = Math.min(bits, 8 - this.bitPos); this.bitBuf = (this.bitBuf << take) | (value >> (bits - take)); this.bitPos += take; bits -= take; // value &= Math.pow(2, bits) - 1; value &= BigInt(2) ** BigInt(bits) - 1; if (this.bitPos === 8) { this.bitPos = 0; this.buffers.push(new Uint8Array([this.bitBuf])); this.pos++; } } }; Writer.prototype.fieldPathPush = function (s) { this.fieldPath.push(s); }; Writer.prototype.fieldPathPop = function () { this.fieldPath.pop(); }; return Writer; }()); P.Writer = Writer; // Immutable LE<->BE var swap = function (b) { return Uint8Array.from(b).reverse(); }; function checkBounds(p, value, bits, signed) { if (signed) { // [-(2**(32-1)), 2**(32-1)-1] // var signBit = Math.pow(2n, (bits - 1n)); var signBit = BigInt(2n) ** BigInt((bits - 1n)); if (value < -signBit || value >= signBit) throw p.err('sInt: value out of bounds'); } else { // [0, 2**32-1] // if (0n > value || value >= Math.pow(2n, bits)) if (0n > value || value >= BigInt(2n) ** BigInt(bits))//Math.pow(2n, bits)) throw p.err('uInt: value out of bounds'); } } P.checkBounds = checkBounds; // Wrap stream encoder into generic encoder function wrap(inner) { return __assign(__assign({}, inner), { encode: function (value) { var w = new Writer(); inner.encodeStream(w, value); return w.buffer; }, decode: function (data) { var r = new Reader(data); var res = inner.decodeStream(r); r.finish(); return res; } }); } P.wrap = wrap; function getPath(objPath, path) { objPath = Array.from(objPath); var i = 0; for (; i < path.length; i++) { if (path[i] === '..') objPath.pop(); else break; } var cur = objPath.pop(); for (; i < path.length; i++) { if (!cur || cur[path[i]] === undefined) return undefined; cur = cur[path[i]]; } return cur; } function isCoder(elm) { return (typeof elm.encode === 'function' && typeof elm.encodeStream === 'function' && typeof elm.decode === 'function' && typeof elm.decodeStream === 'function'); } P.isCoder = isCoder; // Coders (like in @scure/base) for common operations // TODO: // - move to base? very generic converters, not releated to base and packed // - encode/decode -> from/to? coder->convert? function dict() { return { encode: function (from) { var to = {}; for (var _i = 0, from_1 = from; _i < from_1.length; _i++) { var _a = from_1[_i], name = _a[0], value = _a[1]; if (to[name] !== undefined) throw new Error("coders.dict: same key(".concat(name, ") appears twice in struct")); to[name] = value; } return to; }, decode: function (to) { return Object.entries(to); }, }; } // Safely converts bigint to number // Sometimes pointers / tags use u64 or other big numbers which cannot be represented by number, // but we still can use them since real value will be smaller than u32 var number = { encode: function (from) { if (from > BigInt(Number.MAX_SAFE_INTEGER)) throw new Error("coders.number: element bigger than MAX_SAFE_INTEGER=".concat(from)); return Number(from); }, decode: function (to) { return BigInt(to); }, // decode: function (to) {if (!isNaN(to)) { return BigInt(to);} else { return to;} }, }; function tsEnum(e) { return { encode: function (from) { return e[from]; }, decode: function (to) { return e[to]; }, }; } function decimal(precision) { //var decimalMask = Math.pow(10n, BigInt(precision)); //CHECK THIS var decimalMask = BitInt(10n) ** BigInt(precision); return { encode: function (from) { var s = (from < 0n ? -from : from).toString(10); var sep = s.length - precision; if (sep < 0) { s = s.padStart(s.length - sep, '0'); sep = 0; } var i = s.length - 1; for (; i >= sep && s[i] === '0'; i--) ; var _a = [s.slice(0, sep), s.slice(sep, i + 1)], int = _a[0], frac = _a[1]; if (!int) int = '0'; if (from < 0n) int = '-' + int; if (!frac) return int; return "".concat(int, ".").concat(frac); }, decode: function (to) { var neg = false; if (to.startsWith('-')) { neg = true; to = to.slice(1); } var sep = to.indexOf('.'); sep = sep === -1 ? to.length : sep; var _a = [to.slice(0, sep), to.slice(sep + 1)], intS = _a[0], fracS = _a[1]; var int = BigInt(intS) * decimalMask; var fracLen = Math.min(fracS.length, precision); // var frac = BigInt(fracS.slice(0, fracLen)) * Math.pow(10n, BigInt(precision - fracLen)); var frac = BigInt(fracS.slice(0, fracLen)) * (BigInt(10n) ** BigInt(precision - fracLen)); var value = int + frac; return neg ? -value : value; }, }; } /** * Allows to split big conditional coders into a small one; also sort of parser combinator: * * `encode = [Ae, Be]; decode = [Ad, Bd]` * -> * `match([{encode: Ae, decode: Ad}, {encode: Be; decode: Bd}])` * * 1. It is easier to reason: encode/decode of specific part are closer to each other * 2. Allows composable coders and ability to add conditions on runtime * @param lst * @returns */ function match(lst) { return { encode: function (from) { for (var _i = 0, lst_1 = lst; _i < lst_1.length; _i++) { var c = lst_1[_i]; var elm = c.encode(from); if (elm !== undefined) return elm; } throw new Error("match/encode: cannot find match in ".concat(from)); }, decode: function (to) { for (var _i = 0, lst_2 = lst; _i < lst_2.length; _i++) { var c = lst_2[_i]; var elm = c.decode(to); if (elm !== undefined) return elm; } throw new Error("match/decode: cannot find match in ".concat(to)); }, }; } P.coders = { dict: dict, number: number, tsEnum: tsEnum, decimal: decimal, match: match }; // PackedCoders var bits = function (len) { return wrap({ encodeStream: function (w, value) { return w.bits(value, len); }, decodeStream: function (r) { return r.bits(len); }, }); }; P.bits = bits; var bigint = function (size, le, signed) { if (le === void 0) { le = false; } if (signed === void 0) { signed = false; } return wrap({ size: size, encodeStream: function (w, value) { if (typeof value !== 'number' && typeof value !== 'bigint') throw w.err("bigint: invalid value: ".concat(value)); var _value = BigInt(value); var bLen = BigInt(size); checkBounds(w, _value, 8n * bLen, !!signed); // var signBit = Math.pow(2n, (8n * bLen - 1n)); var signBit = BigInt(2n) ** BigInt(8n * bLen - 1n); if (signed && _value < 0) _value = _value | signBit; var b = []; for (var i = 0; i < size; i++) { b.push(Number(_value & 255n)); _value >>= 8n; } var res = new Uint8Array(b).reverse(); w.bytes(le ? res.reverse() : res); }, decodeStream: function (r) { var bLen = BigInt(size); var value = r.bytes(size); if (le) value = swap(value); var b = swap(value); //var signBit = Math.pow(2n, (8n * bLen - 1n)); var signBit = BigInt(2n) ** BigInt(8n * bLen - 1n); var res = 0n; for (var i = 0; i < b.length; i++) res |= BigInt(b[i]) << (8n * BigInt(i)); if (signed && res & signBit) res = (res ^ signBit) - signBit; checkBounds(r, res, 8n * bLen, !!signed); return res; }, }); }; P.bigint = bigint; P.U256LE = (0, P.bigint)(32, true); P.U256BE = (0, P.bigint)(32, false); P.I256LE = (0, P.bigint)(32, true, true); P.I256BE = (0, P.bigint)(32, false, true); P.U128LE = (0, P.bigint)(16, true); P.U128BE = (0, P.bigint)(16, false); P.I128LE = (0, P.bigint)(16, true, true); P.I128BE = (0, P.bigint)(16, false, true); P.U64LE = (0, P.bigint)(8, true); P.U64BE = (0, P.bigint)(8, false); P.I64LE = (0, P.bigint)(8, true, true); P.I64BE = (0, P.bigint)(8, false, true); var int = function (size, le, signed) { if (le === void 0) { le = false; } if (signed === void 0) { signed = false; } if (size > 6) throw new Error('int supports size up to 6 bytes (48 bits), for other use bigint'); return apply((0, P.bigint)(size, le, signed), P.coders.number); }; P.int = int; P.U32LE = (0, P.int)(4, true); P.U32BE = (0, P.int)(4, false); P.I32LE = (0, P.int)(4, true, true); P.I32BE = (0, P.int)(4, false, true); P.U16LE = (0, P.int)(2, true); P.U16BE = (0, P.int)(2, false); P.I16LE = (0, P.int)(2, true, true); P.I16BE = (0, P.int)(2, false, true); P.U8 = (0, P.int)(1, false); P.I8 = (0, P.int)(1, false, true); P.bool = wrap({ size: 1, encodeStream: function (w, value) { return w.byte(value ? 1 : 0); }, decodeStream: function (r) { var value = r.byte(); if (value !== 0 && value !== 1) throw r.err("bool: invalid value ".concat(value)); return value === 1; }, }); // Can be done w array, but specific implementation should be // faster: no need to create js array of numbers. var bytes = function (len, le) { if (le === void 0) { le = false; } return wrap({ size: typeof len === 'number' ? len : undefined, encodeStream: function (w, value) { if (!(0, P.isBytes)(value)) throw w.err("bytes: invalid value ".concat(value)); if (!(0, P.isBytes)(len)) w.length(len, value.length); w.bytes(le ? swap(value) : value); if ((0, P.isBytes)(len)) w.bytes(len); }, decodeStream: function (r) { var bytes; if ((0, P.isBytes)(len)) { var tPos = r.find(len); if (!tPos) throw r.err("bytes: cannot find terminator"); bytes = r.bytes(tPos - r.pos); r.bytes(len.length); } else bytes = r.bytes(len === null ? r.leftBytes : r.length(len)); return le ? swap(bytes) : bytes; }, }); }; P.bytes = bytes; var string = function (len, le) { if (le === void 0) { le = false; } var inner = (0, P.bytes)(len, le); return wrap({ size: inner.size, encodeStream: function (w, value) { return inner.encodeStream(w, base.utf8.decode(value)); }, decodeStream: function (r) { return base.utf8.encode(inner.decodeStream(r)); }, }); }; P.string = string; P.cstring = (0, P.string)(P.NULL); var hex = function (len, le, withZero) { if (le === void 0) { le = false; } if (withZero === void 0) { withZero = false; } var inner = (0, P.bytes)(len, le); return wrap({ size: inner.size, encodeStream: function (w, value) { if (withZero && !value.startsWith('0x')) throw new Error('hex(withZero=true).encode input should start with 0x'); var bytes = base.hex.decode(withZero ? value.slice(2) : value); return inner.encodeStream(w, bytes); }, decodeStream: function (r) { return (withZero ? '0x' : '') + base.hex.encode(inner.decodeStream(r)); }, }); }; P.hex = hex; // Interoperability with base function apply(inner, b) { if (!isCoder(inner)) throw new Error("apply: invalid inner value ".concat(inner)); return wrap({ size: inner.size, encodeStream: function (w, value) { var innerValue; try { innerValue = b.decode(value); } catch (e) { throw w.err('' + e); } return inner.encodeStream(w, innerValue); }, decodeStream: function (r) { var innerValue = inner.decodeStream(r); try { return b.encode(innerValue); } catch (e) { throw r.err('' + e); } }, }); } P.apply = apply; // Additional check of values both on encode and decode steps. // E.g. to force uint32 to be 1..10 function validate(inner, fn) { if (!isCoder(inner)) throw new Error("validate: invalid inner value ".concat(inner)); return wrap({ size: inner.size, encodeStream: function (w, value) { return inner.encodeStream(w, fn(value)); }, decodeStream: function (r) { return fn(inner.decodeStream(r)); }, }); } P.validate = validate; function lazy(fn) { return wrap({ encodeStream: function (w, value) { return fn().encodeStream(w, value); }, decodeStream: function (r) { return fn().decodeStream(r); }, }); } P.lazy = lazy; var bytesFormatted = function (len, fmt, le) { if (le === void 0) { le = false; } var inner = (0, P.bytes)(len, le); return wrap({ size: inner.size, encodeStream: function (w, value) { return inner.encodeStream(w, base.bytes(fmt, value)); }, decodeStream: function (r) { return base.str(fmt, inner.decodeStream(r)); }, }); }; P.bytesFormatted = bytesFormatted; // Returns true if some marker exists, otherwise false. Xor argument flips behaviour var flag = function (flagValue, xor) { if (xor === void 0) { xor = false; } return wrap({ size: flagValue.length, encodeStream: function (w, value) { if (!!value !== xor) w.bytes(flagValue); }, decodeStream: function (r) { var hasFlag = r.leftBytes >= flagValue.length; if (hasFlag) { hasFlag = equalBytes(r.bytes(flagValue.length, true), flagValue); // Found flag, advance cursor position if (hasFlag) r.bytes(flagValue.length); } // hasFlag ^ xor return hasFlag !== xor; }, }); }; P.flag = flag; // Decode/encode only if flag found function flagged(path, inner, def) { if (!isCoder(inner)) throw new Error("flagged: invalid inner value ".concat(inner)); return wrap({ encodeStream: function (w, value) { if (typeof path === 'string') { if (getPath(w.path, path.split('/'))) inner.encodeStream(w, value); else if (def) inner.encodeStream(w, def); } else { path.encodeStream(w, !!value); if (!!value) inner.encodeStream(w, value); else if (def) inner.encodeStream(w, def); } }, decodeStream: function (r) { var hasFlag = false; if (typeof path === 'string') hasFlag = getPath(r.path, path.split('/')); else hasFlag = path.decodeStream(r); // If there is a flag -- decode and return value if (hasFlag) return inner.decodeStream(r); else if (def) inner.decodeStream(r); }, }); } P.flagged = flagged; function optional(flag, inner, def) { if (!isCoder(flag) || !isCoder(inner)) throw new Error("optional: invalid flag or inner value flag=".concat(flag, " inner=").concat(inner)); return wrap({ size: def !== undefined && flag.size && inner.size ? flag.size + inner.size : undefined, encodeStream: function (w, value) { flag.encodeStream(w, !!value); if (value) inner.encodeStream(w, value); else if (def !== undefined) inner.encodeStream(w, def); }, decodeStream: function (r) { if (flag.decodeStream(r)) return inner.decodeStream(r); else if (def !== undefined) inner.decodeStream(r); }, }); } P.optional = optional; function magic(inner, constant, check) { if (check === void 0) { check = true; } if (!isCoder(inner)) throw new Error("flagged: invalid inner value ".concat(inner)); return wrap({ size: inner.size, encodeStream: function (w, value) { return inner.encodeStream(w, constant); }, decodeStream: function (r) { var value = inner.decodeStream(r); if ((check && typeof value !== 'object' && value !== constant) || ((0, P.isBytes)(constant) && !equalBytes(constant, value))) { throw r.err("magic: invalid value: ".concat(value, " !== ").concat(constant)); } return; }, }); } P.magic = magic; var magicBytes = function (constant) { var c = typeof constant === 'string' ? base.utf8.decode(constant) : constant; return magic((0, P.bytes)(c.length), c); }; P.magicBytes = magicBytes; function constant(c) { return wrap({ encodeStream: function (w, value) { if (value !== c) throw new Error("constant: invalid value ".concat(value, " (exp: ").concat(c, ")")); }, decodeStream: function (r) { return c; }, }); } P.constant = constant; function sizeof(fields) { var size = 0; for (var _i = 0, fields_1 = fields; _i < fields_1.length; _i++) { var f = fields_1[_i]; if (!f.size) return; size += f.size; } return size; } function struct(fields) { if (Array.isArray(fields)) throw new Error('Packed.Struct: got array instead of object'); return wrap({ size: sizeof(Object.values(fields)), encodeStream: function (w, value) { if (typeof value !== 'object' || value === null) throw w.err("struct: invalid value ".concat(value)); w.path.push(value); for (var name in fields) { w.fieldPathPush(name); var field = fields[name]; field.encodeStream(w, value[name]); w.fieldPathPop(); } w.path.pop(); }, decodeStream: function (r) { var res = {}; r.path.push(res); for (var name in fields) { r.fieldPathPush(name); res[name] = fields[name].decodeStream(r); r.fieldPathPop(); } r.path.pop(); return res; }, }); } P.struct = struct; function tuple(fields) { if (!Array.isArray(fields)) throw new Error("Packed.Tuple: got ".concat(typeof fields, " instead of array")); return wrap({ size: sizeof(fields), encodeStream: function (w, value) { if (!Array.isArray(value)) throw w.err("tuple: invalid value ".concat(value)); w.path.push(value); for (var i = 0; i < fields.length; i++) { w.fieldPathPush('' + i); fields[i].encodeStream(w, value[i]); w.fieldPathPop(); } w.path.pop(); }, decodeStream: function (r) { var res = []; r.path.push(res); for (var i = 0; i < fields.length; i++) { r.fieldPathPush('' + i); res.push(fields[i].decodeStream(r)); r.fieldPathPop(); } r.path.pop(); return res; }, }); } P.tuple = tuple; function prefix(len, inner) { if (!isCoder(inner)) throw new Error("prefix: invalid inner value ".concat(inner)); if ((0, P.isBytes)(len)) throw new Error("prefix: len cannot be Uint8Array"); var b = (0, P.bytes)(len); return wrap({ size: typeof len === 'number' ? len : undefined, encodeStream: function (w, value) { var wChild = new Writer(w.path, w.fieldPath); inner.encodeStream(wChild, value); b.encodeStream(w, wChild.buffer); }, decodeStream: function (r) { var data = b.decodeStream(r); return inner.decodeStream(new Reader(data, r.path, r.fieldPath)); }, }); } P.prefix = prefix; function array(len, inner) { if (!isCoder(inner)) throw new Error("array: invalid inner value ".concat(inner)); return wrap({ size: typeof len === 'number' && inner.size ? len * inner.size : undefined, encodeStream: function (w, value) { if (!Array.isArray(value)) throw w.err("array: invalid value ".concat(value)); if (!(0, P.isBytes)(len)) w.length(len, value.length); w.path.push(value); for (var i = 0; i < value.length; i++) { w.fieldPathPush('' + i); var elm = value[i]; var startPos = w.pos; inner.encodeStream(w, elm); if ((0, P.isBytes)(len)) { // Terminator is bigger than elm size, so skip if (len.length > w.pos - startPos) continue; var data = w.buffer.subarray(startPos, w.pos); // There is still possible case when multiple elements create terminator, // but it is hard to catch here, will be very slow if (equalBytes(data.subarray(0, len.length), len)) throw w.err("array: inner element encoding same as separator. elm=".concat(elm, " data=").concat(data)); } w.fieldPathPop(); } w.path.pop(); if ((0, P.isBytes)(len)) w.bytes(len); }, decodeStream: function (r) { var res = []; if (len === null) { var i = 0; r.path.push(res); while (!r.isEnd()) { r.fieldPathPush('' + i++); res.push(inner.decodeStream(r)); r.fieldPathPop(); if (inner.size && r.leftBytes < inner.size) break; } r.path.pop(); } else if ((0, P.isBytes)(len)) { var i = 0; r.path.push(res); while (true) { if (equalBytes(r.bytes(len.length, true), len)) { // Advance cursor position if terminator found r.bytes(len.length); break; } r.fieldPathPush('' + i++); res.push(inner.decodeStream(r)); r.fieldPathPop(); } r.path.pop(); } else { r.fieldPathPush('arrayLen'); var length = r.length(len); r.fieldPathPop(); r.path.push(res); for (var i = 0; i < length; i++) { r.fieldPathPush('' + i); res.push(inner.decodeStream(r)); r.fieldPathPop(); } r.path.pop(); } return res; }, }); } P.array = array; function map(inner, variants) { if (!isCoder(inner)) throw new Error("map: invalid inner value ".concat(inner)); var variantNames = new Map(); for (var k in variants) variantNames.set(variants[k], k); return wrap({ size: inner.size, encodeStream: function (w, value) { if (typeof value !== 'string') throw w.err("map: invalid value ".concat(value)); if (!(value in variants)) throw w.err("Map: unknown variant: ".concat(value)); inner.encodeStream(w, variants[value]); }, decodeStream: function (r) { var variant = inner.decodeStream(r); var name = variantNames.get(variant); if (name === undefined) throw r.err("Enum: unknown value: ".concat(variant, " ").concat(Array.from(variantNames.keys()))); return name; }, }); } P.map = map; function tag(tag, variants) { if (!isCoder(tag)) throw new Error("tag: invalid tag value ".concat(tag)); return wrap({ size: tag.size, encodeStream: function (w, value) { var TAG = value.TAG, data = value.data; var dataType = variants[TAG]; if (!dataType) throw w.err("Tag: invalid tag ".concat(TAG.toString())); tag.encodeStream(w, TAG); dataType.encodeStream(w, data); }, decodeStream: function (r) { var TAG = tag.decodeStream(r); var dataType = variants[TAG]; if (!dataType) throw r.err("Tag: invalid tag ".concat(TAG)); return { TAG: TAG, data: dataType.decodeStream(r) }; }, }); } P.tag = tag; // Takes {name: [value, coder]} function mappedTag(tagCoder, variants) { if (!isCoder(tagCoder)) throw new Error("mappedTag: invalid tag value ".concat(tag)); var mapValue = {}; var tagValue = {}; for (var key in variants) { mapValue[key] = variants[key][0]; tagValue[key] = variants[key][1]; } return tag(map(tagCoder, mapValue), tagValue); } P.mappedTag = mappedTag; function bitset(names, pad) { if (pad === void 0) { pad = false; } return wrap({ encodeStream: function (w, value) { if (typeof value !== 'object' || value === null) throw w.err("bitset: invalid value ".concat(value)); for (var i = 0; i < names.length; i++) w.bits(+value[names[i]], 1); if (pad && names.length % 8) w.bits(0, 8 - (names.length % 8)); }, decodeStream: function (r) { var out = {}; for (var i = 0; i < names.length; i++) out[names[i]] = !!r.bits(1); if (pad && names.length % 8) r.bits(8 - (names.length % 8)); return out; }, }); } P.bitset = bitset; var ZeroPad = function (_) { return 0; }; P.ZeroPad = ZeroPad; function padLength(blockSize, len) { if (len % blockSize === 0) return 0; return blockSize - (len % blockSize); } function padLeft(blockSize, inner, padFn) { if (!isCoder(inner)) throw new Error("padLeft: invalid inner value ".concat(inner)); var _padFn = padFn || P.ZeroPad; if (!inner.size) throw new Error('padLeft with dynamic size argument is impossible'); return wrap({ size: inner.size + padLength(blockSize, inner.size), encodeStream: function (w, value) { var padBytes = padLength(blockSize, inner.size); for (var i = 0; i < padBytes; i++) w.byte(_padFn(i)); inner.encodeStream(w, value); }, decodeStream: function (r) { r.bytes(padLength(blockSize, inner.size)); return inner.decodeStream(r); }, }); } P.padLeft = padLeft; function padRight(blockSize, inner, padFn) { if (!isCoder(inner)) throw new Error("padRight: invalid inner value ".concat(inner)); var _padFn = padFn || P.ZeroPad; return wrap({ size: inner.size ? inner.size + padLength(blockSize, inner.size) : undefined, encodeStream: function (w, value) { var pos = w.pos; inner.encodeStream(w, value); var padBytes = padLength(blockSize, w.pos - pos); for (var i = 0; i < padBytes; i++) w.byte(_padFn(i)); }, decodeStream: function (r) { var start = r.pos; var res = inner.decodeStream(r); r.bytes(padLength(blockSize, r.pos - start)); return res; }, }); } P.padRight = padRight; function pointer(ptr, inner, sized) { if (sized === void 0) { sized = false; } if (!isCoder(ptr)) throw new Error("pointer: invalid ptr value ".concat(ptr)); if (!isCoder(inner)) throw new Error("pointer: invalid inner value ".concat(inner)); if (!ptr.size) throw new Error('Pointer: unsized ptr'); return wrap({ size: sized ? ptr.size : undefined, encodeStream: function (w, value) { var start = w.pos; ptr.encodeStream(w, 0); w.ptrs.push({ pos: start, ptr: ptr, buffer: inner.encode(value) }); }, decodeStream: function (r) { var ptrVal = ptr.decodeStream(r); // This check enforces termination of parser, if there is backwards pointers, // then it is possible to create loop and cause DoS. if (ptrVal < r.pos) throw new Error('pointer.decodeStream pointer less than position'); r.hasPtr = true; var rChild = new Reader(r.absBytes(ptrVal), r.path, r.fieldPath); return inner.decodeStream(rChild); }, }); } P.pointer = pointer; // lineLen: gpg=64, ssh=70 function base64armor(name, lineLen, inner, checksum) { var markBegin = "-----BEGIN ".concat(name.toUpperCase(), "-----"); var markEnd = "-----END ".concat(name.toUpperCase(), "-----"); return { encode: function (value) { var data = inner.encode(value); var encoded = base.base64.encode(data); var lines = []; for (var i = 0; i < encoded.length; i += lineLen) { var s = encoded.slice(i, i + lineLen); if (s.length) lines.push("".concat(encoded.slice(i, i + lineLen), "\n")); } var body = lines.join(''); if (checksum) body += "=".concat(base.base64.encode(checksum(data)), "\n"); return "".concat(markBegin, "\n\n").concat(body).concat(markEnd, "\n"); }, decode: function (s) { var lines = s.replace(markBegin, '').replace(markEnd, '').trim().split('\n'); lines = lines.map(function (l) { return l.replace('\r', '').trim(); }); if (checksum && lines[lines.length - 1].startsWith('=')) { var body = base.base64.decode(lines.slice(0, -1).join('')); var cs = lines[lines.length - 1].slice(1); var realCS = base.base64.encode(checksum(body)); if (realCS !== cs) throw new Error("Base64Armor: invalid checksum ".concat(cs, " instead of ").concat(realCS)); return inner.decode(body); } return inner.decode(base.base64.decode(lines.join(''))); }, }; } P.base64armor = base64armor; // Does nothing at all P.nothing = magic((0, P.bytes)(0), P.EMPTY); function debug(inner) { if (!isCoder(inner)) throw new Error("debug: invalid inner value ".concat(inner)); var log = function (name, rw, value) { console.log("DEBUG/".concat(name, "(").concat(rw.fieldPath.join('/'), "):"), { type: typeof value, value: value }); return value; }; return wrap({ size: inner.size, encodeStream: function (w, value) { return inner.encodeStream(w, log('encode', w, value)); }, decodeStream: function (r) { return log('decode', r, inner.decodeStream(r)); }, }); } P.debug = debug; /****** * START OF TAPROOT SECTION * * ******/ var __assign = (this && this.__assign) || function () { __assign = Object.assign || function (t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; //var taproot = {}; Object.defineProperty(taproot, "__esModule", { value: true }); taproot.Transaction = taproot.tapLeafHash = taproot.TAP_LEAF_VERSION = taproot._sortPubkeys = taproot.SigHashCoder = taproot.SignatureHash = taproot.Address = taproot.WIF = taproot.parseWitnessProgram = taproot.programToWitness = taproot.OutScript = taproot.p2tr_ms = taproot.p2tr_pk = taproot.p2tr_ns = taproot.combinations = taproot.p2tr = taproot.TAPROOT_UNSPENDABLE_KEY = taproot.taprootListToTree = taproot.p2ms = taproot.p2wpkh = taproot.p2wsh = taproot.p2sh = taproot.p2pkh = taproot.p2pk = taproot.RawPSBTV2 = taproot.RawPSBTV0 = taproot._DebugPSBT = taproot._RawPSBTV2 = taproot._RawPSBTV0 = taproot.TaprootControlBlock = taproot.RawTx = taproot.RawWitness = taproot.RawOutput = taproot.RawInput = taproot.VarBytes = taproot.BTCArray = taproot.CompactSize = taproot.Script = taproot.OPNum = taproot.OP = taproot.cmp = taproot.decimal = taproot.DEFAULT_SEQUENCE = taproot.DEFAULT_LOCKTIME = taproot.DEFAULT_VERSION = taproot.PRECISION = taproot.NETWORK = taproot.taprootTweakPubkey = taproot.taprootTweakPrivKey = taproot.base58check = void 0; taproot.PSBTCombine = taproot.bip32Path = taproot.sortedMultisig = taproot.multisig = taproot.getAddress = void 0; /*! micro-btc-signer - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /***** // ALL OF THESE ARE INTEGRATED ABOVE var secp = require("@noble/secp256k1"); var base = require("@scure/base"); var sha256_1 = require("@noble/hashes/sha256"); var hmac_1 = require("@noble/hashes/hmac"); var ripemd160_1 = require("@noble/hashes/ripemd160"); var P = require("micro-packed"); *****/ var hash160 = function (msg) { return (0, ripemd160_1.ripemd160)((0, sha256_1.sha256)(msg)); }; var sha256x2 = function () { var msgs = []; for (var _i = 0; _i < arguments.length; _i++) { msgs[_i] = arguments[_i]; } return (0, sha256_1.sha256)((0, sha256_1.sha256)(concat.apply(void 0, msgs))); }; var concat = P.concatBytes; // Make base58check work taproot.base58check = base.base58check(sha256_1.sha256); // Enable sync API for noble-secp256k1 secp.utils.hmacSha256Sync = function (key) { var msgs = []; for (var _i = 1; _i < arguments.length; _i++) { msgs[_i - 1] = arguments[_i]; } return (0, hmac_1.hmac)(sha256_1.sha256, key, concat.apply(void 0, msgs)); }; secp.utils.sha256Sync = function () { var msgs = []; for (var _i = 0; _i < arguments.length; _i++) { msgs[_i] = arguments[_i]; } return (0, sha256_1.sha256)(concat.apply(void 0, msgs)); }; var taggedHash = secp.utils.taggedHashSync; var PubT; (function (PubT) { PubT[PubT["ecdsa"] = 0] = "ecdsa"; PubT[PubT["schnorr"] = 1] = "schnorr"; })(PubT || (PubT = {})); var validatePubkey = function (pub, type) { var len = pub.length; if (type === PubT.ecdsa) { if (len === 32) throw new Error('Expected non-Schnorr key'); } else if (type === PubT.schnorr) { if (len !== 32) throw new Error('Expected 32-byte Schnorr key'); } else { throw new Error('Unknown key type'); } secp.Point.fromHex(pub); // does assertValidity return pub; }; function isValidPubkey(pub, type) { try { return !!validatePubkey(pub, type); } catch (e) { return false; } } // Not best way, but closest to bitcoin implementation (easier to check) var hasLowR = function (sig) { return secp.Signature.fromHex(sig).toCompactRawBytes()[0] < 0x80; }; // TODO: move to @noble/secp256k1? function signECDSA(hash, privateKey, lowR) { if (lowR === void 0) { lowR = false; } var sig = secp.signSync(hash, privateKey, { canonical: true }); if (lowR && !hasLowR(sig)) { var extraEntropy = new Uint8Array(32); for (var cnt = 0; cnt < Number.MAX_SAFE_INTEGER; cnt++) { extraEntropy.set(P.U32LE.encode(cnt)); sig = secp.signSync(hash, privateKey, { canonical: true, extraEntropy: extraEntropy }); if (hasLowR(sig)) break; } } return sig; } function taprootTweakPrivKey(privKey, merkleRoot) { if (merkleRoot === void 0) { merkleRoot = new Uint8Array(); } var n = secp.CURVE.n; var priv = secp.utils._normalizePrivateKey(privKey); var point = secp.Point.fromPrivateKey(priv); var tweak = taggedHash('TapTweak', point.toRawX(), merkleRoot); var privWithProperY = point.hasEvenY() ? priv : n - priv; var tweaked = secp.utils.mod(privWithProperY + secp.utils._normalizePrivateKey(tweak), n); return secp.utils._bigintTo32Bytes(tweaked); } taproot.taprootTweakPrivKey = taprootTweakPrivKey; function taprootTweakPubkey(pubKey, h) { var tweak = taggedHash('TapTweak', pubKey, h); var tweaked = secp.Point.fromHex(pubKey).add(secp.Point.fromPrivateKey(tweak)); return [tweaked.toRawX(), !tweaked.hasEvenY()]; } taproot.taprootTweakPubkey = taprootTweakPubkey; // Can be 33 or 64 bytes var PubKeyECDSA = P.validate(P.bytes(null), function (pub) { return validatePubkey(pub, PubT.ecdsa); }); var PubKeySchnorr = P.validate(P.bytes(32), function (pub) { return validatePubkey(pub, PubT.schnorr); }); var SignatureSchnorr = P.validate(P.bytes(null), function (sig) { if (sig.length !== 64 && sig.length !== 65) throw new Error('Schnorr signature should be 64 or 65 bytes long'); return sig; }); function uniqPubkey(pubkeys) { var map = {}; for (var _i = 0, pubkeys_1 = pubkeys; _i < pubkeys_1.length; _i++) { var pub = pubkeys_1[_i]; var key = base.hex.encode(pub); if (map[key]) throw new Error("Multisig: non-uniq pubkey: ".concat(pubkeys.map(base.hex.encode))); map[key] = true; } } taproot.NETWORK = { bech32: 'bc', pubKeyHash: 0x00, scriptHash: 0x05, wif: 0x80, }; taproot.PRECISION = 8; taproot.DEFAULT_VERSION = 2; taproot.DEFAULT_LOCKTIME = 0; taproot.DEFAULT_SEQUENCE = 4294967293; var EMPTY32 = new Uint8Array(32); // Utils //ROHIT taproot.Decimal = 8; //taproot.Decimal = P.coders.decimal(taproot.PRECISION);//CHECK THIS var decimal = {}; decimal.decode = function (numberAsString) { return parseInt(numberAsString * 100000000) }; taproot.decimal = decimal; class Decimal { static decode(bitcoinAmount) { const bitcoinInSatoshis = BigInt(Math.floor(parseFloat(bitcoinAmount) * 100000000)); return bitcoinInSatoshis; } static encode(satoshis) { const bitcoinAmount = (Number(satoshis) / 100000000).toFixed(8); return bitcoinAmount; } } taproot.Decimal = Decimal; function cmp(a, b) { if (a instanceof Uint8Array && b instanceof Uint8Array) { // -1 -> a a==b, 1 -> a>b var len = Math.min(a.length, b.length); for (var i = 0; i < len; i++) if (a[i] != b[i]) return Math.sign(a[i] - b[i]); return Math.sign(a.length - b.length); } else if (a instanceof Uint8Array || b instanceof Uint8Array) throw new Error("cmp: wrong values a=".concat(a, " b=").concat(b)); if ((typeof a === 'bigint' && typeof b === 'number') || (typeof a === 'number' && typeof b === 'bigint')) { a = BigInt(a); b = BigInt(b); } if (a === undefined || b === undefined) throw new Error("cmp: wrong values a=".concat(a, " b=").concat(b)); // Default js comparasion return Number(a > b) - Number(a < b); } taproot.cmp = cmp; // Coders // prettier-ignore var OP; (function (OP) { OP[OP["OP_0"] = 0] = "OP_0"; OP[OP["PUSHDATA1"] = 76] = "PUSHDATA1"; OP[OP["PUSHDATA2"] = 77] = "PUSHDATA2"; OP[OP["PUSHDATA4"] = 78] = "PUSHDATA4"; OP[OP["1NEGATE"] = 79] = "1NEGATE"; OP[OP["RESERVED"] = 80] = "RESERVED"; OP[OP["OP_1"] = 81] = "OP_1"; OP[OP["OP_2"] = 82] = "OP_2"; OP[OP["OP_3"] = 83] = "OP_3"; OP[OP["OP_4"] = 84] = "OP_4"; OP[OP["OP_5"] = 85] = "OP_5"; OP[OP["OP_6"] = 86] = "OP_6"; OP[OP["OP_7"] = 87] = "OP_7"; OP[OP["OP_8"] = 88] = "OP_8"; OP[OP["OP_9"] = 89] = "OP_9"; OP[OP["OP_10"] = 90] = "OP_10"; OP[OP["OP_11"] = 91] = "OP_11"; OP[OP["OP_12"] = 92] = "OP_12"; OP[OP["OP_13"] = 93] = "OP_13"; OP[OP["OP_14"] = 94] = "OP_14"; OP[OP["OP_15"] = 95] = "OP_15"; OP[OP["OP_16"] = 96] = "OP_16"; // Control OP[OP["NOP"] = 97] = "NOP"; OP[OP["VER"] = 98] = "VER"; OP[OP["IF"] = 99] = "IF"; OP[OP["NOTIF"] = 100] = "NOTIF"; OP[OP["VERIF"] = 101] = "VERIF"; OP[OP["VERNOTIF"] = 102] = "VERNOTIF"; OP[OP["ELSE"] = 103] = "ELSE"; OP[OP["ENDIF"] = 104] = "ENDIF"; OP[OP["VERIFY"] = 105] = "VERIFY"; OP[OP["RETURN"] = 106] = "RETURN"; // Stack OP[OP["TOALTSTACK"] = 107] = "TOALTSTACK"; OP[OP["FROMALTSTACK"] = 108] = "FROMALTSTACK"; OP[OP["2DROP"] = 109] = "2DROP"; OP[OP["2DUP"] = 110] = "2DUP"; OP[OP["3DUP"] = 111] = "3DUP"; OP[OP["2OVER"] = 112] = "2OVER"; OP[OP["2ROT"] = 113] = "2ROT"; OP[OP["2SWAP"] = 114] = "2SWAP"; OP[OP["IFDUP"] = 115] = "IFDUP"; OP[OP["DEPTH"] = 116] = "DEPTH"; OP[OP["DROP"] = 117] = "DROP"; OP[OP["DUP"] = 118] = "DUP"; OP[OP["NIP"] = 119] = "NIP"; OP[OP["OVER"] = 120] = "OVER"; OP[OP["PICK"] = 121] = "PICK"; OP[OP["ROLL"] = 122] = "ROLL"; OP[OP["ROT"] = 123] = "ROT"; OP[OP["SWAP"] = 124] = "SWAP"; OP[OP["TUCK"] = 125] = "TUCK"; // Splice OP[OP["CAT"] = 126] = "CAT"; OP[OP["SUBSTR"] = 127] = "SUBSTR"; OP[OP["LEFT"] = 128] = "LEFT"; OP[OP["RIGHT"] = 129] = "RIGHT"; OP[OP["SIZE"] = 130] = "SIZE"; // Boolean logic OP[OP["INVERT"] = 131] = "INVERT"; OP[OP["AND"] = 132] = "AND"; OP[OP["OR"] = 133] = "OR"; OP[OP["XOR"] = 134] = "XOR"; OP[OP["EQUAL"] = 135] = "EQUAL"; OP[OP["EQUALVERIFY"] = 136] = "EQUALVERIFY"; OP[OP["RESERVED1"] = 137] = "RESERVED1"; OP[OP["RESERVED2"] = 138] = "RESERVED2"; // Numbers OP[OP["1ADD"] = 139] = "1ADD"; OP[OP["1SUB"] = 140] = "1SUB"; OP[OP["2MUL"] = 141] = "2MUL"; OP[OP["2DIV"] = 142] = "2DIV"; OP[OP["NEGATE"] = 143] = "NEGATE"; OP[OP["ABS"] = 144] = "ABS"; OP[OP["NOT"] = 145] = "NOT"; OP[OP["0NOTEQUAL"] = 146] = "0NOTEQUAL"; OP[OP["ADD"] = 147] = "ADD"; OP[OP["SUB"] = 148] = "SUB"; OP[OP["MUL"] = 149] = "MUL"; OP[OP["DIV"] = 150] = "DIV"; OP[OP["MOD"] = 151] = "MOD"; OP[OP["LSHIFT"] = 152] = "LSHIFT"; OP[OP["RSHIFT"] = 153] = "RSHIFT"; OP[OP["BOOLAND"] = 154] = "BOOLAND"; OP[OP["BOOLOR"] = 155] = "BOOLOR"; OP[OP["NUMEQUAL"] = 156] = "NUMEQUAL"; OP[OP["NUMEQUALVERIFY"] = 157] = "NUMEQUALVERIFY"; OP[OP["NUMNOTEQUAL"] = 158] = "NUMNOTEQUAL"; OP[OP["LESSTHAN"] = 159] = "LESSTHAN"; OP[OP["GREATERTHAN"] = 160] = "GREATERTHAN"; OP[OP["LESSTHANOREQUAL"] = 161] = "LESSTHANOREQUAL"; OP[OP["GREATERTHANOREQUAL"] = 162] = "GREATERTHANOREQUAL"; OP[OP["MIN"] = 163] = "MIN"; OP[OP["MAX"] = 164] = "MAX"; OP[OP["WITHIN"] = 165] = "WITHIN"; // Crypto OP[OP["RIPEMD160"] = 166] = "RIPEMD160"; OP[OP["SHA1"] = 167] = "SHA1"; OP[OP["SHA256"] = 168] = "SHA256"; OP[OP["HASH160"] = 169] = "HASH160"; OP[OP["HASH256"] = 170] = "HASH256"; OP[OP["CODESEPARATOR"] = 171] = "CODESEPARATOR"; OP[OP["CHECKSIG"] = 172] = "CHECKSIG"; OP[OP["CHECKSIGVERIFY"] = 173] = "CHECKSIGVERIFY"; OP[OP["CHECKMULTISIG"] = 174] = "CHECKMULTISIG"; OP[OP["CHECKMULTISIGVERIFY"] = 175] = "CHECKMULTISIGVERIFY"; // Expansion OP[OP["NOP1"] = 176] = "NOP1"; OP[OP["CHECKLOCKTIMEVERIFY"] = 177] = "CHECKLOCKTIMEVERIFY"; OP[OP["CHECKSEQUENCEVERIFY"] = 178] = "CHECKSEQUENCEVERIFY"; OP[OP["NOP4"] = 179] = "NOP4"; OP[OP["NOP5"] = 180] = "NOP5"; OP[OP["NOP6"] = 181] = "NOP6"; OP[OP["NOP7"] = 182] = "NOP7"; OP[OP["NOP8"] = 183] = "NOP8"; OP[OP["NOP9"] = 184] = "NOP9"; OP[OP["NOP10"] = 185] = "NOP10"; // BIP 342 OP[OP["CHECKSIGADD"] = 186] = "CHECKSIGADD"; // Invalid OP[OP["INVALID"] = 255] = "INVALID"; })(OP = taproot.OP || (taproot.OP = {})); // OP_\n to numeric value // TODO: maybe add numbers to script parser for this case? // prettier-ignore var OPNum; (function (OPNum) { OPNum[OPNum["OP_0"] = 0] = "OP_0"; OPNum[OPNum["OP_1"] = 1] = "OP_1"; OPNum[OPNum["OP_2"] = 2] = "OP_2"; OPNum[OPNum["OP_3"] = 3] = "OP_3"; OPNum[OPNum["OP_4"] = 4] = "OP_4"; OPNum[OPNum["OP_5"] = 5] = "OP_5"; OPNum[OPNum["OP_6"] = 6] = "OP_6"; OPNum[OPNum["OP_7"] = 7] = "OP_7"; OPNum[OPNum["OP_8"] = 8] = "OP_8"; OPNum[OPNum["OP_9"] = 9] = "OP_9"; OPNum[OPNum["OP_10"] = 10] = "OP_10"; OPNum[OPNum["OP_11"] = 11] = "OP_11"; OPNum[OPNum["OP_12"] = 12] = "OP_12"; OPNum[OPNum["OP_13"] = 13] = "OP_13"; OPNum[OPNum["OP_14"] = 14] = "OP_14"; OPNum[OPNum["OP_15"] = 15] = "OP_15"; OPNum[OPNum["OP_16"] = 16] = "OP_16"; })(OPNum = taproot.OPNum || (taproot.OPNum = {})); function OPtoNumber(op) { if (typeof op === 'string' && OP[op] !== undefined && OPNum[op] !== undefined) return OPNum[op]; } // Converts script bytes to parsed script // 5221030000000000000000000000000000000000000000000000000000000000000001210300000000000000000000000000000000000000000000000000000000000000022103000000000000000000000000000000000000000000000000000000000000000353ae // => // OP_2 // 030000000000000000000000000000000000000000000000000000000000000001 // 030000000000000000000000000000000000000000000000000000000000000002 // 030000000000000000000000000000000000000000000000000000000000000003 // OP_3 // CHECKMULTISIG // TODO: simplify like CompactSize? taproot.Script = P.wrap({ encodeStream: function (w, value) { for (var _i = 0, value_1 = value; _i < value_1.length; _i++) { var o = value_1[_i]; if (typeof o === 'string') { if (OP[o] === undefined) throw new Error("Unknown opcode=".concat(o)); w.byte(OP[o]); continue; } var len = o.length; if (len < OP.PUSHDATA1) w.byte(len); else if (len <= 0xff) { w.byte(OP.PUSHDATA1); w.byte(len); } else if (len <= 0xffff) { w.byte(OP.PUSHDATA2); w.bytes(P.U16LE.encode(len)); } else { w.byte(OP.PUSHDATA4); w.bytes(P.U32LE.encode(len)); } w.bytes(o); } }, decodeStream: function (r) { var out = []; while (!r.isEnd()) { var cur = r.byte(); // if 0 < cur < 78 if (OP.OP_0 < cur && cur <= OP.PUSHDATA4) { var len = void 0; if (cur < OP.PUSHDATA1) len = cur; else if (cur === OP.PUSHDATA1) len = P.U8.decodeStream(r); else if (cur === OP.PUSHDATA2) len = P.U16LE.decodeStream(r); else if (cur === OP.PUSHDATA4) len = P.U32LE.decodeStream(r); else throw new Error('Should be not possible'); out.push(r.bytes(len)); } else { var op = OP[cur]; if (op === undefined) throw new Error("Unknown opcode=".concat(cur.toString(16))); out.push(op); } } return out; }, }); // BTC specific variable length integer encoding // https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer var CSLimits = { 0xfd: [0xfd, 2, 253n, 65535n], 0xfe: [0xfe, 4, 65536n, 4294967295n], 0xff: [0xff, 8, 4294967296n, 18446744073709551615n], }; taproot.CompactSize = P.wrap({ encodeStream: function (w, value) { if (typeof value === 'number') value = BigInt(value); if (0n <= value && value <= 252n) return w.byte(Number(value)); for (var _i = 0, _a = Object.values(CSLimits); _i < _a.length; _i++) { var _b = _a[_i], flag = _b[0], bytes = _b[1], start = _b[2], stop = _b[3]; if (start > value || value > stop) continue; w.byte(flag); for (var i = 0; i < bytes; i++) w.byte(Number((value >> (8n * BigInt(i))) & 0xffn)); return; } throw w.err("VarInt too big: ".concat(value)); }, decodeStream: function (r) { var b0 = r.byte(); if (b0 <= 0xfc) return BigInt(b0); var _a = CSLimits[b0], _ = _a[0], bytes = _a[1], start = _a[2]; var num = 0n; for (var i = 0; i < bytes; i++) num |= BigInt(r.byte()) << (8n * BigInt(i)); if (num < start) throw r.err("Wrong CompactSize(".concat(8 * bytes, ")")); return num; }, }); // Same thing, but in number instead of bigint. Checks for safe integer inside var CompactSizeLen = P.apply(taproot.CompactSize, P.coders.number); // Array of size var BTCArray = function (t) { return P.array(taproot.CompactSize, t); }; taproot.BTCArray = BTCArray; // ui8a of size taproot.VarBytes = P.bytes(taproot.CompactSize); taproot.RawInput = P.struct({ hash: P.bytes(32, true), index: P.U32LE, finalScriptSig: taproot.VarBytes, sequence: P.U32LE, // ? }); taproot.RawOutput = P.struct({ amount: P.U64LE, script: taproot.VarBytes }); var EMPTY_OUTPUT = { amount: 0xffffffffffffffffn, script: P.EMPTY, }; // SegWit v0 stack of witness buffers taproot.RawWitness = P.array(CompactSizeLen, taproot.VarBytes); // https://en.bitcoin.it/wiki/Protocol_documentation#tx // TODO: more tests. Unsigned tx has version=2 for some reason, // probably we're exporting broken unsigned tx // Related: https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki var _RawTx = P.struct({ version: P.I32LE, segwitFlag: P.flag(new Uint8Array([0x00, 0x01])), inputs: (0, taproot.BTCArray)(taproot.RawInput), outputs: (0, taproot.BTCArray)(taproot.RawOutput), witnesses: P.flagged('segwitFlag', P.array('inputs/length', taproot.RawWitness)), // Need to handle that? // < 500000000 Block number at which this transaction is unlocked // >= 500000000 UNIX timestamp at which this transaction is unlocked lockTime: P.U32LE, }); function validateRawTx(tx) { if (tx.segwitFlag && tx.witnesses && !tx.witnesses.length) throw new Error('Segwit flag with empty witnesses array'); return tx; } taproot.RawTx = P.validate(_RawTx, validateRawTx); var BIP32Der = P.struct({ fingerprint: P.U32BE, path: P.array(null, P.U32LE), }); // var _TaprootControlBlock = P.struct({ version: P.U8, internalKey: P.bytes(32), merklePath: P.array(null, P.bytes(32)), }); taproot.TaprootControlBlock = P.validate(_TaprootControlBlock, function (cb) { if (cb.merklePath.length > 128) throw new Error('TaprootControlBlock: merklePath should be of length 0..128 (inclusive)'); return cb; }); var TaprootBIP32Der = P.struct({ hashes: P.array(CompactSizeLen, P.bytes(32)), der: BIP32Der, }); // {name: [tag, keyCoder, valueCoder]} var PSBTGlobal = { // TODO: RAW TX here unsignedTx: [0x00, false, taproot.RawTx, [0], [2], [0]], // The 78 byte serialized extended public key as defined by BIP 32. xpub: [0x01, P.bytes(78), BIP32Der, [], [], [0, 2]], txVersion: [0x02, false, P.U32LE, [2], [0], [2]], fallbackLocktime: [0x03, false, P.U32LE, [], [0], [2]], inputCount: [0x04, false, CompactSizeLen, [2], [0], [2]], outputCount: [0x05, false, CompactSizeLen, [2], [0], [2]], // bitfield txModifiable: [0x06, false, P.U8, [], [0], [2]], version: [0xfb, false, P.U32LE, [], [], [0, 2]], // key = propietary: [0xfc, P.bytes(null), P.bytes(null), [], [], [0, 2]], }; var PSBTInput = { nonWitnessUtxo: [0x00, false, taproot.RawTx, [], [], [0, 2]], witnessUtxo: [0x01, false, taproot.RawOutput, [], [], [0, 2]], partialSig: [0x02, PubKeyECDSA, P.bytes(null), [], [], [0, 2]], sighashType: [0x03, false, P.U32LE, [], [], [0, 2]], redeemScript: [0x04, false, P.bytes(null), [], [], [0, 2]], witnessScript: [0x05, false, P.bytes(null), [], [], [0, 2]], bip32Derivation: [0x06, PubKeyECDSA, BIP32Der, [], [], [0, 2]], finalScriptSig: [0x07, false, P.bytes(null), [], [], [0, 2]], finalScriptWitness: [0x08, false, taproot.RawWitness, [], [], [0, 2]], porCommitment: [0x09, false, P.bytes(null), [], [], [0, 2]], ripemd160: [0x0a, P.bytes(20), P.bytes(null), [], [], [0, 2]], sha256: [0x0b, P.bytes(32), P.bytes(null), [], [], [0, 2]], hash160: [0x0c, P.bytes(20), P.bytes(null), [], [], [0, 2]], hash256: [0x0d, P.bytes(32), P.bytes(null), [], [], [0, 2]], hash: [0x0e, false, P.bytes(32), [2], [0], [2]], index: [0x0f, false, P.U32LE, [2], [0], [2]], sequence: [0x10, false, P.U32LE, [], [0], [2]], requiredTimeLocktime: [0x11, false, P.U32LE, [], [0], [2]], requiredHeightLocktime: [0x12, false, P.U32LE, [], [0], [2]], tapKeySig: [0x13, false, SignatureSchnorr, [], [], [0, 2]], tapScriptSig: [ 0x14, P.struct({ pubKey: PubKeySchnorr, leafHash: P.bytes(32) }), SignatureSchnorr, [], [], [0, 2], ], // value = <8-bit uint leaf version> tapLeafScript: [0x15, taproot.TaprootControlBlock, P.bytes(null), [], [], [0, 2]], tapBip32Derivation: [0x16, P.bytes(32), TaprootBIP32Der, [], [], [0, 2]], tapInternalKey: [0x17, false, PubKeySchnorr, [], [], [0, 2]], tapMerkleRoot: [0x18, false, P.bytes(32), [], [], [0, 2]], propietary: [0xfc, P.bytes(null), P.bytes(null), [], [], [0, 2]], }; // All other keys removed when finalizing var PSBTInputFinalKeys = [ 'hash', 'sequence', 'index', 'witnessUtxo', 'nonWitnessUtxo', 'finalScriptSig', 'finalScriptWitness', 'unknown', ]; // Can be modified even on signed input var PSBTInputUnsignedKeys = [ 'partialSig', 'finalScriptSig', 'finalScriptWitness', 'tapKeySig', 'tapScriptSig', ]; var PSBTOutput = { redeemScript: [0x00, false, P.bytes(null), [], [], [0, 2]], witnessScript: [0x01, false, P.bytes(null), [], [], [0, 2]], bip32Derivation: [0x02, PubKeyECDSA, BIP32Der, [], [], [0, 2]], amount: [0x03, false, P.I64LE, [2], [0], [2]], script: [0x04, false, P.bytes(null), [2], [0], [2]], tapInternalKey: [0x05, false, PubKeySchnorr, [], [], [0, 2]], /* {<8-bit uint depth> <8-bit uint leaf version> }* */ tapTree: [ 0x06, false, P.array(null, P.struct({ depth: P.U8, version: P.U8, script: taproot.VarBytes, })), [], [], [0, 2], ], tapBip32Derivation: [0x07, PubKeySchnorr, TaprootBIP32Der, [], [], [0, 2]], propietary: [0xfc, P.bytes(null), P.bytes(null), [], [], [0, 2]], }; // Can be modified even on signed input var PSBTOutputUnsignedKeys = []; var PSBTKeyPair = P.array(P.NULL, P.struct({ // := WHERE keylen = len(keytype)+len(keydata) key: P.prefix(CompactSizeLen, P.struct({ type: CompactSizeLen, key: P.bytes(null) })), // := value: P.bytes(CompactSizeLen), })); var PSBTUnknownKey = P.struct({ type: CompactSizeLen, key: P.bytes(null) }); // Key cannot be 'unknown', value coder cannot be array for elements with empty key function PSBTKeyMap(psbtEnum) { // -> Record var byType = {}; for (var k in psbtEnum) { var _a = psbtEnum[k], num = _a[0], kc = _a[1], vc = _a[2]; byType[num] = [k, kc, vc]; } return P.wrap({ encodeStream: function (w, value) { var out = []; var _loop_1 = function (name) { var val = value[name]; if (val === undefined) return "continue"; var _c = psbtEnum[name], type_1 = _c[0], kc = _c[1], vc = _c[2]; if (!kc) out.push({ key: { type: type_1, key: P.EMPTY }, value: vc.encode(val) }); else { // TODO: check here if there is duplicate keys var kv = val.map(function (_a) { var k = _a[0], v = _a[1]; return [ kc.encode(k), vc.encode(v), ]; }); // sort by keys kv.sort(function (a, b) { return cmp(a[0], b[0]); }); for (var _d = 0, kv_1 = kv; _d < kv_1.length; _d++) { var _e = kv_1[_d], key = _e[0], value_2 = _e[1]; out.push({ key: { key: key, type: type_1 }, value: value_2 }); } } }; // Because we use order of psbtEnum, keymap is sorted here for (var name in psbtEnum) { _loop_1(name); } if (value.unknown) { value.unknown.sort(function (a, b) { return cmp(a[0], b[0]); }); for (var _i = 0, _a = value.unknown; _i < _a.length; _i++) { var _b = _a[_i], k = _b[0], v = _b[1]; out.push({ key: PSBTUnknownKey.decode(k), value: v }); } } PSBTKeyPair.encodeStream(w, out); }, decodeStream: function (r) { var raw = PSBTKeyPair.decodeStream(r); var out = {}; var noKey = {}; for (var _i = 0, raw_1 = raw; _i < raw_1.length; _i++) { var elm = raw_1[_i]; var name = 'unknown'; var key = elm.key.key; var value = elm.value; if (byType[elm.key.type]) { var _a = byType[elm.key.type], _name = _a[0], kc = _a[1], vc = _a[2]; name = _name; if (!kc && key.length) { throw new Error("PSBT: Non-empty key for ".concat(name, " (key=").concat(base.hex.encode(key), " value=").concat(base.hex.encode(value))); } key = kc ? kc.decode(key) : undefined; value = vc.decode(value); if (!kc) { if (out[name]) throw new Error("PSBT: Same keys: ".concat(name, " (key=").concat(key, " value=").concat(value, ")")); out[name] = value; noKey[name] = true; continue; } } else { // For unknown: add key type inside key key = PSBTUnknownKey.encode({ type: elm.key.type, key: elm.key.key }); } // Only keyed elements at this point if (noKey[name]) throw new Error("PSBT: Key type with empty key and no key=".concat(name, " val=").concat(value)); if (!out[name]) out[name] = []; out[name].push([key, value]); } return out; }, }); } // Basic sanity check for scripts function checkWSH(s, witnessScript) { if (!P.equalBytes(s.hash, (0, sha256_1.sha256)(witnessScript))) throw new Error('checkScript: wsh wrong witnessScript hash'); var w = taproot.OutScript.decode(witnessScript); if (w.type === 'tr' || w.type === 'tr_ns' || w.type === 'tr_ms') throw new Error("checkScript: P2".concat(w.type, " cannot be wrapped in P2SH")); if (w.type === 'wpkh' || w.type === 'sh') throw new Error("checkScript: P2".concat(w.type, " cannot be wrapped in P2WSH")); } function checkScript(script, redeemScript, witnessScript) { // TODO: revalidate if (script) { var s = taproot.OutScript.decode(script); // TODO: ms||pk maybe work, but there will be no address if (s.type === 'tr_ns' || s.type === 'tr_ms' || s.type === 'ms' || s.type == 'pk') throw new Error("checkScript: non-wrapped ".concat(s.type)); if (s.type === 'sh' && redeemScript) { if (!P.equalBytes(s.hash, hash160(redeemScript))) throw new Error('checkScript: sh wrong redeemScript hash'); var r = taproot.OutScript.decode(redeemScript); if (r.type === 'tr' || r.type === 'tr_ns' || r.type === 'tr_ms') throw new Error("checkScript: P2".concat(r.type, " cannot be wrapped in P2SH")); // Not sure if this unspendable, but we cannot represent this via PSBT if (r.type === 'sh') throw new Error('checkScript: P2SH cannot be wrapped in P2SH'); } if (s.type === 'wsh' && witnessScript) checkWSH(s, witnessScript); } if (redeemScript) { var r = taproot.OutScript.decode(redeemScript); if (r.type === 'wsh' && witnessScript) checkWSH(r, witnessScript); } } var PSBTInputCoder = P.validate(PSBTKeyMap(PSBTInput), function (i) { if (i.finalScriptWitness && !i.finalScriptWitness.length) throw new Error('validateInput: wmpty finalScriptWitness'); //if (i.finalScriptSig && !i.finalScriptSig.length) throw new Error('validateInput: empty finalScriptSig'); if (i.partialSig && !i.partialSig.length) throw new Error('Empty partialSig'); if (i.partialSig) for (var _i = 0, _a = i.partialSig; _i < _a.length; _i++) { var _b = _a[_i], k = _b[0], v = _b[1]; validatePubkey(k, PubT.ecdsa); } if (i.bip32Derivation) for (var _c = 0, _d = i.bip32Derivation; _c < _d.length; _c++) { var _e = _d[_c], k = _e[0], v = _e[1]; validatePubkey(k, PubT.ecdsa); } // Locktime = unsigned little endian integer greater than or equal to 500000000 representing if (i.requiredTimeLocktime !== undefined && i.requiredTimeLocktime < 500000000) throw new Error("validateInput: wrong timeLocktime=".concat(i.requiredTimeLocktime)); // unsigned little endian integer greater than 0 and less than 500000000 if (i.requiredHeightLocktime !== undefined && (i.requiredHeightLocktime <= 0 || i.requiredHeightLocktime >= 500000000)) throw new Error("validateInput: wrong heighLocktime=".concat(i.requiredHeightLocktime)); if (i.nonWitnessUtxo && i.index !== undefined) { var last = i.nonWitnessUtxo.outputs.length - 1; if (i.index > last) throw new Error("validateInput: index(".concat(i.index, ") not in nonWitnessUtxo")); var prevOut = i.nonWitnessUtxo.outputs[i.index]; if (i.witnessUtxo && (!P.equalBytes(i.witnessUtxo.script, prevOut.script) || i.witnessUtxo.amount !== prevOut.amount)) throw new Error('validateInput: witnessUtxo different from nonWitnessUtxo'); } if (i.tapLeafScript) { // tap leaf version appears here twice: in control block and at the end of script for (var _f = 0, _g = i.tapLeafScript; _f < _g.length; _f++) { var _h = _g[_f], k = _h[0], v = _h[1]; if ((k.version & 254) !== v[v.length - 1]) throw new Error('validateInput: tapLeafScript version mimatch'); if (v[v.length - 1] & 1) throw new Error('validateInput: tapLeafScript version has parity bit!'); } } return i; }); var PSBTOutputCoder = P.validate(PSBTKeyMap(PSBTOutput), function (o) { if (o.bip32Derivation) for (var _i = 0, _a = o.bip32Derivation; _i < _a.length; _i++) { var _b = _a[_i], k = _b[0], v = _b[1]; validatePubkey(k, PubT.ecdsa); } return o; }); var PSBTGlobalCoder = P.validate(PSBTKeyMap(PSBTGlobal), function (g) { var version = g.version || 0; if (version === 0) { if (!g.unsignedTx) throw new Error('PSBTv0: missing unsignedTx'); if (g.unsignedTx.segwitFlag || g.unsignedTx.witnesses) throw new Error('PSBTv0: witness in unsingedTx'); for (var _i = 0, _a = g.unsignedTx.inputs; _i < _a.length; _i++) { var inp = _a[_i]; if (inp.finalScriptSig && inp.finalScriptSig.length) throw new Error('PSBTv0: input scriptSig found in unsignedTx'); } } return g; }); taproot._RawPSBTV0 = P.struct({ magic: P.magic(P.string(new Uint8Array([0xff])), 'psbt'), global: PSBTGlobalCoder, inputs: P.array('global/unsignedTx/inputs/length', PSBTInputCoder), outputs: P.array(null, PSBTOutputCoder), }); taproot._RawPSBTV2 = P.struct({ magic: P.magic(P.string(new Uint8Array([0xff])), 'psbt'), global: PSBTGlobalCoder, inputs: P.array('global/inputCount', PSBTInputCoder), outputs: P.array('global/outputCount', PSBTOutputCoder), }); taproot._DebugPSBT = P.struct({ magic: P.magic(P.string(new Uint8Array([0xff])), 'psbt'), items: P.array(null, P.apply(P.array(P.NULL, P.tuple([P.hex(CompactSizeLen), P.bytes(taproot.CompactSize)])), P.coders.dict())), }); function validatePSBTFields(version, info, lst) { for (var k in lst) { if (k === 'unknown') continue; if (!info[k]) continue; var _a = info[k].slice(-3), reqInc = _a[0], reqExc = _a[1], allowInc = _a[2]; if (reqExc.includes(version) || !allowInc.includes(version)) throw new Error("PSBTv".concat(version, ": field ").concat(k, " is not allowed")); } for (var k in info) { var _b = info[k].slice(-3), reqInc = _b[0], reqExc = _b[1], allowInc = _b[2]; if (reqInc.includes(version) && lst[k] === undefined) throw new Error("PSBTv".concat(version, ": missing required field ").concat(k)); } } function cleanPSBTFields(version, info, lst) { var out = {}; for (var k in lst) { if (k !== 'unknown') { if (!info[k]) continue; var _a = info[k].slice(-3), reqInc = _a[0], reqExc = _a[1], allowInc = _a[2]; if (reqExc.includes(version) || !allowInc.includes(version)) continue; } out[k] = lst[k]; } return out; } function validatePSBT(tx) { var version = (tx && tx.global && tx.global.version) || 0; validatePSBTFields(version, PSBTGlobal, tx.global); for (var _i = 0, _a = tx.inputs; _i < _a.length; _i++) { var i = _a[_i]; validatePSBTFields(version, PSBTInput, i); } for (var _b = 0, _c = tx.outputs; _b < _c.length; _b++) { var o = _c[_b]; validatePSBTFields(version, PSBTOutput, o); } // We allow only one empty element at the end of map (compat with bitcoinjs-lib bug) var inputCount = !version ? tx.global.unsignedTx.inputs.length : tx.global.inputCount; if (tx.inputs.length < inputCount) throw new Error('Not enough inputs'); var inputsLeft = tx.inputs.slice(inputCount); if (inputsLeft.length > 1 || (inputsLeft.length && Object.keys(inputsLeft[0]).length)) throw new Error("Unexpected inputs left in tx=".concat(inputsLeft)); // Same for inputs var outputCount = !version ? tx.global.unsignedTx.outputs.length : tx.global.outputCount; if (tx.outputs.length < outputCount) throw new Error('Not outputs inputs'); var outputsLeft = tx.outputs.slice(outputCount); if (outputsLeft.length > 1 || (outputsLeft.length && Object.keys(outputsLeft[0]).length)) throw new Error("Unexpected outputs left in tx=".concat(outputsLeft)); return tx; } // Check if object doens't have custom constructor (like Uint8Array/Array) var isPlainObject = function (obj) { return Object.prototype.toString.call(obj) === '[object Object]' && obj.constructor === Object; }; function type(v) { if (v instanceof Uint8Array) return 'bytes'; if (Array.isArray(v)) return 'array'; if (['number', 'string', 'bigint', 'boolean', 'undefined'].includes(typeof v)) return typeof v; if (v === null) return 'null'; // typeof null=object if (isPlainObject(v)) return 'object'; throw new Error("Unknown type=".concat(v)); } // Basic structure merge: object = {...old, ...new}, arrays = old.concat(new). other -> replace // function merge( // psbtEnum: T, // val: PSBTKeyMapKeys, // cur?: PSBTKeyMapKeys // ): PSBTKeyMapKeys { // } function mergeKeyMap(psbtEnum, val, cur, allowedFields) { var res = __assign(__assign({}, cur), val); var _loop_2 = function (k) { var key = k; var _a = psbtEnum[key], _ = _a[0], kC = _a[1], vC = _a[2]; var cannotChange = allowedFields && !allowedFields.includes(k); if (val[k] === undefined && k in val) { if (cannotChange) throw new Error("Cannot remove signed field=".concat(k)); delete res[k]; } else if (kC) { var oldKV = (cur && cur[k] ? cur[k] : []); var newKV = val[key]; if (newKV) { if (!Array.isArray(newKV)) throw new Error("keyMap(".concat(k, "): KV pairs should be [k, v][]")); // Decode hex in k-v newKV = newKV.map(function (val) { if (val.length !== 2) throw new Error("keyMap(".concat(k, "): KV pairs should be [k, v][]")); return [ typeof val[0] === 'string' ? kC.decode(base.hex.decode(val[0])) : val[0], typeof val[1] === 'string' ? vC.decode(base.hex.decode(val[1])) : val[1], ]; }); var map_1 = {}; var add = function (kStr, k, v) { if (map_1[kStr] === undefined) { map_1[kStr] = [k, v]; return; } var oldVal = base.hex.encode(vC.encode(map_1[kStr][1])); var newVal = base.hex.encode(vC.encode(v)); if (oldVal !== newVal) throw new Error("keyMap(".concat(key, "): same key=").concat(kStr, " oldVal=").concat(oldVal, " newVal=").concat(newVal)); }; for (var _i = 0, oldKV_1 = oldKV; _i < oldKV_1.length; _i++) { var _b = oldKV_1[_i], k_1 = _b[0], v = _b[1]; var kStr = base.hex.encode(kC.encode(k_1)); add(kStr, k_1, v); } for (var _c = 0, newKV_1 = newKV; _c < newKV_1.length; _c++) { var _d = newKV_1[_c], k_2 = _d[0], v = _d[1]; var kStr = base.hex.encode(kC.encode(k_2)); // undefined removes previous value if (v === undefined) delete map_1[kStr]; else add(kStr, k_2, v); } res[key] = Object.values(map_1); } } else if (typeof res[k] === 'string') { res[k] = vC.decode(base.hex.decode(res[k])); } }; // All arguments can be provided as hex for (var k in psbtEnum) { _loop_2(k); } // Remove unknown keys for (var k in res) if (!psbtEnum[k]) delete res[k]; return res; } taproot.RawPSBTV0 = P.validate(taproot._RawPSBTV0, validatePSBT); taproot.RawPSBTV2 = P.validate(taproot._RawPSBTV2, validatePSBT); // (TxHash, Idx) var TxHashIdx = P.struct({ hash: P.bytes(32, true), index: P.U32LE }); // /Coders var isBytes = function (b) { return b instanceof Uint8Array; }; var OutPK = { encode: function (from) { if (from.length !== 2 || !P.isBytes(from[0]) || !isValidPubkey(from[0], PubT.ecdsa) || from[1] !== 'CHECKSIG') return; return { type: 'pk', pubkey: from[0] }; }, decode: function (to) { return (to.type === 'pk' ? [to.pubkey, 'CHECKSIG'] : undefined); }, }; var p2pk = function (pubkey, network) { if (network === void 0) { network = taproot.NETWORK; } if (!isValidPubkey(pubkey, PubT.ecdsa)) throw new Error('P2PK: invalid publicKey'); return { type: 'pk', script: taproot.OutScript.encode({ type: 'pk', pubkey: pubkey }), }; }; taproot.p2pk = p2pk; var OutPKH = { encode: function (from) { if (from.length !== 5 || from[0] !== 'DUP' || from[1] !== 'HASH160' || !isBytes(from[2])) return; if (from[3] !== 'EQUALVERIFY' || from[4] !== 'CHECKSIG') return; return { type: 'pkh', hash: from[2] }; }, decode: function (to) { return to.type === 'pkh' ? ['DUP', 'HASH160', to.hash, 'EQUALVERIFY', 'CHECKSIG'] : undefined; }, }; var p2pkh = function (publicKey, network) { if (network === void 0) { network = taproot.NETWORK; } if (!isValidPubkey(publicKey, PubT.ecdsa)) throw new Error('P2PKH: invalid publicKey'); var hash = hash160(publicKey); return { type: 'pkh', script: taproot.OutScript.encode({ type: 'pkh', hash: hash }), address: Address(network).encode({ type: 'pkh', hash: hash }), }; }; taproot.p2pkh = p2pkh; var OutSH = { encode: function (from) { if (from.length !== 3 || from[0] !== 'HASH160' || !isBytes(from[1]) || from[2] !== 'EQUAL') return; return { type: 'sh', hash: from[1] }; }, decode: function (to) { return to.type === 'sh' ? ['HASH160', to.hash, 'EQUAL'] : undefined; }, }; var p2sh = function (child, network) { if (network === void 0) { network = taproot.NETWORK; } var hash = hash160(child.script); var script = taproot.OutScript.encode({ type: 'sh', hash: hash }); checkScript(script, child.script, child.witnessScript); var res = { type: 'sh', redeemScript: child.script, script: taproot.OutScript.encode({ type: 'sh', hash: hash }), address: Address(network).encode({ type: 'sh', hash: hash }), }; if (child.witnessScript) res.witnessScript = child.witnessScript; return res; }; taproot.p2sh = p2sh; var OutWSH = { encode: function (from) { if (from.length !== 2 || from[0] !== 'OP_0' || !isBytes(from[1])) return; if (from[1].length !== 32) return; return { type: 'wsh', hash: from[1] }; }, decode: function (to) { return (to.type === 'wsh' ? ['OP_0', to.hash] : undefined); }, }; var p2wsh = function (child, network) { if (network === void 0) { network = taproot.NETWORK; } var hash = (0, sha256_1.sha256)(child.script); var script = taproot.OutScript.encode({ type: 'wsh', hash: hash }); checkScript(script, undefined, child.script); return { type: 'wsh', witnessScript: child.script, script: taproot.OutScript.encode({ type: 'wsh', hash: hash }), address: Address(network).encode({ type: 'wsh', hash: hash }), }; }; taproot.p2wsh = p2wsh; var OutWPKH = { encode: function (from) { if (from.length !== 2 || from[0] !== 'OP_0' || !isBytes(from[1])) return; if (from[1].length !== 20) return; return { type: 'wpkh', hash: from[1] }; }, decode: function (to) { return (to.type === 'wpkh' ? ['OP_0', to.hash] : undefined); }, }; var p2wpkh = function (publicKey, network) { if (network === void 0) { network = taproot.NETWORK; } if (!isValidPubkey(publicKey, PubT.ecdsa)) throw new Error('P2WPKH: invalid publicKey'); if (publicKey.length === 65) throw new Error('P2WPKH: uncompressed public key'); var hash = hash160(publicKey); return { type: 'wpkh', script: taproot.OutScript.encode({ type: 'wpkh', hash: hash }), address: Address(network).encode({ type: 'wpkh', hash: hash }), }; }; taproot.p2wpkh = p2wpkh; var OutMS = { encode: function (from) { var last = from.length - 1; if (from[last] !== 'CHECKMULTISIG') return; var m = OPtoNumber(from[0]); var n = OPtoNumber(from[last - 1]); if (m === undefined || n === undefined) throw new Error('OutScript.encode/multisig wrong params'); var pubkeys = from.slice(1, -2); // Any is ok, check in for later if (n !== pubkeys.length) throw new Error('OutScript.encode/multisig: wrong length'); return { type: 'ms', m: m, pubkeys: pubkeys }; // we don't need n, since it is the same as pubkeys }, // checkmultisig(n, ..pubkeys, m) decode: function (to) { return to.type === 'ms' ? __spreadArray(__spreadArray(["OP_".concat(to.m)], to.pubkeys, true), ["OP_".concat(to.pubkeys.length), 'CHECKMULTISIG'], false) : undefined; }, }; var p2ms = function (m, pubkeys, allowSamePubkeys) { if (allowSamePubkeys === void 0) { allowSamePubkeys = false; } if (!allowSamePubkeys) uniqPubkey(pubkeys); return { type: 'ms', script: taproot.OutScript.encode({ type: 'ms', pubkeys: pubkeys, m: m }) }; }; taproot.p2ms = p2ms; var OutTR = { encode: function (from) { if (from.length !== 2 || from[0] !== 'OP_1' || !isBytes(from[1])) return; return { type: 'tr', pubkey: from[1] }; }, decode: function (to) { return (to.type === 'tr' ? ['OP_1', to.pubkey] : undefined); }, }; // Helper for generating binary tree from list, with weights function taprootListToTree(taprootList) { // Clone input in order to not corrupt it var lst = Array.from(taprootList); // We have at least 2 elements => can create branch while (lst.length >= 2) { // Sort: elements with smallest weight are in the end of queue lst.sort(function (a, b) { return (b.weight || 1) - (a.weight || 1); }); var b = lst.pop(); var a = lst.pop(); var weight = ((a === null || a === void 0 ? void 0 : a.weight) || 1) + ((b === null || b === void 0 ? void 0 : b.weight) || 1); lst.push({ weight: weight, // Unwrap children array childs: [a.childs || a, b.childs || b], }); } // At this point there is always 1 element in lst var last = lst[0]; return (last.childs || last); } taproot.taprootListToTree = taprootListToTree; function checkTaprootScript(script, allowUnknowOutput) { if (allowUnknowOutput === void 0) { allowUnknowOutput = false; } var out = taproot.OutScript.decode(script); if (out.type === 'unknown' && allowUnknowOutput) return; if (!['tr_ns', 'tr_ms'].includes(out.type)) throw new Error("P2TR: invalid leaf script=".concat(out.type)); } //Contructs a binary tree from a list of scripts function taprootHashTree(tree, allowUnknowOutput) { var _a; if (allowUnknowOutput === void 0) { allowUnknowOutput = false; } if (!tree) throw new Error('taprootHashTree: empty tree'); if (Array.isArray(tree) && tree.length === 1) tree = tree[0]; // Terminal node (leaf) if (!Array.isArray(tree)) { var version = tree.leafVersion, leafScript = tree.script, tapInternalKey = tree.tapInternalKey; // Earliest tree walk where we can validate tapScripts if (tree.tapLeafScript || (tree.tapMerkleRoot && !P.equalBytes(tree.tapMerkleRoot, P.EMPTY))) throw new Error('P2TR: tapRoot leafScript cannot have tree'); // Just to be sure that it is spendable if (tapInternalKey && P.equalBytes(tapInternalKey, taproot.TAPROOT_UNSPENDABLE_KEY)) throw new Error('P2TR: tapRoot leafScript cannot have unspendble key'); var script = typeof leafScript === 'string' ? base.hex.decode(leafScript) : leafScript; checkTaprootScript(script, allowUnknowOutput); return { type: 'leaf', tapInternalKey: tapInternalKey, version: version, script: script, hash: (0, taproot.tapLeafHash)(script, version), }; } // If tree / branch is not binary tree, convert it if (tree.length !== 2) tree = taprootListToTree(tree); if (tree.length !== 2) throw new Error('hashTree: non binary tree!'); // branch // NOTE: both nodes should exist var left = taprootHashTree(tree[0], allowUnknowOutput); var right = taprootHashTree(tree[1], allowUnknowOutput); // We cannot swap left/right here, since it will change structure of tree var _b = [left.hash, right.hash], lH = _b[0], rH = _b[1]; if (cmp(rH, lH) === -1) _a = [rH, lH], lH = _a[0], rH = _a[1]; return { type: 'branch', left: left, right: right, hash: taggedHash('TapBranch', lH, rH) }; } taproot.taprootHashTree = taprootHashTree; //Adds a path element to a binary tree function taprootAddPath(tree, path) { if (path === void 0) { path = []; } if (!tree) throw new Error("taprootAddPath: empty tree"); if (tree.type === 'leaf') return __assign(__assign({}, tree), { path: path }); if (tree.type !== 'branch') throw new Error("taprootAddPath: wrong type=".concat(tree)); return __assign(__assign({}, tree), { path: path, // Left element has right hash in path and otherwise left: taprootAddPath(tree.left, __spreadArray([tree.right.hash], path, true)), right: taprootAddPath(tree.right, __spreadArray([tree.left.hash], path, true)) }); } taproot.taprootAddPath = taprootAddPath; //Flattens the tree function taprootWalkTree(tree) { if (!tree) throw new Error("taprootAddPath: empty tree"); if (tree.type === 'leaf') return [tree]; if (tree.type !== 'branch') throw new Error("taprootWalkTree: wrong type=".concat(tree)); return __spreadArray(__spreadArray([], taprootWalkTree(tree.left), true), taprootWalkTree(tree.right), true); } taproot.taprootWalkTree = taprootWalkTree; // Another stupid decision, where lack of standard affects security. // Multisig needs to be generated with some key. // We are using approach from BIP 341/bitcoinjs-lib: SHA256(uncompressedDER(SECP256K1_GENERATOR_POINT)) // It is possible to switch SECP256K1_GENERATOR_POINT with some random point; // but it's too complex to prove. // Also used by bitcoin-core and bitcoinjs-lib taproot.TAPROOT_UNSPENDABLE_KEY = (0, sha256_1.sha256)(secp.Point.BASE.toRawBytes(false)); // Works as key OR tree. // If we only have tree, need to add unspendable key, otherwise // complex multisig wallet can be spent by owner of key only. See TAPROOT_UNSPENDABLE_KEY function p2tr(internalPubKey, tree, network, allowUnknowOutput) { if (network === void 0) { network = taproot.NETWORK; } if (allowUnknowOutput === void 0) { allowUnknowOutput = false; } // Unspendable if (!internalPubKey && !tree) throw new Error('p2tr: should have pubKey or scriptTree (or both)'); var pubKey = typeof internalPubKey === 'string' ? base.hex.decode(internalPubKey) : internalPubKey || taproot.TAPROOT_UNSPENDABLE_KEY; if (!isValidPubkey(pubKey, PubT.schnorr)) throw new Error('p2tr: non-schnorr pubkey'); var hashedTree = tree ? taprootAddPath(taprootHashTree(tree, allowUnknowOutput)) : undefined; var tapMerkleRoot = hashedTree ? hashedTree.hash : undefined; var _a = taprootTweakPubkey(pubKey, tapMerkleRoot || P.EMPTY), tweakedPubkey = _a[0], parity = _a[1]; var leaves; if (hashedTree) { leaves = taprootWalkTree(hashedTree).map(function (l) { return (__assign(__assign({}, l), { controlBlock: taproot.TaprootControlBlock.encode({ version: (l.version || taproot.TAP_LEAF_VERSION) + +parity, internalKey: l.tapInternalKey || pubKey, merklePath: l.path, }) })); }); } var tapLeafScript; if (leaves) { tapLeafScript = leaves.map(function (l) { return [ taproot.TaprootControlBlock.decode(l.controlBlock), concat(l.script, new Uint8Array([l.version || taproot.TAP_LEAF_VERSION])), ]; }); } var res = { type: 'tr', script: taproot.OutScript.encode({ type: 'tr', pubkey: tweakedPubkey }), address: Address(network).encode({ type: 'tr', pubkey: tweakedPubkey }), // For tests tweakedPubkey: tweakedPubkey, // PSBT stuff tapInternalKey: pubKey, }; // Just in case someone would want to select a specific script if (leaves) res.leaves = leaves; if (tapLeafScript) res.tapLeafScript = tapLeafScript; if (tapMerkleRoot) res.tapMerkleRoot = tapMerkleRoot; return res; } taproot.p2tr = p2tr; var OutTRNS = { encode: function (from) { var last = from.length - 1; if (from[last] !== 'CHECKSIG') return; var pubkeys = []; for (var i = 0; i < last; i++) { var elm = from[i]; if (i & 1) { if (elm !== 'CHECKSIGVERIFY') // ROHIT Removing throw, and replacing with return to let the process continue //throw new Error('OutScript.encode/tr_ns: wrong element'); return; if (i === last - 1) // ROHIT Removing throw, and replacing with return to let the process continue //throw new Error('OutScript.encode/tr_ns: wrong element'); return; continue; } if (!isBytes(elm)) //ROHIT TO SOLVE CHEKKSIG ERROR //throw new Error('OutScript.encode/tr_ns: wrong element'); return; pubkeys.push(elm); } return { type: 'tr_ns', pubkeys: pubkeys }; }, decode: function (to) { if (to.type !== 'tr_ns') return; var out = []; for (var i = 0; i < to.pubkeys.length - 1; i++) out.push(to.pubkeys[i], 'CHECKSIGVERIFY'); out.push(to.pubkeys[to.pubkeys.length - 1], 'CHECKSIG'); return out; }, }; // Returns all combinations of size M from lst function combinations(m, list) { var res = []; if (!Array.isArray(list)) throw new Error('combinations: lst arg should be array'); var n = list.length; if (m > n) throw new Error('combinations: m > lst.length, no combinations possible'); /* Basically works as M nested loops like: for (;idx[0]= 0 && idx[i] > n - m + i; i--) { idx[i] = 0; // Overflow in idx[0], break if (i === 0) break main; idx[i - 1] += 1; } // Propagate: idx[i+1] = idx[idx]+1 for (i += 1; i < idx.length; i++) idx[i] = idx[i - 1] + 1; } return res; } taproot.combinations = combinations; /** * M-of-N multi-leaf wallet via p2tr_ns. If m == n, single script is emitted. * Takes O(n^2) if m != n. 99-of-100 is ok, 5-of-100 is not. * `2-of-[A,B,C] => [A,B] | [A,C] | [B,C]` */ var p2tr_ns = function (m, pubkeys, allowSamePubkeys) { if (allowSamePubkeys === void 0) { allowSamePubkeys = false; } if (!allowSamePubkeys) uniqPubkey(pubkeys); return combinations(m, pubkeys).map(function (i) { return ({ type: 'tr_ns', script: taproot.OutScript.encode({ type: 'tr_ns', pubkeys: i }), }); }); }; taproot.p2tr_ns = p2tr_ns; // Taproot public key (case of p2tr_ns) var p2tr_pk = function (pubkey) { return (0, taproot.p2tr_ns)(1, [pubkey], undefined)[0]; }; taproot.p2tr_pk = p2tr_pk; var OutTRMS = { encode: function (from) { var last = from.length - 1; if (from[last] !== 'NUMEQUAL' || from[1] !== 'CHECKSIG') return; var pubkeys = []; var m = OPtoNumber(from[last - 1]); if (m === undefined) return; for (var i = 0; i < last - 1; i++) { var elm = from[i]; if (i & 1) { if (elm !== (i === 1 ? 'CHECKSIG' : 'CHECKSIGADD')) throw new Error('OutScript.encode/tr_ms: wrong element'); continue; } if (!isBytes(elm)) throw new Error('OutScript.encode/tr_ms: wrong key element'); pubkeys.push(elm); } return { type: 'tr_ms', pubkeys: pubkeys, m: m }; }, decode: function (to) { if (to.type !== 'tr_ms') return; var out = [to.pubkeys[0], 'CHECKSIG']; for (var i = 1; i < to.pubkeys.length; i++) out.push(to.pubkeys[i], 'CHECKSIGADD'); out.push("OP_".concat(to.m), 'NUMEQUAL'); return out; }, }; function p2tr_ms(m, pubkeys, allowSamePubkeys) { if (allowSamePubkeys === void 0) { allowSamePubkeys = false; } if (!allowSamePubkeys) uniqPubkey(pubkeys); return { type: 'tr_ms', script: taproot.OutScript.encode({ type: 'tr_ms', pubkeys: pubkeys, m: m }), }; } taproot.p2tr_ms = p2tr_ms; var OutUnknown = { encode: function (from) { return { type: 'unknown', script: taproot.Script.encode(from) }; }, decode: function (to) { return to.type === 'unknown' ? taproot.Script.decode(to.script) : undefined; }, }; // /Payments var OutScripts = [ OutPK, OutPKH, OutSH, OutWSH, OutWPKH, OutMS, OutTR, OutTRNS, OutTRMS, OutUnknown, ]; // TODO: we can support user supplied output scripts now // - addOutScript // - removeOutScript // - We can do that as log we modify array in-place var _OutScript = P.apply(taproot.Script, P.coders.match(OutScripts)); // We can validate this once, because of packed & coders taproot.OutScript = P.validate(_OutScript, function (i) { if (i.type === 'pk' && !isValidPubkey(i.pubkey, PubT.ecdsa)) throw new Error('OutScript/pk: wrong key'); if ((i.type === 'pkh' || i.type === 'sh' || i.type === 'wpkh') && (!isBytes(i.hash) || i.hash.length !== 20)) throw new Error("OutScript/".concat(i.type, ": wrong hash")); if (i.type === 'wsh' && (!isBytes(i.hash) || i.hash.length !== 32)) throw new Error("OutScript/wsh: wrong hash"); if (i.type === 'tr' && (!isBytes(i.pubkey) || !isValidPubkey(i.pubkey, PubT.schnorr))) throw new Error('OutScript/tr: wrong taproot public key'); if (i.type === 'ms' || i.type === 'tr_ns' || i.type === 'tr_ms') if (!Array.isArray(i.pubkeys)) throw new Error('OutScript/multisig: wrong pubkeys array'); if (i.type === 'ms') { var n = i.pubkeys.length; for (var _i = 0, _a = i.pubkeys; _i < _a.length; _i++) { var p = _a[_i]; if (!isValidPubkey(p, PubT.ecdsa)) throw new Error('OutScript/multisig: wrong pubkey'); } if (i.m <= 0 || n > 16 || i.m > n) throw new Error('OutScript/multisig: invalid params'); } if (i.type === 'tr_ns' || i.type === 'tr_ms') { for (var _b = 0, _c = i.pubkeys; _b < _c.length; _b++) { var p = _c[_b]; if (!isValidPubkey(p, PubT.schnorr)) throw new Error("OutScript/".concat(i.type, ": wrong pubkey")); } } if (i.type === 'tr_ms') { var n = i.pubkeys.length; if (i.m <= 0 || n > 16 || i.m > n) throw new Error('OutScript/tr_ms: invalid params'); } return i; }); // Address // TODO: clean-up function validateWitness(version, data) { if (data.length < 2 || data.length > 40) throw new Error('Witness: invalid length'); if (version > 16) throw new Error('Witness: invalid version'); if (version === 0 && !(data.length === 20 || data.length === 32)) throw new Error('Witness: invalid length for version'); } taproot.validateWitness = validateWitness; function programToWitness(version, data, network) { if (network === void 0) { network = taproot.NETWORK; } validateWitness(version, data); var coder = version === 0 ? base.bech32 : base.bech32m; return coder.encode(network.bech32, [version].concat(coder.toWords(data))); } taproot.programToWitness = programToWitness; // TODO: remove? function parseWitnessProgram(version, data) { validateWitness(version, data); var encodedVersion = version > 0 ? version + 0x50 : version; return concat(new Uint8Array([encodedVersion]), taproot.VarBytes.encode(Uint8Array.from(data))); } taproot.parseWitnessProgram = parseWitnessProgram; function formatKey(hashed, prefix) { return taproot.base58check.encode(concat(Uint8Array.from(prefix), hashed)); } function WIF(network) { if (network === void 0) { network = taproot.NETWORK; } return { encode: function (privKey) { var compressed = concat(privKey, new Uint8Array([0x01])); return formatKey(compressed.subarray(0, 33), [network.wif]); }, decode: function (wif) { var parsed = taproot.base58check.decode(wif); if (parsed[0] !== network.wif) throw new Error('Wrong WIF prefix'); parsed = parsed.subarray(1); // Check what it is. Compressed flag? if (parsed.length !== 33) throw new Error('Wrong WIF length'); if (parsed[32] !== 0x01) throw new Error('Wrong WIF postfix'); return parsed.subarray(0, -1); }, }; } taproot.WIF = WIF; // Returns OutType, which can be used to create outscript function Address(network) { if (network === void 0) { network = taproot.NETWORK; } return { encode: function (from) { var type = from.type; if (type === 'wpkh') return programToWitness(0, from.hash, network); else if (type === 'wsh') return programToWitness(0, from.hash, network); else if (type === 'tr') return programToWitness(1, from.pubkey, network); else if (type === 'pkh') return formatKey(from.hash, [network.pubKeyHash]); else if (type === 'sh') return formatKey(from.hash, [network.scriptHash]); return 1; }, decode: function (address) { if (address.length < 14 || address.length > 74) throw new Error('Invalid address length'); // Bech32 if (network.bech32 && address.toLowerCase().startsWith(network.bech32)) { var res = void 0; try { res = base.bech32.decode(address); if (res.words[0] !== 0) throw new Error("bech32: wrong version=".concat(res.words[0])); } catch (_) { // Starting from version 1 it is decoded as bech32m res = base.bech32m.decode(address); if (res.words[0] === 0) throw new Error("bech32m: wrong version=".concat(res.words[0])); } if (res.prefix !== network.bech32) throw new Error("wrong bech32 prefix=".concat(res.prefix)); var _a = res.words, version = _a[0], program = _a.slice(1); var data_1 = base.bech32.fromWords(program); validateWitness(version, data_1); if (version === 0 && data_1.length === 32) return { type: 'wsh', hash: data_1 }; else if (version === 0 && data_1.length === 20) return { type: 'wpkh', hash: data_1 }; else if (version === 1 && data_1.length === 32) return { type: 'tr', pubkey: data_1 }; else throw new Error('Unkown witness program'); } var data = base.base58.decode(address); if (data.length !== 25) throw new Error('Invalid base58 address'); // Pay To Public Key Hash if (data[0] === network.pubKeyHash) { var bytes = base.base58.decode(address); return { type: 'pkh', hash: bytes.slice(1, bytes.length - 4) }; } else if (data[0] === network.scriptHash) { var bytes = base.base58.decode(address); return { type: 'sh', hash: base.base58.decode(address).slice(1, bytes.length - 4), }; } throw new Error("Invalid address prefix=".concat(data[0])); }, }; } taproot.Address = Address; // /Address var SignatureHash; (function (SignatureHash) { SignatureHash[SignatureHash["DEFAULT"] = 0] = "DEFAULT"; SignatureHash[SignatureHash["ALL"] = 1] = "ALL"; SignatureHash[SignatureHash["NONE"] = 2] = "NONE"; SignatureHash[SignatureHash["SINGLE"] = 3] = "SINGLE"; SignatureHash[SignatureHash["ANYONECANPAY"] = 128] = "ANYONECANPAY"; SignatureHash[SignatureHash["ALL_SIGHASH_ANYONECANPAY"] = 129] = "ALL_SIGHASH_ANYONECANPAY"; SignatureHash[SignatureHash["NONE_SIGHASH_ANYONECANPAY"] = 130] = "NONE_SIGHASH_ANYONECANPAY"; SignatureHash[SignatureHash["SINGLE_SIGHASH_ANYONECANPAY"] = 131] = "SINGLE_SIGHASH_ANYONECANPAY"; })(SignatureHash = taproot.SignatureHash || (taproot.SignatureHash = {})); taproot.SigHashCoder = P.apply(P.U32LE, P.coders.tsEnum(SignatureHash)); function sum(arr) { return arr.map(function (n) { return BigInt(n); }).reduce(function (a, b) { return a + b; }); } // TODO: encoder maybe? function unpackSighash(hashType) { var masked = hashType & 31; return { isAny: !!(hashType & 128), isNone: masked === 2, isSingle: masked === 3, }; } var _sortPubkeys = function (pubkeys) { return Array.from(pubkeys).sort(cmp); }; taproot._sortPubkeys = _sortPubkeys; var def = { sequence: function (n) { return (n === undefined ? taproot.DEFAULT_SEQUENCE : n); }, lockTime: function (n) { return (n === undefined ? taproot.DEFAULT_LOCKTIME : n); }, }; taproot.TAP_LEAF_VERSION = 0xc0; var tapLeafHash = function (script, version) { if (version === void 0) { version = taproot.TAP_LEAF_VERSION; } return taggedHash('TapLeaf', new Uint8Array([version]), taproot.VarBytes.encode(script)); }; taproot.tapLeafHash = tapLeafHash; function getTaprootKeys(privKey, pubKey, internalKey, merkleRoot) { if (merkleRoot === void 0) { merkleRoot = P.EMPTY; } if (P.equalBytes(internalKey, pubKey)) { privKey = taprootTweakPrivKey(privKey, merkleRoot); pubKey = secp.schnorr.getPublicKey(privKey); } return { privKey: privKey, pubKey: pubKey }; } taproot.getTaprootKeys = getTaprootKeys; var Transaction = /** @class */ (function () { // function Transaction(version, lockTime, PSBTVersion,opts) { //Changed the position of opts so that it can test examples work function Transaction(opts, version, lockTime, PSBTVersion) { if (version === void 0) { version = taproot.DEFAULT_VERSION; } if (lockTime === void 0) { lockTime = 0; } if (PSBTVersion === void 0) { PSBTVersion = 0; } if (opts === void 0) { opts = {}; } this.PSBTVersion = PSBTVersion; this.opts = opts; this.global = {}; this.inputs = []; this.outputs = []; if (lockTime !== taproot.DEFAULT_LOCKTIME) this.global.fallbackLocktime = lockTime; this.global.txVersion = version; } // Import Transaction.fromRaw = function (raw, opts) { if (opts === void 0) { opts = {}; } var parsed = taproot.RawTx.decode(raw); var tx = new Transaction(parsed.version, parsed.lockTime, undefined, opts); for (var _i = 0, _a = parsed.outputs; _i < _a.length; _i++) { var o = _a[_i]; tx.addOutput(o); } tx.outputs = parsed.outputs; tx.inputs = parsed.inputs; if (parsed.witnesses) { for (var i = 0; i < parsed.witnesses.length; i++) tx.inputs[i].finalScriptWitness = parsed.witnesses[i]; } return tx; }; // PSBT Transaction.fromPSBT = function (psbt, opts) { if (opts === void 0) { opts = {}; } var parsed; try { parsed = taproot.RawPSBTV0.decode(psbt); } catch (e0) { try { parsed = taproot.RawPSBTV2.decode(psbt); } catch (e2) { // Throw error for v0 parsing, since it popular, otherwise it would be shadowed by v2 error throw e0; } } var version = parsed.global.version || 0; var unsigned = parsed.global.unsignedTx; var txVersion = !version ? unsigned === null || unsigned === void 0 ? void 0 : unsigned.version : parsed.global.txVersion; var lockTime = !version ? unsigned === null || unsigned === void 0 ? void 0 : unsigned.lockTime : parsed.global.fallbackLocktime; var tx = new Transaction(txVersion, lockTime, version, opts); // We need slice here, because otherwise var inputCount = !version ? unsigned === null || unsigned === void 0 ? void 0 : unsigned.inputs.length : parsed.global.inputCount; tx.inputs = parsed.inputs.slice(0, inputCount).map(function (i, j) { var _a; return (__assign(__assign({ finalScriptSig: P.EMPTY }, (_a = parsed.global.unsignedTx) === null || _a === void 0 ? void 0 : _a.inputs[j]), i)); }); var outputCount = !version ? unsigned === null || unsigned === void 0 ? void 0 : unsigned.outputs.length : parsed.global.outputCount; tx.outputs = parsed.outputs.slice(0, outputCount).map(function (i, j) { var _a; return (__assign(__assign({}, i), (_a = parsed.global.unsignedTx) === null || _a === void 0 ? void 0 : _a.outputs[j])); }); tx.global = __assign(__assign({}, parsed.global), { txVersion: txVersion }); // just in case propietary/unknown fields if (lockTime !== taproot.DEFAULT_LOCKTIME) tx.global.fallbackLocktime = lockTime; return tx; }; Transaction.prototype.toPSBT = function (ver) { if (ver === void 0) { ver = this.PSBTVersion; } var inputs = this.inputs.map(function (i) { return cleanPSBTFields(ver, PSBTInput, i); }); for (var _i = 0, inputs_1 = inputs; _i < inputs_1.length; _i++) { var inp = inputs_1[_i]; // Don't serialize empty fields if (inp.partialSig && !inp.partialSig.length) delete inp.partialSig; if (inp.finalScriptSig && !inp.finalScriptSig.length) delete inp.finalScriptSig; if (inp.finalScriptWitness && !inp.finalScriptWitness.length) delete inp.finalScriptWitness; } var outputs = this.outputs.map(function (i) { return cleanPSBTFields(ver, PSBTOutput, i); }); if (ver && ver !== 2) throw new Error("Wrong PSBT version=".concat(ver)); var global = __assign({}, this.global); if (!ver) { global.unsignedTx = taproot.RawTx.decode(this.unsignedTx); delete global.fallbackLocktime; delete global.txVersion; } else { global.version = ver; global.txVersion = this.version; global.inputCount = this.inputs.length; global.outputCount = this.outputs.length; if (global.fallbackLocktime && global.fallbackLocktime === taproot.DEFAULT_LOCKTIME) delete global.fallbackLocktime; } if (this.opts.bip174jsCompat) { if (!inputs.length) inputs.push({}); if (!outputs.length) outputs.push({}); } return (ver === 2 ? taproot.RawPSBTV2 : taproot.RawPSBTV0).encode({ global: global, inputs: inputs, outputs: outputs, }); }; Object.defineProperty(Transaction.prototype, "lockTime", { // BIP370 lockTime (https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#determining-lock-time) get: function () { var height = taproot.DEFAULT_LOCKTIME; var heightCnt = 0; var time = taproot.DEFAULT_LOCKTIME; var timeCnt = 0; for (var _i = 0, _a = this.inputs; _i < _a.length; _i++) { var i = _a[_i]; if (i.requiredHeightLocktime) { height = Math.max(height, i.requiredHeightLocktime); heightCnt++; } if (i.requiredTimeLocktime) { time = Math.max(time, i.requiredTimeLocktime); timeCnt++; } } if (heightCnt && heightCnt >= timeCnt) return height; if (time !== taproot.DEFAULT_LOCKTIME) return time; return this.global.fallbackLocktime || taproot.DEFAULT_LOCKTIME; }, enumerable: false, configurable: true }); Object.defineProperty(Transaction.prototype, "version", { get: function () { // Should be not possible if (this.global.txVersion === undefined) throw new Error('No global.txVersion'); return this.global.txVersion; }, enumerable: false, configurable: true }); Transaction.prototype.inputStatus = function (idx) { this.checkInputIdx(idx); var input = this.inputs[idx]; // Finalized if (input.finalScriptSig && input.finalScriptSig.length) return 'finalized'; if (input.finalScriptWitness && input.finalScriptWitness.length) return 'finalized'; // Signed taproot if (input.tapKeySig) return 'signed'; if (input.tapScriptSig && input.tapScriptSig.length) return 'signed'; // Signed if (input.partialSig && input.partialSig.length) return 'signed'; return 'unsigned'; }; // TODO: re-use in preimages Transaction.prototype.inputSighash = function (idx) { this.checkInputIdx(idx); var sighash = this.inputType(this.inputs[idx]).sighash; // ALL or DEFAULT -- everything signed // NONE -- all inputs + no outputs // SINGLE -- all inputs + output with same index // ALL + ANYONE -- specific input + all outputs // NONE + ANYONE -- specific input + no outputs // SINGLE -- specific inputs + output with same index var sigOutputs = sighash === SignatureHash.DEFAULT ? SignatureHash.ALL : sighash & 3; var sigInputs = sighash & SignatureHash.ANYONECANPAY; return { sigInputs: sigInputs, sigOutputs: sigOutputs }; }; Transaction.prototype.signStatus = function () { // if addInput or addOutput is not possible, then all inputs or outputs are signed var addInput = true, addOutput = true; var inputs = [], outputs = []; for (var idx = 0; idx < this.inputs.length; idx++) { var status = this.inputStatus(idx); // Unsigned input doesn't affect anything if (status === 'unsigned') continue; var _a = this.inputSighash(idx), sigInputs = _a.sigInputs, sigOutputs = _a.sigOutputs; // Input type if (sigInputs === SignatureHash.ANYONECANPAY) inputs.push(idx); else addInput = false; // Output type if (sigOutputs === SignatureHash.ALL) addOutput = false; else if (sigOutputs === SignatureHash.SINGLE) outputs.push(idx); else if (sigOutputs === SignatureHash.NONE) { // Doesn't affect any outputs at all } else throw new Error("Wrong signature hash output type: ".concat(sigOutputs)); } return { addInput: addInput, addOutput: addOutput, inputs: inputs, outputs: outputs }; }; Object.defineProperty(Transaction.prototype, "isFinal", { get: function () { for (var idx = 0; idx < this.inputs.length; idx++) if (this.inputStatus(idx) !== 'finalized') return false; return true; }, enumerable: false, configurable: true }); Object.defineProperty(Transaction.prototype, "hasWitnesses", { // Info utils get: function () { var out = false; for (var _i = 0, _a = this.inputs; _i < _a.length; _i++) { var i = _a[_i]; if (i.finalScriptWitness && i.finalScriptWitness.length) out = true; } return out; }, enumerable: false, configurable: true }); Object.defineProperty(Transaction.prototype, "weight", { // https://en.bitcoin.it/wiki/Weight_units get: function () { if (!this.isFinal) throw new Error('Transaction is not finalized'); // TODO: Can we find out how much witnesses/script will be used before signing? var out = 32; if (this.hasWitnesses) out += 2; out += 4 * CompactSizeLen.encode(this.inputs.length).length; out += 4 * CompactSizeLen.encode(this.outputs.length).length; for (var _i = 0, _a = this.inputs; _i < _a.length; _i++) { var i = _a[_i]; out += 160 + 4 * taproot.VarBytes.encode(i.finalScriptSig).length; } for (var _b = 0, _c = this.outputs; _b < _c.length; _b++) { var o = _c[_b]; out += 32 + 4 * taproot.VarBytes.encode(o.script).length; } if (this.hasWitnesses) { for (var _d = 0, _e = this.inputs; _d < _e.length; _d++) { var i = _e[_d]; if (i.finalScriptWitness) out += taproot.RawWitness.encode(i.finalScriptWitness).length; } } return out; }, enumerable: false, configurable: true }); Object.defineProperty(Transaction.prototype, "vsize", { get: function () { return Math.ceil(this.weight / 4); }, enumerable: false, configurable: true }); Transaction.prototype.toBytes = function (withScriptSig, withWitness) { if (withScriptSig === void 0) { withScriptSig = false; } if (withWitness === void 0) { withWitness = false; } return taproot.RawTx.encode({ version: this.version, lockTime: this.lockTime, inputs: this.inputs.map(function (i) { return (__assign(__assign({}, i), { finalScriptSig: (withScriptSig && i.finalScriptSig) || P.EMPTY })); }), outputs: this.outputs, witnesses: this.inputs.map(function (i) { return i.finalScriptWitness || []; }), segwitFlag: withWitness && this.hasWitnesses, }); }; Object.defineProperty(Transaction.prototype, "unsignedTx", { get: function () { return this.toBytes(false, false); }, enumerable: false, configurable: true }); Object.defineProperty(Transaction.prototype, "hex", { get: function () { return base.hex.encode(this.toBytes(true, this.hasWitnesses)); }, enumerable: false, configurable: true }); Object.defineProperty(Transaction.prototype, "hash", { // TODO: hash requires non-empty script in inputs, why? get: function () { if (!this.isFinal) throw new Error('Transaction is not finalized'); return base.hex.encode(sha256x2(this.toBytes(true))); }, enumerable: false, configurable: true }); Object.defineProperty(Transaction.prototype, "id", { get: function () { if (!this.isFinal) throw new Error('Transaction is not finalized'); return base.hex.encode(sha256x2(this.toBytes(true)).reverse()); }, enumerable: false, configurable: true }); // Input stuff Transaction.prototype.checkInputIdx = function (idx) { if (!Number.isSafeInteger(idx) || 0 > idx || idx >= this.inputs.length) throw new Error("Wrong input index=".concat(idx)); }; // Modification Transaction.prototype.normalizeInput = function (i, cur, allowedFields) { var res = __assign(__assign({}, cur), i); if (res.sequence === undefined) res.sequence = taproot.DEFAULT_SEQUENCE; if (res.hash === undefined && typeof res.txid === 'string') res.hash = base.hex.decode(res.txid); if (res.hash === undefined && typeof res.txid === 'object') res.hash = res.txid; if (typeof res.hash === 'string') res.hash = base.hex.decode(res.hash).reverse(); if (res.tapMerkleRoot === null) delete res.tapMerkleRoot; if (res.hash === undefined || res.index === undefined) throw new Error('Transaction/input: hash and index required'); res = mergeKeyMap(PSBTInput, res, cur, allowedFields); PSBTInputCoder.encode(res); // Cannot move in PSBTInputCoder, since it requires opts for parsing if (res.nonWitnessUtxo) { var outputs = res.nonWitnessUtxo.outputs; if (outputs.length - 1 < res.index) throw new Error('nonWitnessUtxo: incorect output index'); var tx = Transaction.fromRaw(taproot.RawTx.encode(res.nonWitnessUtxo), this.opts); var hash = base.hex.encode(res.hash); if (tx.id !== hash) //ROHIT UNDONE var txid = base.hex.encode(res.txid); //ROHIT UNDONE if (tx.id !== txid) throw new Error("nonWitnessUtxo: wrong hash, exp=".concat(txid, " got=").concat(tx.id)); } // TODO: use this.prevout? var prevOut; if (res.nonWitnessUtxo && i.index !== undefined) prevOut = res.nonWitnessUtxo.outputs[res.index]; else if (res.witnessUtxo) prevOut = res.witnessUtxo; if (!this.opts.disableScriptCheck) checkScript(prevOut && prevOut.script, res.redeemScript, res.witnessScript); return res; }; Transaction.prototype.addInput = function (input) { if (!this.signStatus().addInput) throw new Error('Tx has signed inputs, cannot add new one'); this.inputs.push(this.normalizeInput(input)); //ROHIT ATTEMPT UNDONE this.inputs.push(this.normalizeInput(this.inputs,input)); return this.inputs.length - 1; }; Transaction.prototype.updateInput = function (idx, input) { this.checkInputIdx(idx); var allowedFields = undefined; var status = this.signStatus(); if (!status.addInput || status.inputs.includes(idx)) allowedFields = PSBTInputUnsignedKeys; this.inputs[idx] = this.normalizeInput(input, this.inputs[idx], allowedFields); }; // Output stuff Transaction.prototype.checkOutputIdx = function (idx) { if (!Number.isSafeInteger(idx) || 0 > idx || idx >= this.outputs.length) throw new Error("Wrong output index=".concat(idx)); }; Transaction.prototype.normalizeOutput = function (o, cur, allowedFields) { var res = __assign(__assign({}, cur), o); if (res.amount !== undefined) res.amount = typeof res.amount === 'string' ? taproot.decimal.decode(res.amount) : res.amount; res = mergeKeyMap(PSBTOutput, res, cur, allowedFields); PSBTOutputCoder.encode(res); if (res.script === undefined || res.amount === undefined) throw new Error('Transaction/output: script and amount required'); if (!this.opts.allowUnknowOutput && taproot.OutScript.decode(res.script).type === 'unknown') { throw new Error('Transaction/output: unknown output script type, there is a chance that input is unspendable. Pass allowUnkownScript=true, if you sure'); } if (!this.opts.disableScriptCheck) checkScript(res.script, res.redeemScript, res.witnessScript); return res; }; Transaction.prototype.addOutput = function (o) { if (!this.signStatus().addOutput) throw new Error('Tx has signed outputs, cannot add new one'); this.outputs.push(this.normalizeOutput(o)); return this.outputs.length - 1; }; Transaction.prototype.updateOutput = function (idx, output) { this.checkOutputIdx(idx); var allowedFields = undefined; var status = this.signStatus(); if (!status.addOutput || status.outputs.includes(idx)) allowedFields = PSBTOutputUnsignedKeys; this.outputs[idx] = this.normalizeOutput(output, this.outputs[idx], allowedFields); }; Transaction.prototype.addOutputAddress = function (address, amount, network) { if (network === void 0) { network = taproot.NETWORK; } return this.addOutput({ script: taproot.OutScript.encode(Address(network).decode(address)), amount: typeof amount === 'string' ? taproot.decimal.decode(amount) : amount, }); }; Object.defineProperty(Transaction.prototype, "fee", { // Utils get: function () { var res = 0n; for (var _i = 0, _a = this.inputs; _i < _a.length; _i++) { var i = _a[_i]; var prevOut = this.prevOut(i); if (!prevOut) throw new Error('Empty input amount'); res += prevOut.amount; } for (var _b = 0, _c = this.outputs; _b < _c.length; _b++) { var i = _c[_b]; res -= i.amount; } return res; }, enumerable: false, configurable: true }); // Signing // Based on https://github.com/bitcoin/bitcoin/blob/5871b5b5ab57a0caf9b7514eb162c491c83281d5/test/functional/test_framework/script.py#L624 // There is optimization opportunity to re-use hashes for multiple inputs for witness v0/v1, // but we are trying to be less complicated for audit purpose for now. Transaction.prototype.preimageLegacy = function (idx, prevOutScript, hashType) { var _a = unpackSighash(hashType), isAny = _a.isAny, isNone = _a.isNone, isSingle = _a.isSingle; if (idx < 0 || !Number.isSafeInteger(idx)) throw new Error("Invalid input idx=".concat(idx)); if ((isSingle && idx >= this.outputs.length) || idx >= this.inputs.length) return P.U256BE.encode(1n); prevOutScript = taproot.Script.encode(taproot.Script.decode(prevOutScript).filter(function (i) { return i !== 'CODESEPARATOR'; })); var inputs = this.inputs.map(function (input, inputIdx) { return (__assign(__assign({}, input), { finalScriptSig: inputIdx === idx ? prevOutScript : P.EMPTY })); }); if (isAny) inputs = [inputs[idx]]; else if (isNone || isSingle) { inputs = inputs.map(function (input, inputIdx) { return (__assign(__assign({}, input), { sequence: inputIdx === idx ? def.sequence(input.sequence) : 0 })); }); } var outputs = this.outputs; if (isNone) outputs = []; else if (isSingle) { outputs = this.outputs.slice(0, idx).fill(EMPTY_OUTPUT).concat([outputs[idx]]); } var tmpTx = taproot.RawTx.encode({ lockTime: this.lockTime, version: this.version, segwitFlag: false, inputs: inputs, outputs: outputs, }); return sha256x2(tmpTx, P.I32LE.encode(hashType)); }; Transaction.prototype.preimageWitnessV0 = function (idx, prevOutScript, hashType, amount) { var _a = unpackSighash(hashType), isAny = _a.isAny, isNone = _a.isNone, isSingle = _a.isSingle; var inputHash = EMPTY32; var sequenceHash = EMPTY32; var outputHash = EMPTY32; var inputs = this.inputs; if (!isAny) inputHash = sha256x2.apply(void 0, inputs.map(TxHashIdx.encode)); if (!isAny && !isSingle && !isNone) sequenceHash = sha256x2.apply(void 0, inputs.map(function (i) { return P.U32LE.encode(def.sequence(i.sequence)); })); if (!isSingle && !isNone) { outputHash = sha256x2.apply(void 0, this.outputs.map(taproot.RawOutput.encode)); } else if (isSingle && idx < this.outputs.length) outputHash = sha256x2(taproot.RawOutput.encode(this.outputs[idx])); var input = inputs[idx]; return sha256x2(P.I32LE.encode(this.version), inputHash, sequenceHash, P.bytes(32, true).encode(input.hash), P.U32LE.encode(input.index), taproot.VarBytes.encode(prevOutScript), P.U64LE.encode(amount), P.U32LE.encode(def.sequence(input.sequence)), outputHash, P.U32LE.encode(this.lockTime), P.U32LE.encode(hashType)); }; Transaction.prototype.preimageWitnessV1 = function (idx, prevOutScript, hashType, amount, codeSeparator, leafScript, leafVer, annex) { if (codeSeparator === void 0) { codeSeparator = -1; } if (leafVer === void 0) { leafVer = 0xc0; } if (!Array.isArray(amount) || this.inputs.length !== amount.length) throw new Error("Invalid amounts array=".concat(amount)); if (!Array.isArray(prevOutScript) || this.inputs.length !== prevOutScript.length) throw new Error("Invalid prevOutScript array=".concat(prevOutScript)); var out = [ P.U8.encode(0), P.U8.encode(hashType), P.I32LE.encode(this.version), P.U32LE.encode(this.lockTime), ]; var outType = hashType === SignatureHash.DEFAULT ? SignatureHash.ALL : hashType & 3; var inType = hashType & SignatureHash.ANYONECANPAY; if (inType !== SignatureHash.ANYONECANPAY) { out.push.apply(out, [ this.inputs.map(TxHashIdx.encode), amount.map(P.U64LE.encode), prevOutScript.map(taproot.VarBytes.encode), this.inputs.map(function (i) { return P.U32LE.encode(def.sequence(i.sequence)); }), ].map(function (i) { return (0, sha256_1.sha256)(concat.apply(void 0, i)); })); } if (outType === SignatureHash.ALL) { out.push((0, sha256_1.sha256)(concat.apply(void 0, this.outputs.map(taproot.RawOutput.encode)))); } var spendType = (annex ? 1 : 0) | (leafScript ? 2 : 0); out.push(new Uint8Array([spendType])); if (inType === SignatureHash.ANYONECANPAY) { var inp = this.inputs[idx]; out.push(TxHashIdx.encode(inp), P.U64LE.encode(amount[idx]), taproot.VarBytes.encode(prevOutScript[idx]), P.U32LE.encode(def.sequence(inp.sequence))); } else out.push(P.U32LE.encode(idx)); if (spendType & 1) out.push((0, sha256_1.sha256)(taproot.VarBytes.encode(annex))); if (outType === SignatureHash.SINGLE) out.push(idx < this.outputs.length ? (0, sha256_1.sha256)(taproot.RawOutput.encode(this.outputs[idx])) : EMPTY32); if (leafScript) out.push((0, taproot.tapLeafHash)(leafScript, leafVer), P.U8.encode(0), P.I32LE.encode(codeSeparator)); return taggedHash.apply(void 0, __spreadArray(['TapSighash'], out, false)); }; // Utils for sign/finalize // Used pretty often, should be fast Transaction.prototype.prevOut = function (input) { if (input.nonWitnessUtxo) return input.nonWitnessUtxo.outputs[input.index]; else if (input.witnessUtxo) return input.witnessUtxo; else throw new Error('Cannot find previous output info.'); }; Transaction.prototype.inputType = function (input) { // TODO: check here if non-segwit tx + no nonWitnessUtxo var txType = 'legacy'; var defaultSighash = SignatureHash.ALL; var prevOut = this.prevOut(input); var first = taproot.OutScript.decode(prevOut.script); var type = first.type; var cur = first; var stack = [first]; if (first.type === 'tr') { defaultSighash = SignatureHash.DEFAULT; return { txType: 'taproot', type: 'tr', last: first, lastScript: prevOut.script, defaultSighash: defaultSighash, sighash: input.sighashType || defaultSighash, }; } else { if (first.type === 'wpkh' || first.type === 'wsh') txType = 'segwit'; if (first.type === 'sh') { if (!input.redeemScript) throw new Error('inputType: sh without redeemScript'); var child = taproot.OutScript.decode(input.redeemScript); if (child.type === 'wpkh' || child.type === 'wsh') txType = 'segwit'; stack.push(child); cur = child; type += "-".concat(child.type); } // wsh can be inside sh if (cur.type === 'wsh') { if (!input.witnessScript) throw new Error('inputType: wsh without witnessScript'); var child = taproot.OutScript.decode(input.witnessScript); if (child.type === 'wsh') txType = 'segwit'; stack.push(child); cur = child; type += "-".concat(child.type); } // TODO: check for uncompressed public keys in segwit tx var last = stack[stack.length - 1]; if (last.type === 'sh' || last.type === 'wsh') throw new Error('inputType: sh/wsh cannot be terminal type'); var lastScript = taproot.OutScript.encode(last); var res = { type: type, txType: txType, last: last, lastScript: lastScript, defaultSighash: defaultSighash, sighash: input.sighashType || defaultSighash, }; return res; } }; // TODO: signer can be privateKey OR instance of bip32 HD stuff Transaction.prototype.signIdx = function (privateKey, idx, allowedSighash, _auxRand) { this.checkInputIdx(idx); var input = this.inputs[idx]; var inputType = this.inputType(input); // Handle BIP32 HDKey if (!(privateKey instanceof Uint8Array)) { if (!input.bip32Derivation || !input.bip32Derivation.length) throw new Error('bip32Derivation: empty'); var signers = input.bip32Derivation .filter(function (i) { return i[1].fingerprint == privateKey.fingerprint; }) .map(function (_a) { var pubKey = _a[0], path = _a[1].path; var s = privateKey; for (var _i = 0, path_1 = path; _i < path_1.length; _i++) { var i = path_1[_i]; s = s.deriveChild(i); } if (!P.equalBytes(s.publicKey, pubKey)) throw new Error('bip32Derivation: wrong pubKey'); if (!s.privateKey) throw new Error('bip32Derivation: no privateKey'); return s; }); if (!signers.length) throw new Error("bip32Derivation: no items with fingerprint=".concat(privateKey.fingerprint)); var signed = false; for (var _i = 0, signers_1 = signers; _i < signers_1.length; _i++) { var s = signers_1[_i]; if (this.signIdx(s.privateKey, idx)) signed = true; } return signed; } // Sighash checks // Just for compat with bitcoinjs-lib, so users won't face unexpected behaviour. if (!allowedSighash) allowedSighash = [inputType.defaultSighash]; var sighash = inputType.sighash; if (!allowedSighash.includes(sighash)) { throw new Error("Input with not allowed sigHash=".concat(sighash, ". Allowed: ").concat(allowedSighash.join(', '))); } // NOTE: it is possible to sign these inputs for legacy/segwit v0 (but no taproot!), // however this was because of bug in bitcoin-core, which remains here because of consensus. // If this is absolutely neccessary for you workflow, please open issue. For now it is disable to // avoid complicated workflow where SINGLE will block adding new outputs var _a = this.inputSighash(idx), sigInputs = _a.sigInputs, sigOutputs = _a.sigOutputs; if (sigOutputs === SignatureHash.SINGLE && idx >= this.outputs.length) { throw new Error("Input with sighash SINGLE, but there is no output with corresponding index=".concat(idx)); } // Actual signing // Taproot var prevOut = this.prevOut(input); if (inputType.txType === 'taproot') { if (input.tapBip32Derivation) throw new Error('tapBip32Derivation unsupported'); var prevOuts = this.inputs.map(this.prevOut); var prevOutScript = prevOuts.map(function (i) { return i.script; }); var amount = prevOuts.map(function (i) { return i.amount; }); var signed = false; var schnorrPub = secp.schnorr.getPublicKey(privateKey); var merkleRoot = input.tapMerkleRoot || P.EMPTY; if (input.tapInternalKey) { // internal + tweak = tweaked key // if internal key == current public key, we need to tweak private key, // otherwise sign as is. bitcoinjs implementation always wants tweaked // priv key to be provided var _b = getTaprootKeys(privateKey, schnorrPub, input.tapInternalKey, merkleRoot), pubKey = _b.pubKey, privKey = _b.privKey; var _c = taprootTweakPubkey(input.tapInternalKey, merkleRoot), taprootPubKey = _c[0], parity = _c[1]; if (P.equalBytes(taprootPubKey, pubKey)) { var hash = this.preimageWitnessV1(idx, prevOutScript, sighash, amount); var sig = concat(secp.schnorr.signSync(hash, privKey, _auxRand), sighash !== SignatureHash.DEFAULT ? new Uint8Array([sighash]) : P.EMPTY); this.updateInput(idx, { tapKeySig: sig }); signed = true; } } if (input.tapLeafScript) { input.tapScriptSig = input.tapScriptSig || []; var _loop_3 = function (cb, _script) { var script = _script.subarray(0, -1); var scriptDecoded = taproot.Script.decode(script); var ver = _script[_script.length - 1]; var hash = (0, taproot.tapLeafHash)(script, ver); var _j = getTaprootKeys(privateKey, schnorrPub, cb.internalKey, P.EMPTY // Because we cannot have nested taproot tree ), pubKey = _j.pubKey, privKey = _j.privKey; var pos = scriptDecoded.findIndex(function (i) { return i instanceof Uint8Array && P.equalBytes(i, pubKey); }); // Skip if there is no public key in tapLeafScript if (pos === -1) return "continue"; var msg = this_1.preimageWitnessV1(idx, prevOutScript, sighash, amount, undefined, script, ver); var sig = concat(secp.schnorr.signSync(msg, privKey, _auxRand), sighash !== SignatureHash.DEFAULT ? new Uint8Array([sighash]) : P.EMPTY); this_1.updateInput(idx, { tapScriptSig: [[{ pubKey: pubKey, leafHash: hash }, sig]] }); signed = true; }; var this_1 = this; for (var _d = 0, _e = input.tapLeafScript; _d < _e.length; _d++) { var _f = _e[_d], cb = _f[0], _script = _f[1]; _loop_3(cb, _script); } } if (!signed) throw new Error('No taproot scripts signed'); return true; } else { // only compressed keys are supported for now var pubKey = secp.getPublicKey(privateKey, true); // TODO: replace with explicit checks // Check if script has public key or its has inside var hasPubkey = false; var pubKeyHash = hash160(pubKey); for (var _g = 0, _h = taproot.Script.decode(inputType.lastScript); _g < _h.length; _g++) { var i = _h[_g]; if (i instanceof Uint8Array && (P.equalBytes(i, pubKey) || P.equalBytes(i, pubKeyHash))) hasPubkey = true; } if (!hasPubkey) throw new Error("Input script doesn't have pubKey: ".concat(inputType.lastScript)); var hash = void 0; if (inputType.txType === 'legacy') { if (!this.opts.allowLegacyWitnessUtxo && !input.nonWitnessUtxo) { throw new Error("Transaction/sign: legacy input without nonWitnessUtxo, can result in attack that forces paying higher fees. Pass allowLegacyWitnessUtxo=true, if you sure"); } hash = this.preimageLegacy(idx, inputType.lastScript, sighash); } else if (inputType.txType === 'segwit') { var script = inputType.lastScript; // If wpkh OR sh-wpkh, wsh-wpkh is impossible, so looks ok // TODO: re-check if (inputType.last.type === 'wpkh') script = taproot.OutScript.encode({ type: 'pkh', hash: inputType.last.hash }); hash = this.preimageWitnessV0(idx, script, sighash, prevOut.amount); } else throw new Error("Transaction/sign: unknown tx type: ".concat(inputType.txType)); var sig = signECDSA(hash, privateKey, this.opts.lowR); this.updateInput(idx, { partialSig: [[pubKey, concat(sig, new Uint8Array([sighash]))]], }); } return true; }; // TODO: this is bad API. Will work if user creates and signs tx, but if // there is some complex workflow with exchanging PSBT and signing them, // then it is better to validate which output user signs. How could a better API look like? // Example: user adds input, sends to another party, then signs received input (mixer etc), // another user can add different input for same key and user will sign it. // Even worse: another user can add bip32 derivation, and spend money from different address. Transaction.prototype.sign = function (privateKey, allowedSighash, _auxRand) { //debugger; //ROHIT var num = 0; for (var i = 0; i < this.inputs.length; i++) { try { if (this.signIdx(privateKey, i, allowedSighash, _auxRand)) num++; } catch (e) { } } if (!num) throw new Error('No inputs signed'); return num; }; Transaction.prototype.finalizeIdx = function (idx) { this.checkInputIdx(idx); if (this.fee < 0n) throw new Error('Outputs spends more than inputs amount'); var input = this.inputs[idx]; var inputType = this.inputType(input); // Taproot finalize if (inputType.txType === 'taproot') { if (input.tapKeySig) input.finalScriptWitness = [input.tapKeySig]; else if (input.tapLeafScript && input.tapScriptSig) { // TODO: this works the same as bitcoinjs lib fork, however it is not secure, // since we add signatures to script which we don't understand. // Maybe it is better to disable it? // Proper way will be to create check for known scripts, however MuSig, p2tr_ns and other // scripts are still not standard; and it will take significant amount of work for them. // Sort leafs by control block length. TODO: maybe need to check script length too? var leafs = input.tapLeafScript.sort(function (a, b) { return taproot.TaprootControlBlock.encode(a[0]).length - taproot.TaprootControlBlock.encode(b[0]).length; }); var _loop_4 = function (cb, _script) { // Last byte is version var script = _script.slice(0, -1); var ver = _script[_script.length - 1]; var outScript_1 = taproot.OutScript.decode(script); var hash = (0, taproot.tapLeafHash)(script, ver); var scriptSig = input.tapScriptSig.filter(function (i) { return P.equalBytes(i[0].leafHash, hash); }); var signatures = []; if (outScript_1.type === 'tr_ms') { var m = outScript_1.m; var pubkeys = outScript_1.pubkeys; var added = 0; var _loop_6 = function (pub) { var sigIdx = scriptSig.findIndex(function (i) { return P.equalBytes(i[0].pubKey, pub); }); // Should have exact amount of signatures (more -- will fail) if (added === m || sigIdx === -1) { signatures.push(P.EMPTY); return "continue"; } signatures.push(scriptSig[sigIdx][1]); added++; }; for (var _c = 0, pubkeys_3 = pubkeys; _c < pubkeys_3.length; _c++) { var pub = pubkeys_3[_c]; _loop_6(pub); } // Should be exact same as m if (added !== m) return "continue"; } else if (outScript_1.type === 'tr_ns') { var _loop_7 = function (pub) { var sigIdx = scriptSig.findIndex(function (i) { return P.equalBytes(i[0].pubKey, pub); }); if (sigIdx === -1) return "continue"; signatures.push(scriptSig[sigIdx][1]); }; for (var _d = 0, _e = outScript_1.pubkeys; _d < _e.length; _d++) { var pub = _e[_d]; _loop_7(pub); } if (signatures.length !== outScript_1.pubkeys.length) return "continue"; } else if (outScript_1.type === 'unknown' && this_2.opts.allowUnknowInput) { // Trying our best to sign what we can var scriptDecoded_1 = taproot.Script.decode(script); signatures = scriptSig .map(function (_a) { var pubKey = _a[0].pubKey, signature = _a[1]; var pos = scriptDecoded_1.findIndex(function (i) { return i instanceof Uint8Array && P.equalBytes(i, pubKey); }); if (pos === -1) throw new Error('finalize/taproot: cannot find position of pubkey in script'); return { signature: signature, pos: pos }; }) // Reverse order (because witness is stack and we take last element first from it) .sort(function (a, b) { return a.pos - b.pos; }) .map(function (i) { return i.signature; }); if (!signatures.length) return "continue"; } else throw new Error('Finalize: Unknown tapLeafScript'); // Witness is stack, so last element will be used first input.finalScriptWitness = signatures .reverse() .concat([script, taproot.TaprootControlBlock.encode(cb)]); return "break"; }; var this_2 = this; for (var _i = 0, leafs_1 = leafs; _i < leafs_1.length; _i++) { var _a = leafs_1[_i], cb = _a[0], _script = _a[1]; var state_1 = _loop_4(cb, _script); if (state_1 === "break") break; } if (!input.finalScriptWitness) throw new Error('finalize/taproot: empty witness'); } else throw new Error('finalize/taproot: unknown input'); // Clean input for (var k in input) if (!PSBTInputFinalKeys.includes(k)) delete input[k]; return; } var outScript = inputType.lastScript; var isSegwit = inputType.txType === 'segwit'; if (!input.partialSig || !input.partialSig.length) throw new Error('Not enough partial sign'); // TODO: this is completely broken, fix. var inputScript; var witness = []; // TODO: move input scripts closer to payments/output scripts // Multisig if (inputType.last.type === 'ms') { var m = inputType.last.m; var pubkeys = inputType.last.pubkeys; var signatures = []; var _loop_5 = function (pub) { var sign = input.partialSig.find(function (s) { return P.equalBytes(pub, s[0]); }); if (!sign) return "continue"; signatures.push(sign[1]); }; // partial: [pubkey, sign] for (var _b = 0, pubkeys_2 = pubkeys; _b < pubkeys_2.length; _b++) { var pub = pubkeys_2[_b]; _loop_5(pub); } signatures = signatures.slice(0, m); if (signatures.length !== m) { throw new Error("Multisig: wrong signatures count, m=".concat(m, " n=").concat(pubkeys.length, " signatures=").concat(signatures.length)); } inputScript = taproot.Script.encode(__spreadArray(['OP_0'], signatures, true)); } else if (inputType.last.type === 'pk') { inputScript = taproot.Script.encode([input.partialSig[0][1]]); } else if (inputType.last.type === 'pkh') { // check if output is correct here inputScript = taproot.Script.encode([input.partialSig[0][1], input.partialSig[0][0]]); } else if (inputType.last.type === 'wpkh') { // check if output is correct here inputScript = P.EMPTY; witness = [input.partialSig[0][1], input.partialSig[0][0]]; } else if (inputType.last.type === 'unknown' && !this.opts.allowUnknowInput) throw new Error('Unknown inputs not allowed'); var finalScriptSig, finalScriptWitness; if (input.witnessScript) { // P2WSH if (inputScript && inputScript.length > 0 && outScript && outScript.length > 0) { witness = taproot.Script.decode(inputScript).map(function (i) { if (i === 'OP_0') return P.EMPTY; if (i instanceof Uint8Array) return i; throw new Error("Wrong witness op=".concat(i)); }); } if (witness && outScript) witness = witness.concat(outScript); outScript = taproot.Script.encode(['OP_0', (0, sha256_1.sha256)(outScript)]); inputScript = P.EMPTY; } if (isSegwit) finalScriptWitness = witness; if (input.redeemScript) { // P2SH finalScriptSig = taproot.Script.encode(__spreadArray(__spreadArray([], taproot.Script.decode(inputScript), true), [outScript], false)); } else if (!isSegwit) finalScriptSig = inputScript; if (!finalScriptSig && !finalScriptWitness) throw new Error('Unknown error finalizing input'); if (finalScriptSig) input.finalScriptSig = finalScriptSig; if (finalScriptWitness) input.finalScriptWitness = finalScriptWitness; // Clean input for (var k in input) if (!PSBTInputFinalKeys.includes(k)) delete input[k]; }; Transaction.prototype.finalize = function () { for (var i = 0; i < this.inputs.length; i++) this.finalizeIdx(i); }; Transaction.prototype.extract = function () { if (!this.isFinal) throw new Error('Transaction has unfinalized inputs'); if (!this.outputs.length) throw new Error('Transaction has no outputs'); // TODO: Check if inputs.amount >= outputs.amount return this.toBytes(true, true); }; Transaction.prototype.combine = function (other) { for (var _i = 0, _a = ['PSBTVersion', 'version', 'lockTime']; _i < _a.length; _i++) { var k = _a[_i]; if (this[k] !== other[k]) throw new Error("Transaction/combine: different ".concat(k, " this=").concat(this[k], " other=").concat(other[k])); } for (var _b = 0, _c = ['inputs', 'outputs']; _b < _c.length; _b++) { var k = _c[_b]; if (this[k].length !== other[k].length) { throw new Error("Transaction/combine: different ".concat(k, " length this=").concat(this[k].length, " other=").concat(other[k].length)); } } var thisUnsigned = this.global.unsignedTx ? taproot.RawTx.encode(this.global.unsignedTx) : P.EMPTY; var otherUnsigned = other.global.unsignedTx ? taproot.RawTx.encode(other.global.unsignedTx) : P.EMPTY; if (!P.equalBytes(thisUnsigned, otherUnsigned)) throw new Error("Transaction/combine: different unsigned tx"); this.global = mergeKeyMap(PSBTGlobal, this.global, other.global); for (var i = 0; i < this.inputs.length; i++) this.updateInput(i, other.inputs[i]); for (var i = 0; i < this.outputs.length; i++) this.updateOutput(i, other.outputs[i]); return this; }; return Transaction; }()); taproot.Transaction = Transaction; // User facing API? // Simple pubkey address, without complex scripts function getAddress(type, privKey, network) { if (network === void 0) { network = taproot.NETWORK; } if (type === 'tr') { return p2tr(secp.schnorr.getPublicKey(privKey), undefined, network).address; } var pubKey = secp.getPublicKey(privKey, true); if (type === 'pkh') return (0, taproot.p2pkh)(pubKey, network).address; if (type === 'wpkh') return (0, taproot.p2wpkh)(pubKey, network).address; throw new Error("getAddress: unknown type=".concat(type)); } taproot.getAddress = getAddress; // TODO: rewrite function multisig(m, pubkeys, sorted, witness) { if (sorted === void 0) { sorted = false; } if (witness === void 0) { witness = false; } var ms = (0, taproot.p2ms)(m, sorted ? (0, taproot._sortPubkeys)(pubkeys) : pubkeys); return witness ? (0, taproot.p2wsh)(ms) : (0, taproot.p2sh)(ms); } taproot.multisig = multisig; function sortedMultisig(m, pubkeys, witness) { if (witness === void 0) { witness = false; } return multisig(m, pubkeys, true, witness); } taproot.sortedMultisig = sortedMultisig; // Copy-pase from bip32 derive, maybe do something like 'bip32.parsePath'? var HARDENED_OFFSET = 0x80000000; function bip32Path(path) { var out = []; if (!/^[mM]'?/.test(path)) throw new Error('Path must start with "m" or "M"'); if (/^[mM]'?$/.test(path)) return out; var parts = path.replace(/^[mM]'?\//, '').split('/'); for (var _i = 0, parts_1 = parts; _i < parts_1.length; _i++) { var c = parts_1[_i]; var m = /^(\d+)('?)$/.exec(c); if (!m || m.length !== 3) throw new Error("Invalid child index: ".concat(c)); var idx = +m[1]; if (!Number.isSafeInteger(idx) || idx >= HARDENED_OFFSET) throw new Error('Invalid index'); // hardened key if (m[2] === "'") idx += HARDENED_OFFSET; out.push(idx); } return out; } taproot.bip32Path = bip32Path; function PSBTCombine(psbts) { if (!psbts || !Array.isArray(psbts) || !psbts.length) throw new Error('PSBTCombine: wrong PSBT list'); var tx = Transaction.fromPSBT(psbts[0]); for (var i = 1; i < psbts.length; i++) tx.combine(Transaction.fromPSBT(psbts[i])); return tx.toPSBT(); } taproot.PSBTCombine = PSBTCombine; function numberToScriptArray(number) { array = coinjs.numToBytes(number); let lastIndex = array.length - 1; while (lastIndex >= 0 && array[lastIndex] === 0) { lastIndex--; } array.splice(lastIndex + 1); return array; } taproot.numberToScriptArray = numberToScriptArray; })(); //Inserting these two lines so that Paul Miller Github tests can work. var hex = base.hex; var btc = taproot; var Transaction = taproot.Transaction; var secp256k1_schnorr = secp.schnorr; var secp256k1 = secp; var schnorr = secp.schnorr; //ChatGPT replaced code for deeStrictEqual and should function deepStrictEqual(actual, expected) { if (typeof actual !== 'object' || typeof expected !== 'object') { const result = actual === expected; console.log(`deepStrictEqual: ${result ? 'Success' : 'Failure'} - Actual: ${actual}, Expected: ${expected}`); return result; } const actualKeys = Object.keys(actual); const expectedKeys = Object.keys(expected); if (actualKeys.length !== expectedKeys.length) { console.log(`deepStrictEqual: Failure - Different number of keys`); return false; } for (const key of actualKeys) { if (!deepStrictEqual(actual[key], expected[key])) { console.log(`deepStrictEqual: Failure - Property '${key}' mismatch`); return false; } } console.log(`deepStrictEqual: Success`); return true; } // Test suite function (replaced with should) function should(suiteDescription, tests) { console.log(`Running test suite: ${suiteDescription}`); tests(); } // Test runner function function test(testDescription, testFunction) { try { testFunction(); } catch (error) { logResult(false, `${testDescription} - ${error}`); } } // Log test result function logResult(result, message) { if (result) { console.log(`YESSS ${message}`); } else { console.error(`NO!!! ${message}`); } } function throws(func) { try { func(); console.log(`throws: Failure - No exception was thrown`); return false; } catch (error) { console.log(`throws: Success - Exception was thrown: ${error}`); return true; } } // End of ChatGPT test functions function demonstrateNestedArray(nestedArray) { console.log("Original nestedArray:"); console.log(nestedArray); console.log("\nAccessing elements:"); console.log("\nIterating through nestedArray:"); for (var i = 0; i < nestedArray.length; i++) { if (Array.isArray(nestedArray[i])) { console.log("Element at index", i, "is an array:", nestedArray[i]); for (var j = 0; j < nestedArray[i].length; j++) { console.log(" Element at index", j, ":", nestedArray[i][j]); } } else { console.log("Element at index", i, ":", nestedArray[i]); } } } //START OF BIP32 SECTION /*! scure-bip32 - MIT License (c) 2022 Patricio Palladino, Paul Miller (paulmillr.com) */ var bip32 = {}; (function () { var HDKey = /** @class */ (function () { var hmac = hashmini.hmac; var ripemd160 = hashmini.ripemd160; var sha256 = hashmini.sha256; var sha512 = hashmini.sha512; //var _assert_1 = require("@noble/hashes/_assert"); var utils_1 = hashmini.utils; var modular_1 = secp.utils; //var base_1 = base; var Point = secp.Point; var base58check = (0, base.base58check)(sha256); function _assert_1(condition, message = "Assertion failed") { if (!condition) { throw new Error(message); } } function _assert_1(value, length, message = "Value is not a Uint8Array or does not have the expected length") { if (!(value instanceof Uint8Array)) { throw new Error(message); } } const createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength); var __assign = (this && this.__assign) || function () { return Object.assign.apply(Object, arguments); }; function utf8ToBytes(str) { if (typeof str !== 'string') { throw new TypeError(`utf8ToBytes expected string, got ${typeof str}`); } return new TextEncoder().encode(str); } function bytesToNumber(bytes) { return BigInt("0x".concat((0, utils_1.bytesToHex)(bytes))); } function numberToBytes(num) { return (0, utils_1.hexToBytes)(num.toString(16).padStart(64, '0')); } var MASTER_SECRET = (0, utf8ToBytes)('Bitcoin seed'); // Bitcoin hardcoded by default var BITCOIN_VERSIONS = { private: 0x0488ade4, public: 0x0488b21e }; var HARDENED_OFFSET = 0x80000000; var hash160 = function (data) { return (0, ripemd160)((0, sha256)(data)); }; var fromU32 = function (data) { return (0, createView)(data).getUint32(0, false); }; var toU32 = function (n) { if (!Number.isSafeInteger(n) || n < 0 || n > Math.pow(2, 32) - 1) { throw new Error("Invalid number=".concat(n, ". Should be from 0 to 2 ** 32 - 1")); } var buf = new Uint8Array(4); (0, createView)(buf).setUint32(0, n, false); return buf; }; function HDKey(opt) { this.depth = 0; this.index = 0; this.chainCode = null; this.parentFingerprint = 0; if (!opt || typeof opt !== 'object') { throw new Error('HDKey.constructor must not be called directly'); } this.versions = opt.versions || BITCOIN_VERSIONS; this.depth = opt.depth || 0; this.chainCode = opt.chainCode; this.index = opt.index || 0; this.parentFingerprint = opt.parentFingerprint || 0; if (!this.depth) { if (this.parentFingerprint || this.index) { throw new Error('HDKey: zero depth with non-zero index/parent fingerprint'); } } if (opt.publicKey && opt.privateKey) { throw new Error('HDKey: publicKey and privateKey at same time.'); } if (opt.privateKey) { if (!secp.utils.isValidPrivateKey(opt.privateKey)) { throw new Error('Invalid private key'); } this.privKey = typeof opt.privateKey === 'bigint' ? opt.privateKey : bytesToNumber(opt.privateKey); this.privKeyBytes = numberToBytes(this.privKey); this.pubKey = secp.getPublicKey(opt.privateKey, true); } else if (opt.publicKey) { this.pubKey = Point.fromHex(opt.publicKey).toRawBytes(true); // force compressed point } else { throw new Error('HDKey: no public or private key provided'); } this.pubHash = hash160(this.pubKey); } Object.defineProperty(HDKey.prototype, "fingerprint", { get: function () { if (!this.pubHash) { throw new Error('No publicKey set!'); } return fromU32(this.pubHash); }, enumerable: false, configurable: true }); Object.defineProperty(HDKey.prototype, "identifier", { get: function () { return this.pubHash; }, enumerable: false, configurable: true }); Object.defineProperty(HDKey.prototype, "pubKeyHash", { get: function () { return this.pubHash; }, enumerable: false, configurable: true }); Object.defineProperty(HDKey.prototype, "privateKey", { get: function () { return this.privKeyBytes || null; }, enumerable: false, configurable: true }); Object.defineProperty(HDKey.prototype, "publicKey", { get: function () { return this.pubKey || null; }, enumerable: false, configurable: true }); Object.defineProperty(HDKey.prototype, "privateExtendedKey", { get: function () { var priv = this.privateKey; if (!priv) { throw new Error('No private key'); } return base58check.encode(this.serialize(this.versions.private, (0, utils_1.concatBytes)(new Uint8Array([0]), priv))); }, enumerable: false, configurable: true }); Object.defineProperty(HDKey.prototype, "publicExtendedKey", { get: function () { if (!this.pubKey) { throw new Error('No public key'); } return base58check.encode(this.serialize(this.versions.public, this.pubKey)); }, enumerable: false, configurable: true }); HDKey.fromMasterSeed = function (seed, versions) { if (versions === void 0) { versions = BITCOIN_VERSIONS; } (0, _assert_1)(seed); if (8 * seed.length < 128 || 8 * seed.length > 512) { throw new Error("HDKey: wrong seed length=".concat(seed.length, ". Should be between 128 and 512 bits; 256 bits is advised)")); } var I = (0, hmac)(sha512, MASTER_SECRET, seed); return new HDKey({ versions: versions, chainCode: I.slice(32), privateKey: I.slice(0, 32), }); }; HDKey.fromExtendedKey = function (base58key, versions) { if (versions === void 0) { versions = BITCOIN_VERSIONS; } // => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33) var keyBuffer = base58check.decode(base58key); var keyView = (0, createView)(keyBuffer); var version = keyView.getUint32(0, false); var opt = { versions: versions, depth: keyBuffer[4], parentFingerprint: keyView.getUint32(5, false), index: keyView.getUint32(9, false), chainCode: keyBuffer.slice(13, 45), }; var key = keyBuffer.slice(45); var isPriv = key[0] === 0; if (version !== versions[isPriv ? 'private' : 'public']) { throw new Error('Version mismatch'); } if (isPriv) { return new HDKey(__assign(__assign({}, opt), { privateKey: key.slice(1) })); } else { return new HDKey(__assign(__assign({}, opt), { publicKey: key })); } }; HDKey.fromJSON = function (json) { return HDKey.fromExtendedKey(json.xpriv); }; HDKey.prototype.derive = function (path) { if (!/^[mM]'?/.test(path)) { throw new Error('Path must start with "m" or "M"'); } if (/^[mM]'?$/.test(path)) { return this; } var parts = path.replace(/^[mM]'?\//, '').split('/'); // tslint:disable-next-line var child = this; for (var _i = 0, parts_1 = parts; _i < parts_1.length; _i++) { var c = parts_1[_i]; var m = /^(\d+)('?)$/.exec(c); var m1 = m && m[1]; if (!m || m.length !== 3 || typeof m1 !== 'string') { throw new Error("Invalid child index: ".concat(c)); } var idx = +m1; if (!Number.isSafeInteger(idx) || idx >= HARDENED_OFFSET) { throw new Error('Invalid index'); } // hardened key if (m[2] === "'") { idx += HARDENED_OFFSET; } child = child.deriveChild(idx); } return child; }; HDKey.prototype.deriveChild = function (index) { if (!this.pubKey || !this.chainCode) { throw new Error('No publicKey or chainCode set'); } var data = toU32(index); if (index >= HARDENED_OFFSET) { // Hardened var priv = this.privateKey; if (!priv) { throw new Error('Could not derive hardened child key'); } // Hardened child: 0x00 || ser256(kpar) || ser32(index) data = (0, utils_1.concatBytes)(new Uint8Array([0]), priv, data); } else { // Normal child: serP(point(kpar)) || ser32(index) data = (0, utils_1.concatBytes)(this.pubKey, data); } var I = (0, hmac)(sha512, this.chainCode, data); var childTweak = bytesToNumber(I.slice(0, 32)); var chainCode = I.slice(32); if (!secp.utils.isValidPrivateKey(childTweak)) { throw new Error('Tweak bigger than curve order'); } var opt = { versions: this.versions, chainCode: chainCode, depth: this.depth + 1, parentFingerprint: this.fingerprint, index: index, }; try { // Private parent key -> private child key if (this.privateKey) { var added = (0, modular_1.mod)(this.privKey + childTweak, secp.CURVE.n); if (!secp.utils.isValidPrivateKey(added)) { throw new Error('The tweak was out of range or the resulted private key is invalid'); } opt.privateKey = added; } else { var added = Point.fromHex(this.pubKey).add(Point.fromPrivateKey(childTweak)); // Cryptographically impossible: hmac-sha512 preimage would need to be found if (added.equals(Point.ZERO)) { throw new Error('The tweak was equal to negative P, which made the result key invalid'); } opt.publicKey = added.toRawBytes(true); } return new HDKey(opt); } catch (err) { return this.deriveChild(index + 1); } }; HDKey.prototype.sign = function (hash) { if (!this.privateKey) { throw new Error('No privateKey set!'); } (0, _assert_1)(hash, 32); return secp.sign(hash, this.privKey).toCompactRawBytes(); }; HDKey.prototype.verify = function (hash, signature) { (0, _assert_1)(hash, 32); (0, _assert_1)(signature, 64); if (!this.publicKey) { throw new Error('No publicKey set!'); } var sig; try { sig = secp.Signature.fromCompact(signature); } catch (error) { return false; } return secp.verify(sig, hash, this.publicKey); }; HDKey.prototype.wipePrivateData = function () { this.privKey = undefined; if (this.privKeyBytes) { this.privKeyBytes.fill(0); this.privKeyBytes = undefined; } return this; }; HDKey.prototype.toJSON = function () { return { xpriv: this.privateExtendedKey, xpub: this.publicExtendedKey, }; }; HDKey.prototype.serialize = function (version, key) { if (!this.chainCode) { throw new Error('No chainCode set'); } (0, _assert_1)(key, 33); // version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33) return (0, utils_1.concatBytes)(toU32(version), new Uint8Array([this.depth]), toU32(this.parentFingerprint), toU32(this.index), this.chainCode, key); }; return HDKey; }()); bip32.HDKey = HDKey })(); var HDKey = bip32.HDKey;