mina-attestations
Version:
Private Attestations on Mina
308 lines • 11.1 kB
JavaScript
/**
* 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