UNPKG

ccxt

Version:

A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 100+ exchanges

228 lines (225 loc) • 8.81 kB
// ---------------------------------------------------------------------------- // PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN: // https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code // EDIT THE CORRESPONDENT .ts FILE INSTEAD /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ const _0n = BigInt(0); const _1n = BigInt(1); const _2n = BigInt(2); const u8a = (a) => a instanceof Uint8Array; const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0')); export function bytesToHex(bytes) { if (!u8a(bytes)) throw new Error('Uint8Array expected'); // pre-caching improves the speed 6x let hex = ''; for (let i = 0; i < bytes.length; i++) { hex += hexes[bytes[i]]; } return hex; } export function numberToHexUnpadded(num) { const hex = num.toString(16); return hex.length & 1 ? `0${hex}` : hex; } export function hexToNumber(hex) { if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex); // Big Endian return BigInt(hex === '' ? '0' : `0x${hex}`); } // Caching slows it down 2-3x export function hexToBytes(hex) { if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex); if (hex.length % 2) throw new Error('hex string is invalid: unpadded ' + 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; } // Big Endian export function bytesToNumberBE(bytes) { return hexToNumber(bytesToHex(bytes)); } export function bytesToNumberLE(bytes) { if (!u8a(bytes)) throw new Error('Uint8Array expected'); return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse())); } export const numberToBytesBE = (n, len) => hexToBytes(n.toString(16).padStart(len * 2, '0')); export const numberToBytesLE = (n, len) => numberToBytesBE(n, len).reverse(); // Returns variable number bytes (minimal bigint encoding?) export const numberToVarBytesBE = (n) => hexToBytes(numberToHexUnpadded(n)); export function ensureBytes(title, hex, expectedLength) { let res; if (typeof hex === 'string') { try { res = hexToBytes(hex); } catch (e) { throw new Error(`${title} must be valid hex string, got "${hex}". Cause: ${e}`); } } else if (u8a(hex)) { // Uint8Array.from() instead of hash.slice() because node.js Buffer // is instance of Uint8Array, and its slice() creates **mutable** copy res = Uint8Array.from(hex); } else { throw new Error(`${title} must be hex string or Uint8Array`); } const len = res.length; if (typeof expectedLength === 'number' && len !== expectedLength) throw new Error(`${title} expected ${expectedLength} bytes, got ${len}`); return res; } // Copies several Uint8Arrays into one. export function concatBytes(...arrs) { const r = new Uint8Array(arrs.reduce((sum, a) => sum + a.length, 0)); let pad = 0; // walk through each item, ensure they have proper type arrs.forEach((a) => { if (!u8a(a)) throw new Error('Uint8Array expected'); r.set(a, pad); pad += a.length; }); return r; } export function equalBytes(b1, b2) { // We don't care about timing attacks here if (b1.length !== b2.length) return false; for (let i = 0; i < b1.length; i++) if (b1[i] !== b2[i]) return false; return true; } export function utf8ToBytes(str) { if (typeof str !== 'string') { throw new Error(`utf8ToBytes expected string, got ${typeof str}`); } return new TextEncoder().encode(str); } // Bit operations // Amount of bits inside bigint (Same as n.toString(2).length) export function bitLen(n) { let len; for (len = 0; n > 0n; n >>= _1n, len += 1) ; return len; } // Gets single bit at position. NOTE: first bit position is 0 (same as arrays) // Same as !!+Array.from(n.toString(2)).reverse()[pos] export const bitGet = (n, pos) => (n >> BigInt(pos)) & 1n; // Sets single bit at position export const bitSet = (n, pos, value) => n | ((value ? _1n : _0n) << BigInt(pos)); // Return mask for N bits (Same as BigInt(`0b${Array(i).fill('1').join('')}`)) // Not using ** operator with bigints for old engines. export const bitMask = (n) => (_2n << BigInt(n - 1)) - _1n; // DRBG const u8n = (data) => new Uint8Array(data); // creates Uint8Array const u8fr = (arr) => Uint8Array.from(arr); // another shortcut /** * Minimal HMAC-DRBG from NIST 800-90 for RFC6979 sigs. * @returns function that will call DRBG until 2nd arg returns something meaningful * @example * const drbg = createHmacDRBG<Key>(32, 32, hmac); * drbg(seed, bytesToKey); // bytesToKey must return Key or undefined */ export function createHmacDrbg(hashLen, qByteLen, hmacFn) { if (typeof hashLen !== 'number' || hashLen < 2) throw new Error('hashLen must be a number'); if (typeof qByteLen !== 'number' || qByteLen < 2) throw new Error('qByteLen must be a number'); if (typeof hmacFn !== 'function') throw new Error('hmacFn must be a function'); // Step B, Step C: set hashLen to 8*ceil(hlen/8) let v = u8n(hashLen); // Minimal non-full-spec HMAC-DRBG from NIST 800-90 for RFC6979 sigs. let k = u8n(hashLen); // Steps B and C of RFC6979 3.2: set hashLen, in our case always same let i = 0; // Iterations counter, will throw when over 1000 const reset = () => { v.fill(1); k.fill(0); i = 0; }; const h = (...b) => hmacFn(k, v, ...b); // hmac(k)(v, ...values) const reseed = (seed = u8n()) => { // HMAC-DRBG reseed() function. Steps D-G k = h(u8fr([0x00]), seed); // k = hmac(k || v || 0x00 || seed) v = h(); // v = hmac(k || v) if (seed.length === 0) return; k = h(u8fr([0x01]), seed); // k = hmac(k || v || 0x01 || seed) v = h(); // v = hmac(k || v) }; const gen = () => { // HMAC-DRBG generate() function if (i++ >= 1000) throw new Error('drbg: tried 1000 values'); let len = 0; const out = []; while (len < qByteLen) { v = h(); const sl = v.slice(); out.push(sl); len += v.length; } return concatBytes(...out); }; const genUntil = (seed, pred) => { reset(); reseed(seed); // Steps D-G let res = undefined; // Step H: grind until k is in [1..n-1] while (!(res = pred(gen()))) reseed(); reset(); return res; }; return genUntil; } // Validating curves and fields const validatorFns = { bigint: (val) => typeof val === 'bigint', function: (val) => typeof val === 'function', boolean: (val) => typeof val === 'boolean', string: (val) => typeof val === 'string', isSafeInteger: (val) => Number.isSafeInteger(val), array: (val) => Array.isArray(val), field: (val, object) => object.Fp.isValid(val), hash: (val) => typeof val === 'function' && Number.isSafeInteger(val.outputLen), }; // type Record<K extends string | number | symbol, T> = { [P in K]: T; } export function validateObject(object, validators, optValidators = {}) { const checkField = (fieldName, type, isOptional) => { const checkVal = validatorFns[type]; if (typeof checkVal !== 'function') throw new Error(`Invalid validator "${type}", expected function`); const val = object[fieldName]; if (isOptional && val === undefined) return; if (!checkVal(val, object)) { throw new Error(`Invalid param ${String(fieldName)}=${val} (${typeof val}), expected ${type}`); } }; for (const [fieldName, type] of Object.entries(validators)) checkField(fieldName, type, false); for (const [fieldName, type] of Object.entries(optValidators)) checkField(fieldName, type, true); return object; } // validate type tests // const o: { a: number; b: number; c: number } = { a: 1, b: 5, c: 6 }; // const z0 = validateObject(o, { a: 'isSafeInteger' }, { c: 'bigint' }); // Ok! // // Should fail type-check // const z1 = validateObject(o, { a: 'tmp' }, { c: 'zz' }); // const z2 = validateObject(o, { a: 'isSafeInteger' }, { c: 'zz' }); // const z3 = validateObject(o, { test: 'boolean', z: 'bug' }); // const z4 = validateObject(o, { a: 'boolean', z: 'bug' });