UNPKG

mina-attestations

Version:
308 lines 11.1 kB
/** * JSON serialization of provable types and values. */ import { NestedProvable } from "./nested.js"; import { array, ProvableType } from "./o1js-missing.js"; import { Field, Bool, UInt8, UInt32, UInt64, PublicKey, Signature, Undefined, Bytes, DynamicProof, VerificationKey, Struct, Int64, } from 'o1js'; import { assert, assertHasMethod, defined, mapObject } from "./util.js"; import { ProvableFactory } from "./provable-factory.js"; export { serializeProvableType, serializeProvable, serializeProvableField, serializeProvablePublicKey, serializeNestedProvable, serializeNestedProvableValue, serializeSimplyNestedProvableValue, deserializeProvableType, deserializeProvable, deserializeNestedProvable, deserializeNestedProvableValue, replaceNull, replaceUndefined, }; // Supported o1js base types const supportedTypes = { Field, Bool, UInt8, UInt32, UInt64, Int64, PublicKey, Signature, Undefined, VerificationKey, }; let mapProvableTypeToName = new Map(); for (let [key, value] of Object.entries(supportedTypes)) { mapProvableTypeToName.set(value, key); } // SERIALIZE function serializeProvableType(type) { let serialized = ProvableFactory.tryToJSON(type); if (serialized !== undefined) return serialized; if ('serialize' in type && typeof type.serialize === 'function') { return type.serialize(); } if (type.prototype instanceof Bytes.Base) { return { _type: 'Bytes', size: type.size }; } if (type.prototype instanceof DynamicProof) { let { publicInputType, publicOutputType, maxProofsVerified, featureFlags } = type; let proof = { name: type.name, publicInput: serializeProvableType(publicInputType), publicOutput: serializeProvableType(publicOutputType), maxProofsVerified, featureFlags: replaceUndefined(featureFlags), }; return { _type: 'Proof', proof }; } let _type = mapProvableTypeToName.get(type); if (_type === undefined && type._isStruct) { return serializeStructType(type); } if (_type === undefined && type._isArray) { return { _type: 'Array', inner: serializeProvableType(type.innerType), size: type.size, }; } assert(_type !== undefined, () => { console.log('serializeProvableType', type); return `serializeProvableType: Unsupported provable type: ${type}`; }); return { _type }; } function serializeProvable(value) { let typeClass = ProvableType.fromValue(value); let serializedType = serializeProvableType(typeClass); if (ProvableFactory.isSerialized(serializedType)) { let serialized = ProvableFactory.tryValueToJSON(value); return defined(serialized); } switch (serializedType._type) { case 'Bytes': return { ...serializedType, value: value.toHex() }; case 'Proof': let json = value.toJSON(); return { ...serializedType, value: json }; case 'Array': { return { ...serializedType, value: value.map((x) => serializeProvable(x)), }; } case 'Struct': let result = {}; for (let key in serializedType.properties) { result[key] = serializeNestedProvableValue(value[key]); } return { ...serializedType, value: result }; case 'Undefined': return { ...serializedType, value: null }; case 'Constant': return serializedType; case 'String': return { ...serializedType, value }; case 'UInt8': return { ...serializedType, value: value.toString() }; case 'VerificationKey': let vk = value; return { ...serializedType, value: { data: vk.data, hash: vk.hash.toString() }, }; default: assertHasMethod(value, 'toJSON', `Missing toJSON method for ${serializedType._type}`); return { ...serializedType, value: value.toJSON() }; } } // some special cases function serializeProvableField(value) { return { _type: 'Field', value: value.toJSON() }; } function serializeProvablePublicKey(value) { return { _type: 'PublicKey', value: value.toJSON(), }; } function serializeStructType(type) { let value = type.empty(); let properties = {}; for (let key in value) { let type = NestedProvable.fromValue(value[key]); properties[key] = serializeNestedProvable(type); } return { _type: 'Struct', properties }; } function serializeNestedProvable(type) { if (ProvableType.isProvableType(type)) { return serializeProvableType(type); } if (typeof type === 'string' || type === String) return { _type: 'String' }; if (typeof type === 'object' && type !== null) { const serializedObject = {}; for (const key of Object.keys(type)) { serializedObject[key] = serializeNestedProvable(type[key]); } return serializedObject; } throw Error(`Unsupported type in NestedProvable: ${type}`); } function serializeNestedProvableValue(value) { let type = NestedProvable.fromValue(value); return serializeNestedProvableTypeAndValue({ type, value }); } function serializeSimplyNestedProvableValue(value) { return mapObject(value, (v) => serializeProvable(v)); } function serializeNestedProvableTypeAndValue(t) { if (ProvableType.isProvableType(t.type)) { return serializeProvable(t.value); } if (typeof t.type === 'string' || t.type === String) return t.value; return Object.fromEntries(Object.keys(t.type).map((key) => { assert(key in t.value, `Missing value for key ${key}`); return [ key, serializeNestedProvableTypeAndValue({ type: t.type[key], value: t.value[key], }), ]; })); } // DESERIALIZE function deserializeProvableType(type) { if (ProvableFactory.isSerialized(type)) return ProvableFactory.fromJSON(type); if (type._type === 'Constant') { return ProvableType.constant(type.value); } if (type._type === 'Bytes') { return Bytes(type.size); } if (type._type === 'Proof') { let proof = type.proof; let Proof = class extends DynamicProof { static publicInputType = ProvableType.get(deserializeProvableType(proof.publicInput)); static publicOutputType = ProvableType.get(deserializeProvableType(proof.publicOutput)); static maxProofsVerified = proof.maxProofsVerified; static featureFlags = replaceNull(proof.featureFlags); }; Object.defineProperty(Proof, 'name', { value: proof.name }); return Proof; } if (type._type === 'Struct') { let properties = deserializeNestedProvable(type.properties); return Struct(properties); } if (type._type === 'Array') { let inner = deserializeProvableType(type.inner); return array(inner, type.size); } if (type._type === 'String') { return String; } let result = supportedTypes[type._type]; assert(result !== undefined, `Unsupported provable type: ${type._type}`); return result; } function deserializeProvable(json) { if (ProvableFactory.isSerialized(json)) return ProvableFactory.valueFromJSON(json); let { _type, value } = json; switch (json._type) { case 'Field': return Field.fromJSON(value); case 'Bool': return Bool.fromJSON(value); case 'UInt8': return UInt8.fromJSON({ value }); case 'UInt32': return UInt32.fromJSON(value); case 'UInt64': return UInt64.fromJSON(value); case 'Int64': return Int64.fromJSON(value); case 'PublicKey': return PublicKey.fromJSON(value); case 'Signature': return Signature.fromJSON(value); case 'Undefined': return undefined; case 'VerificationKey': return VerificationKey.fromJSON(value); case 'Bytes': let BytesN = deserializeProvableType(json); return BytesN.fromHex(value); case 'Proof': return proofFromJSONSync(json); case 'Array': return value.map((v) => deserializeProvable(v)); case 'Struct': let type = deserializeProvableType(json); let result = {}; for (let key in json.properties) { result[key] = deserializeNestedProvableValue(value[key]); } return new type(result); case 'Constant': return value; case 'String': return value; default: json; throw Error(`Unsupported provable type: ${_type}`); } } // this only works if `await initializeBindings()` from o1js has been called before // but this implicit dependency seems better than making every `fromJSON` method async function proofFromJSONSync(json) { let Proof = deserializeProvableType(json); let { maxProofsVerified, proof: proofString, publicInput, publicOutput, } = json.value; let proof = Proof._proofFromBase64(proofString, maxProofsVerified); let fields = publicInput.map(Field).concat(publicOutput.map(Field)); return Proof.provable.fromFields(fields, [ [], [], [proof, maxProofsVerified], ]); } function deserializeNestedProvable(type) { if (typeof type === 'object' && type !== null) { if ('_type' in type) { // basic provable type return deserializeProvableType(type); } else { // nested object let result = {}; for (const [key, value] of Object.entries(type)) { result[key] = deserializeNestedProvable(value); } return result; } } throw Error(`Invalid type in NestedProvable: ${type}`); } function deserializeNestedProvableValue(value) { if (typeof value === 'string') return value; if (typeof value === 'object' && value !== null) { if ('_type' in value) { // basic provable type return deserializeProvable(value); } else { // nested object const result = {}; for (let [key, v] of Object.entries(value)) { result[key] = deserializeNestedProvableValue(v); } return result; } } throw Error(`Invalid nested provable value: ${value}`); } function replaceNull(obj) { return mapObject(obj, (value) => (value === null ? undefined : value)); } // `null` is preserved in JSON, but `undefined` is removed function replaceUndefined(obj) { return mapObject(obj, (value) => (value === undefined ? null : value)); } //# sourceMappingURL=serialize-provable.js.map