UNPKG

mina-attestations

Version:
154 lines 5.18 kB
import { Bytes, Field, UInt8 } from 'o1js'; import { DynamicArray, DynamicArrayBase, provableDynamicArray, } from "./dynamic-array.js"; import { ProvableFactory } from "../provable-factory.js"; import { assert, chunk, stringLength } from "../util.js"; import { DynamicSHA2 } from "./dynamic-sha2.js"; import { z } from 'zod'; export { DynamicBytes, DynamicBytesBase }; /** * Specialization of `DynamicArray` to bytes, * with added helper methods to convert instances to/from values. * * ```ts * const Bytes = DynamicBytes({ maxLength: 120 }); * * let bytes = Bytes.fromString('hello'); * let bytes2 = Bytes.fromBytes([1, 2, 3]); * * let string = bytes.toString(); * let uint8array = bytes2.toBytes(); * ``` */ function DynamicBytes({ maxLength, }) { // assert maxLength bounds assert(maxLength >= 0, 'maxLength must be >= 0'); assert(maxLength < 2 ** 16, 'maxLength must be < 2^16'); class DynamicBytes_ extends DynamicBytesBase { static get maxLength() { return maxLength; } static get provable() { return provableBytes; } static fromBytes(bytes) { if (bytes instanceof Uint8Array) return provableBytes.fromValue(bytes); if (bytes instanceof Bytes.Base) bytes = bytes.bytes; return new DynamicBytes_(bytes.map((t) => UInt8.from(t)), Field(bytes.length)); } static fromHex(hex) { assert(hex.length % 2 === 0, 'Hex string must have even length'); let bytes = chunk([...hex], 2).map((s) => parseInt(s.join(''), 16)); return DynamicBytes_.fromBytes(bytes); } static fromString(s) { return DynamicBytes_.fromBytes(new TextEncoder().encode(s)); } } const provableBytes = provableDynamicArray(UInt8, DynamicBytes_) .mapValue({ there(s) { return Uint8Array.from(s, ({ value }) => Number(value)); }, backAndDistinguish(s) { // gracefully handle different maxLength if (s instanceof DynamicBytesBase) { if (s.maxLength === maxLength) return s; if (s.maxLength < maxLength) return s.growMaxLengthTo(maxLength); // shrinking max length will only work outside circuit s = s.toBytes(); } return [...s].map((t) => ({ value: BigInt(t) })); }, }) .build(); return DynamicBytes_; } DynamicBytes.fromBytes = function (bytes) { return DynamicBytes({ maxLength: bytes.length }).fromBytes(bytes); }; DynamicBytes.from = function (input) { if (typeof input === 'string') { let Bytes = DynamicBytes({ maxLength: stringLength(input) }); return Bytes.fromString(input); } if (input instanceof Uint8Array || Array.isArray(input)) { let Bytes = DynamicBytes({ maxLength: input.length }); return Bytes.fromBytes(input); } assert(input instanceof DynamicArray.Base, 'invalid input'); if (input instanceof DynamicBytesBase) return input; // if this is not a DynamicBytes, we construct an equivalent one let Bytes = DynamicBytes({ maxLength: input.maxLength }); let bytes = new Bytes(input.array, input.length); bytes._indexMasks = input._indexMasks; bytes.__dummyMask = input.__dummyMask; bytes._indicesInRange = input._indicesInRange; return bytes; }; class DynamicBytesBase extends DynamicArrayBase { get innerType() { return UInt8; } /** * Hash the bytes using variants of SHA2. */ hashToBytes(algorithm) { switch (algorithm) { case 'sha2-256': return DynamicSHA2.hash(256, this); case 'sha2-384': return DynamicSHA2.hash(384, this); case 'sha2-512': return DynamicSHA2.hash(512, this); default: assert(false, 'unsupported hash kind'); } } /** * Convert DynamicBytes to a byte array. */ toBytes() { return this.toValue(); } /** * Convert DynamicBytes to a hex string. */ toHex() { return [...this.toBytes()] .map((b) => b.toString(16).padStart(2, '0')) .join(''); } /** * Convert DynamicBytes to a string. */ toString() { return new TextDecoder().decode(this.toBytes()); } growMaxLengthTo(maxLength) { return DynamicBytes.from(super.growMaxLengthTo(maxLength)); } } DynamicBytes.Base = DynamicBytesBase; // serialize/deserialize ProvableFactory.register('DynamicBytes', DynamicBytes, { typeSchema: z.object({ maxLength: z.number() }), valueSchema: z.string(), typeToJSON(constructor) { return { maxLength: constructor.maxLength }; }, typeFromJSON(json) { return DynamicBytes({ maxLength: json.maxLength }); }, valueToJSON(_, value) { return value.toHex(); }, valueFromJSON(type, value) { return type.fromHex(value); }, }); //# sourceMappingURL=dynamic-bytes.js.map