o1js
Version:
TypeScript framework for zk-SNARKs and zkApps
320 lines (253 loc) • 7.77 kB
text/typescript
// NOTE: these leaves are currently backwards compatible with the old encoding format, but the
// auxiliary components may change format in the future
import { FieldsDecoder, ProvableSerializable } from './util.js';
import { versionBytes } from '../../crypto/constants.js';
import { withVersionNumber } from '../../lib/binable.js';
import { Bool } from '../../../lib/provable/bool.js';
import { Field } from '../../../lib/provable/field.js';
import { Provable } from '../../../lib/provable/provable.js';
import { HashInput } from '../../../lib/provable/types/provable-derivers.js';
import { Struct } from '../../../lib/provable/types/struct.js';
import { toBase58Check } from '../../../lib/util/base58.js';
export { Bool } from '../../../lib/provable/bool.js';
export { Field } from '../../../lib/provable/field.js';
export { Int64, UInt32, UInt64, Sign } from '../../../lib/provable/int.js';
export { PublicKey } from '../../../lib/provable/crypto/signature.js';
// for now, we erase the value conversion in the proxy, as it is currently not utilized
function proxyProvableSerializable<T, Val>(
T: ProvableSerializable<T, Val>
): ProvableSerializable<T, T> {
return {
sizeInFields(): number {
return T.sizeInFields();
},
toJSON(x: T): any {
return T.toJSON(x);
},
toInput(x: T): HashInput {
return T.toInput(x);
},
toFields(x: T): Field[] {
return T.toFields(x);
},
toAuxiliary(x?: T): any[] {
return T.toAuxiliary(x);
},
fromFields(fields: Field[], aux: any[]): T {
return T.fromFields(fields, aux);
},
toValue(x: T): T {
return x;
},
fromValue(x: T): T {
return x;
},
check(x: T) {
T.check(x);
},
};
}
export interface Option<T> {
isSome: Bool;
value: T;
}
export function Option<T>(T: ProvableSerializable<T>) {
return {
sizeInFields(): number {
return Bool.sizeInFields() + T.sizeInFields();
},
toJSON(x: Option<T>): any {
return x.isSome.toBoolean() ? T.toJSON(x.value) : null;
},
toInput(x: Option<T>): HashInput {
const flagInput = Bool.toInput(x.isSome);
const valueInput = T.toInput(x.value);
return {
fields: valueInput.fields,
packed: flagInput.packed!.concat(valueInput.packed ?? []),
};
},
toFields(x: Option<T>): Field[] {
return [...Bool.toFields(x.isSome), ...T.toFields(x.value)];
},
toAuxiliary(x?: Option<T>): any[] {
return T.toAuxiliary(x?.value);
},
fromFields(fields: Field[], aux: any[]): Option<T> {
const decoder = new FieldsDecoder(fields);
const isSome = decoder.decode(Bool.sizeInFields(), Bool.fromFields);
const value = decoder.decode(T.sizeInFields(), (f) => T.fromFields(f, aux));
return { isSome, value };
},
toValue(x: Option<T>): Option<T> {
return x;
},
fromValue(x: Option<T>): Option<T> {
return x;
},
check(_x: Option<T>) {
throw new Error('TODO');
},
};
}
Option.map = <A, B>(option: Option<A>, f: (value: A) => B): Option<B> => ({
isSome: option.isSome,
value: f(option.value),
});
Option.none = <T>(defaultValue: T): Option<T> => ({
isSome: new Bool(false),
value: defaultValue,
});
Option.some = <T>(value: T): Option<T> => ({
isSome: new Bool(true),
value,
});
export interface Range<T> {
lower: T;
upper: T;
}
export function Range<T>(T: Provable<T>) {
return Struct({
lower: T,
upper: T,
});
}
export interface CommittedList {
data: Field[][];
hash: Field;
}
export const CommittedList: ProvableSerializable<CommittedList> = {
sizeInFields(): number {
return 1;
},
toJSON(x: CommittedList): any {
return x.data.map((datum) => datum.map(Field.toJSON));
},
toInput(x: CommittedList): HashInput {
return { fields: [x.hash] };
},
toFields(x: CommittedList): Field[] {
return [x.hash];
},
toAuxiliary(x?: CommittedList): any[] {
if (x === undefined)
throw new Error('cannot convert undefined CommittedList into auxiliary data');
return [x.data];
},
fromFields(fields: Field[], aux: any[]): CommittedList {
// TODO: runtime type-check the aux data
return { data: aux[0], hash: fields[0] };
},
toValue(x: CommittedList): CommittedList {
return x;
},
fromValue(x: CommittedList): CommittedList {
return x;
},
check(_x: CommittedList) {
throw new Error('TODO');
},
};
export type Events = CommittedList;
export const Events = CommittedList;
export type Actions = CommittedList;
export const Actions = CommittedList;
export type AuthRequiredIdentifier = 'Impossible' | 'None' | 'Proof' | 'Signature' | 'Either';
// TODO: Both
export interface AuthRequired {
constant: Bool;
signatureNecessary: Bool;
signatureSufficient: Bool;
}
export const AuthRequired = {
...proxyProvableSerializable<AuthRequired, any>(
Struct({ constant: Bool, signatureNecessary: Bool, signatureSufficient: Bool })
),
empty(): AuthRequired {
return {
constant: new Bool(true),
signatureNecessary: new Bool(false),
signatureSufficient: new Bool(true),
};
},
isImpossible(x: AuthRequired): Bool {
return Bool.allTrue([x.constant, x.signatureNecessary, x.signatureSufficient.not()]);
},
isNone(x: AuthRequired): Bool {
return Bool.allTrue([x.constant, x.signatureNecessary.not(), x.signatureSufficient]);
},
isProof(x: AuthRequired): Bool {
return Bool.allTrue([
x.constant.not(),
x.signatureNecessary.not(),
x.signatureSufficient.not(),
]);
},
isSignature(x: AuthRequired): Bool {
return Bool.allTrue([x.constant.not(), x.signatureNecessary, x.signatureSufficient]);
},
isEither(x: AuthRequired): Bool {
return Bool.allTrue([x.constant.not(), x.signatureNecessary.not(), x.signatureSufficient]);
},
identifier(x: AuthRequired): AuthRequiredIdentifier {
if (AuthRequired.isImpossible(x).toBoolean()) {
return 'Impossible';
} else if (AuthRequired.isNone(x).toBoolean()) {
return 'None';
} else if (AuthRequired.isProof(x).toBoolean()) {
return 'Proof';
} else if (AuthRequired.isSignature(x).toBoolean()) {
return 'Signature';
} else if (AuthRequired.isEither(x).toBoolean()) {
return 'Either';
} else {
throw new Error('invariant broken: invalid authorization level encoding');
}
},
toJSON(x: AuthRequired): any {
return AuthRequired.identifier(x);
},
};
AuthRequired satisfies ProvableSerializable<AuthRequired>;
export type StateHash = Field;
export const StateHash: ProvableSerializable<StateHash> = {
...proxyProvableSerializable<Field, any>(Field),
toJSON(x: StateHash): any {
const bytes = withVersionNumber(Field, 1).toBytes(x);
return toBase58Check(bytes, versionBytes.stateHash);
},
};
export type TokenId = Field;
export const TokenId = Field;
export interface TokenSymbol {
field: Field;
symbol: string;
}
export const TokenSymbol: ProvableSerializable<TokenSymbol> = {
...proxyProvableSerializable<TokenSymbol, any>(Struct({ field: Field, symbol: String })),
toJSON(x: TokenSymbol): any {
return x.symbol;
},
toInput(x: TokenSymbol): HashInput {
return { packed: [[x.field, 48]] };
},
};
export interface ZkappUri {
data: string;
hash: Field;
}
export const ZkappUri: ProvableSerializable<ZkappUri> = {
...proxyProvableSerializable<ZkappUri, any>(Struct({ data: String, hash: Field })),
toJSON(x: ZkappUri): any {
return x.data;
},
toAuxiliary(x?: ZkappUri): any[] {
return [x?.data];
},
fromFields(fields: Field[], aux: any[]) {
return {
data: aux[0],
hash: fields[0],
};
},
};