ccxt
Version:
228 lines (225 loc) • 8.81 kB
JavaScript
// ----------------------------------------------------------------------------
// 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' });