UNPKG

ox

Version:

Ethereum Standard Library

189 lines (175 loc) 7.22 kB
import * as Bytes from './Bytes.js' import * as Errors from './Errors.js' import * as Hash from './Hash.js' import * as Hex from './Hex.js' /** * Checks if an input is matched in the bloom filter. * * @example * ```ts twoslash * import { Bloom } from 'ox' * * Bloom.contains( * '0x00000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002020000000000000000000000000000000000000000000008000000001000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', * '0xef2d6d194084c2de36e0dabfce45d046b37d1106' * ) * // @log: true * ``` * * @param bloom - Bloom filter value. * @param input - Input to check. * @returns Whether the input is matched in the bloom filter. */ export function contains( bloom: Hex.Hex, input: Hex.Hex | Bytes.Bytes, ): boolean { return containsPrepared(prepare(bloom), input) } export declare namespace contains { type ErrorType = | Bytes.fromHex.ErrorType | Hash.keccak256.ErrorType | InvalidBloomError | Errors.GlobalErrorType } /** Prepared bloom filter for use with {@link ox#Bloom.(containsPrepared:function)}/{@link ox#Bloom.(containsHash:function)}. */ export type Prepared = { filter: Uint8Array } /** * Prepares a bloom filter for repeated membership checks against the same filter. * * Pairs with {@link ox#Bloom.(containsPrepared:function)} (or {@link ox#Bloom.(containsHash:function)}) to avoid * the per-call hex-to-bytes conversion that {@link ox#Bloom.(contains:function)} pays. * * @example * ```ts twoslash * import { Bloom } from 'ox' * * const prepared = Bloom.prepare( * '0x00000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002020000000000000000000000000000000000000000000008000000001000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' * ) * Bloom.containsPrepared( * prepared, * '0xef2d6d194084c2de36e0dabfce45d046b37d1106' * ) * // @log: true * ``` * * @param bloom - Bloom filter value. * @returns A prepared bloom filter. */ export function prepare(bloom: Hex.Hex): Prepared { if (!validate(bloom)) throw new InvalidBloomError({ bloom }) return { filter: Bytes.fromHex(bloom) } } export declare namespace prepare { type ErrorType = | Bytes.fromHex.ErrorType | InvalidBloomError | Errors.GlobalErrorType } /** * Checks if an input is matched in a {@link ox#Bloom.Prepared} bloom filter. * * @example * ```ts twoslash * import { Bloom } from 'ox' * * const prepared = Bloom.prepare( * '0x00000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002020000000000000000000000000000000000000000000008000000001000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' * ) * Bloom.containsPrepared( * prepared, * '0xef2d6d194084c2de36e0dabfce45d046b37d1106' * ) * // @log: true * ``` * * @param prepared - Prepared bloom filter. * @param input - Input to check. * @returns Whether the input is matched in the bloom filter. */ export function containsPrepared( prepared: Prepared, input: Hex.Hex | Bytes.Bytes, ): boolean { return containsHash(prepared, Hash.keccak256(input, { as: 'Bytes' })) } export declare namespace containsPrepared { type ErrorType = Hash.keccak256.ErrorType | Errors.GlobalErrorType } /** * Checks if a precomputed `keccak256` hash is matched in a {@link ox#Bloom.Prepared} * bloom filter. Use when the caller already has the hash and wants to skip the * keccak inside `containsPrepared`. * * @example * ```ts twoslash * import { Bloom, Hash } from 'ox' * * const prepared = Bloom.prepare( * '0x00000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002020000000000000000000000000000000000000000000008000000001000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' * ) * const hash = Hash.keccak256( * '0xef2d6d194084c2de36e0dabfce45d046b37d1106', * { as: 'Bytes' } * ) * Bloom.containsHash(prepared, hash) * // @log: true * ``` * * @param prepared - Prepared bloom filter. * @param hash - Precomputed `keccak256` hash of the input, as `Bytes`. * @returns Whether the input is matched in the bloom filter. */ export function containsHash(prepared: Prepared, hash: Bytes.Bytes): boolean { const { filter } = prepared for (const i of [0, 2, 4]) { const bit = (hash[i + 1]! + (hash[i]! << 8)) & 0x7ff if ((filter[256 - 1 - Math.floor(bit / 8)]! & (1 << (bit % 8))) === 0) return false } return true } export declare namespace containsHash { type ErrorType = Errors.GlobalErrorType } /** * Checks if a string is a valid bloom filter value. * * @example * ```ts twoslash * import { Bloom } from 'ox' * * Bloom.validate('0x') * // @log: false * * Bloom.validate( * '0x00000000000000000000008000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000044000200000000000000000002000000000000000000000040000000000000000000000000000020000000000000000000800000000000800000000000800000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808002000000000400000000000000000000000060000000000000000000000000000000000000000000000100000000000002000000' * ) * // @log: true * ``` * * @param value - Value to check. * @returns Whether the value is a valid bloom filter. */ export function validate(value: string): value is Hex.Hex { return Hex.validate(value) && Hex.size(value) === 256 } export declare namespace validate { type ErrorType = | Hex.validate.ErrorType | Hex.size.ErrorType | Errors.GlobalErrorType } /** Thrown when a value is not a valid bloom filter. */ export class InvalidBloomError extends Errors.BaseError { override readonly name = 'Bloom.InvalidBloomError' constructor({ bloom }: { bloom: string }) { super( `Value \`${bloom}\` is not a valid bloom filter (must be a 256-byte hex string).`, ) } }