UNPKG

libnexa-ts

Version:

A pure and powerful Nexa SDK library.

140 lines (118 loc) 4.89 kB
import type { Networkish } from "../common/types"; import { networks } from "../core/network/network-manager"; import BufferUtils from "../utils/buffer.utils"; import { ArgumentIsPrivateExtended, InvalidB58Char, InvalidB58Checksum, InvalidLength, InvalidNetwork, InvalidNetworkArgument } from "./exceptions"; import Base58 from "../encoding/base58"; import Base58Check from "../encoding/base58check"; export default class HDKeyUtils { public static readonly Hardened = 0x80000000; public static readonly MaxIndex = 2 * HDKeyUtils.Hardened; public static readonly RootElementAlias = ['m', 'M', 'm\'', 'M\'']; public static readonly VersionSize = 4; public static readonly DepthSize = 1; public static readonly ParentFingerPrintSize = 4; public static readonly ChildIndexSize = 4; public static readonly ChainCodeSize = 32; public static readonly CheckSumSize = 4; public static readonly DataSize = 78; public static readonly SerializedByteSize = 82; public static readonly VersionStart = 0; public static readonly VersionEnd = HDKeyUtils.VersionStart + HDKeyUtils.VersionSize; public static readonly DepthStart = HDKeyUtils.VersionEnd; public static readonly DepthEnd = HDKeyUtils.DepthStart + HDKeyUtils.DepthSize; public static readonly ParentFingerPrintStart = HDKeyUtils.DepthEnd; public static readonly ParentFingerPrintEnd = HDKeyUtils.ParentFingerPrintStart + HDKeyUtils.ParentFingerPrintSize; public static readonly ChildIndexStart = HDKeyUtils.ParentFingerPrintEnd; public static readonly ChildIndexEnd = HDKeyUtils.ChildIndexStart + HDKeyUtils.ChildIndexSize; public static readonly ChainCodeStart = HDKeyUtils.ChildIndexEnd; public static readonly ChainCodeEnd = HDKeyUtils.ChainCodeStart + HDKeyUtils.ChainCodeSize; /** * Util function that splits a string path into a derivation index array. * It will return null if the string path is malformed. * It does not validate if indexes are in bounds. * * @param path */ public static getDerivationIndexes(path: string): number[] | null { // Special cases: if (this.RootElementAlias.includes(path)) { return []; } let steps = path.split('/'); if (!this.RootElementAlias.includes(steps[0])) { return null; } let indexes = steps.slice(1).map(step => { let isHardened = step.slice(-1) === "'"; if (isHardened) { step = step.slice(0, -1); } if (!step || step[0] === '-') { return NaN; } let index = +step; // cast to number if (isHardened) { index += this.Hardened; } return index; }); return indexes.some(isNaN) ? null : indexes; } private static _validateNetwork(data: Uint8Array, networkArg: Networkish, isPrivate = false): Error | null { let network = networks.get(networkArg); if (!network) { return new InvalidNetworkArgument(networkArg); } let version = data.subarray(this.VersionStart, this.VersionEnd); if (BufferUtils.integerFromBuffer(version) !== (isPrivate ? network.xprivkey : network.xpubkey)) { return new InvalidNetwork(version); } return null; } /** * Verifies that a given serialized hd key in base58 with checksum format * is valid. * * @param data - the serialized hd key * @param network - optional, if present, checks that the network provided matches the network serialized. */ public static isValidSerialized(data: Uint8Array | string, network?: Networkish, isPrivate = false): boolean { return this.getSerializedError(data, network, isPrivate) == null; } /** * Checks what's the error that causes the validation of a serialized public key * in base58 with checksum to fail. * * @param data - the serialized hd key * @param network - optional, if present, checks that the network provided matches the network serialized. */ public static getSerializedError(data: string | Uint8Array, network?: Networkish, isPrivate = false): Error | null { if (!(typeof data === 'string' || BufferUtils.isBuffer(data))) { return new TypeError('Expected string or buffer') } if (!Base58.validCharacters(data)) { return new InvalidB58Char('(unknown)', data); } try { data = Base58Check.decode(data as string); } catch { return new InvalidB58Checksum(data); } if (data.length !== HDKeyUtils.DataSize) { return new InvalidLength(data); } if (network != null) { let error = this._validateNetwork(data, network, isPrivate); if (error) { return error; } } if (!isPrivate) { let version = BufferUtils.integerFromBuffer(data.subarray(0, 4)); if (version === networks.mainnet.xprivkey || version === networks.testnet.xprivkey ) { return new ArgumentIsPrivateExtended(); } } return null; } }