UNPKG

ox

Version:

Ethereum Standard Library

377 lines 12.3 kB
import * as Bytes from './Bytes.js'; import * as Errors from './Errors.js'; import * as Hex from './Hex.js'; import * as Json from './Json.js'; /** * Asserts that a {@link ox#PublicKey.PublicKey} is valid. * * @example * ```ts twoslash * import { PublicKey } from 'ox' * * PublicKey.assert({ * prefix: 4, * y: 49782753348462494199823712700004552394425719014458918871452329774910450607807n, * }) * // @error: PublicKey.InvalidError: Value \`{"y":"1"}\` is not a valid public key. * // @error: Public key must contain: * // @error: - an `x` and `prefix` value (compressed) * // @error: - an `x`, `y`, and `prefix` value (uncompressed) * ``` * * @param publicKey - The public key object to assert. */ export function assert(publicKey, options = {}) { const { compressed } = options; const { prefix, x, y } = publicKey; // Uncompressed if (compressed === false || (typeof x === 'bigint' && typeof y === 'bigint')) { if (prefix !== 4) throw new InvalidPrefixError({ prefix, cause: new InvalidUncompressedPrefixError(), }); return; } // Compressed if (compressed === true || (typeof x === 'bigint' && typeof y === 'undefined')) { if (prefix !== 3 && prefix !== 2) throw new InvalidPrefixError({ prefix, cause: new InvalidCompressedPrefixError(), }); return; } // Unknown/invalid throw new InvalidError({ publicKey }); } /** * Compresses a {@link ox#PublicKey.PublicKey}. * * @example * ```ts twoslash * import { PublicKey } from 'ox' * * const publicKey = PublicKey.from({ * prefix: 4, * x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n, * y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n, * }) * * const compressed = PublicKey.compress(publicKey) // [!code focus] * // @log: { * // @log: prefix: 3, * // @log: x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n, * // @log: } * ``` * * @param publicKey - The public key to compress. * @returns The compressed public key. */ export function compress(publicKey) { const { x, y } = publicKey; return { prefix: y % 2n === 0n ? 2 : 3, x, }; } /** * Instantiates a typed {@link ox#PublicKey.PublicKey} object from a {@link ox#PublicKey.PublicKey}, {@link ox#Bytes.Bytes}, or {@link ox#Hex.Hex}. * * @example * ```ts twoslash * import { PublicKey } from 'ox' * * const publicKey = PublicKey.from({ * prefix: 4, * x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n, * y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n, * }) * // @log: { * // @log: prefix: 4, * // @log: x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n, * // @log: y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n, * // @log: } * ``` * * @example * ### From Serialized * * ```ts twoslash * import { PublicKey } from 'ox' * * const publicKey = PublicKey.from('0x048318535b54105d4a7aae60c08fc45f9687181b4fdfc625bd1a753fa7397fed753547f11ca8696646f2f3acb08e31016afac23e630c5d11f59f61fef57b0d2aa5') * // @log: { * // @log: prefix: 4, * // @log: x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n, * // @log: y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n, * // @log: } * ``` * * @param value - The public key value to instantiate. * @returns The instantiated {@link ox#PublicKey.PublicKey}. */ export function from(value) { const publicKey = (() => { if (Hex.validate(value)) return fromHex(value); if (Bytes.validate(value)) return fromBytes(value); const { prefix, x, y } = value; if (typeof x === 'bigint' && typeof y === 'bigint') return { prefix: prefix ?? 0x04, x, y }; return { prefix, x }; })(); assert(publicKey); return publicKey; } /** * Deserializes a {@link ox#PublicKey.PublicKey} from a {@link ox#Bytes.Bytes} value. * * @example * ```ts twoslash * // @noErrors * import { PublicKey } from 'ox' * * const publicKey = PublicKey.fromBytes(new Uint8Array([128, 3, 131, ...])) * // @log: { * // @log: prefix: 4, * // @log: x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n, * // @log: y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n, * // @log: } * ``` * * @param publicKey - The serialized public key. * @returns The deserialized public key. */ export function fromBytes(publicKey) { return fromHex(Hex.fromBytes(publicKey)); } /** * Deserializes a {@link ox#PublicKey.PublicKey} from a {@link ox#Hex.Hex} value. * * @example * ```ts twoslash * import { PublicKey } from 'ox' * * const publicKey = PublicKey.fromHex('0x8318535b54105d4a7aae60c08fc45f9687181b4fdfc625bd1a753fa7397fed753547f11ca8696646f2f3acb08e31016afac23e630c5d11f59f61fef57b0d2aa5') * // @log: { * // @log: prefix: 4, * // @log: x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n, * // @log: y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n, * // @log: } * ``` * * @example * ### Deserializing a Compressed Public Key * * ```ts twoslash * import { PublicKey } from 'ox' * * const publicKey = PublicKey.fromHex('0x038318535b54105d4a7aae60c08fc45f9687181b4fdfc625bd1a753fa7397fed75') * // @log: { * // @log: prefix: 3, * // @log: x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n, * // @log: } * ``` * * @param publicKey - The serialized public key. * @returns The deserialized public key. */ export function fromHex(publicKey) { if (publicKey.length !== 132 && publicKey.length !== 130 && publicKey.length !== 68) throw new InvalidSerializedSizeError({ publicKey }); if (publicKey.length === 130) { const x = BigInt(Hex.slice(publicKey, 0, 32)); const y = BigInt(Hex.slice(publicKey, 32, 64)); return { prefix: 4, x, y, }; } if (publicKey.length === 132) { const prefix = Number(Hex.slice(publicKey, 0, 1)); const x = BigInt(Hex.slice(publicKey, 1, 33)); const y = BigInt(Hex.slice(publicKey, 33, 65)); return { prefix, x, y, }; } const prefix = Number(Hex.slice(publicKey, 0, 1)); const x = BigInt(Hex.slice(publicKey, 1, 33)); return { prefix, x, }; } /** * Serializes a {@link ox#PublicKey.PublicKey} to {@link ox#Bytes.Bytes}. * * @example * ```ts twoslash * import { PublicKey } from 'ox' * * const publicKey = PublicKey.from({ * prefix: 4, * x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n, * y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n, * }) * * const bytes = PublicKey.toBytes(publicKey) // [!code focus] * // @log: Uint8Array [128, 3, 131, ...] * ``` * * @param publicKey - The public key to serialize. * @returns The serialized public key. */ export function toBytes(publicKey, options = {}) { return Bytes.fromHex(toHex(publicKey, options)); } /** * Serializes a {@link ox#PublicKey.PublicKey} to {@link ox#Hex.Hex}. * * @example * ```ts twoslash * import { PublicKey } from 'ox' * * const publicKey = PublicKey.from({ * prefix: 4, * x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n, * y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n, * }) * * const hex = PublicKey.toHex(publicKey) // [!code focus] * // @log: '0x048318535b54105d4a7aae60c08fc45f9687181b4fdfc625bd1a753fa7397fed753547f11ca8696646f2f3acb08e31016afac23e630c5d11f59f61fef57b0d2aa5' * ``` * * @param publicKey - The public key to serialize. * @returns The serialized public key. */ export function toHex(publicKey, options = {}) { assert(publicKey); const { prefix, x, y } = publicKey; const { includePrefix = true } = options; const publicKey_ = Hex.concat(includePrefix ? Hex.fromNumber(prefix, { size: 1 }) : '0x', Hex.fromNumber(x, { size: 32 }), // If the public key is not compressed, add the y coordinate. typeof y === 'bigint' ? Hex.fromNumber(y, { size: 32 }) : '0x'); return publicKey_; } /** * Validates a {@link ox#PublicKey.PublicKey}. Returns `true` if valid, `false` otherwise. * * @example * ```ts twoslash * import { PublicKey } from 'ox' * * const valid = PublicKey.validate({ * prefix: 4, * y: 49782753348462494199823712700004552394425719014458918871452329774910450607807n, * }) * // @log: false * ``` * * @param publicKey - The public key object to assert. */ export function validate(publicKey, options = {}) { try { assert(publicKey, options); return true; } catch (error) { return false; } } /** * Thrown when a public key is invalid. * * @example * ```ts twoslash * import { PublicKey } from 'ox' * * PublicKey.assert({ y: 1n }) * // @error: PublicKey.InvalidError: Value `{"y":1n}` is not a valid public key. * // @error: Public key must contain: * // @error: - an `x` and `prefix` value (compressed) * // @error: - an `x`, `y`, and `prefix` value (uncompressed) * ``` */ export class InvalidError extends Errors.BaseError { constructor({ publicKey }) { super(`Value \`${Json.stringify(publicKey)}\` is not a valid public key.`, { metaMessages: [ 'Public key must contain:', '- an `x` and `prefix` value (compressed)', '- an `x`, `y`, and `prefix` value (uncompressed)', ], }); Object.defineProperty(this, "name", { enumerable: true, configurable: true, writable: true, value: 'PublicKey.InvalidError' }); } } /** Thrown when a public key has an invalid prefix. */ export class InvalidPrefixError extends Errors.BaseError { constructor({ prefix, cause }) { super(`Prefix "${prefix}" is invalid.`, { cause, }); Object.defineProperty(this, "name", { enumerable: true, configurable: true, writable: true, value: 'PublicKey.InvalidPrefixError' }); } } /** Thrown when the public key has an invalid prefix for a compressed public key. */ export class InvalidCompressedPrefixError extends Errors.BaseError { constructor() { super('Prefix must be 2 or 3 for compressed public keys.'); Object.defineProperty(this, "name", { enumerable: true, configurable: true, writable: true, value: 'PublicKey.InvalidCompressedPrefixError' }); } } /** Thrown when the public key has an invalid prefix for an uncompressed public key. */ export class InvalidUncompressedPrefixError extends Errors.BaseError { constructor() { super('Prefix must be 4 for uncompressed public keys.'); Object.defineProperty(this, "name", { enumerable: true, configurable: true, writable: true, value: 'PublicKey.InvalidUncompressedPrefixError' }); } } /** Thrown when the public key has an invalid serialized size. */ export class InvalidSerializedSizeError extends Errors.BaseError { constructor({ publicKey }) { super(`Value \`${publicKey}\` is an invalid public key size.`, { metaMessages: [ 'Expected: 33 bytes (compressed + prefix), 64 bytes (uncompressed) or 65 bytes (uncompressed + prefix).', `Received ${Hex.size(Hex.from(publicKey))} bytes.`, ], }); Object.defineProperty(this, "name", { enumerable: true, configurable: true, writable: true, value: 'PublicKey.InvalidSerializedSizeError' }); } } //# sourceMappingURL=PublicKey.js.map