Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encode how to map provables to normal values #213

Merged
merged 13 commits into from
Apr 19, 2024
6 changes: 3 additions & 3 deletions lib/binable.unit-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,10 @@ test.negative(Random.field, (field) =>
FieldWithFailingCheck.fromBytes(FieldWithFailingCheck.toBytes(field))
);

let notABool = Random.map(Random.uint32, (x) => x + 2n);
let notABool = Random.map(Random.uint32, (x) => Number(x) + 2);
test.negative(notABool, (bool) =>
// should fail to decode when check fails
Bool.fromBytes(Bool.toBytes(bool as Bool))
Bool.fromBytes([bool])
);

// record combinator
Expand Down Expand Up @@ -133,7 +133,7 @@ test(randomAccountBalance, (accountBalance) => {
let MessedUpBool = defineBinable({
toBytes: Bool.toBytes,
readBytes(bytes, offset) {
let value = BigInt(bytes[offset++]) as Bool;
let value = !!bytes[offset++];
return [value, offset + 1]; // by accident, offset is incremented twice
},
});
Expand Down
99 changes: 88 additions & 11 deletions lib/from-layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,19 @@ type TypeMapValues<
[K in keyof TypeMap & keyof JsonMap]: BaseType;
};

type TypeMapProvable<TypeMap extends AnyTypeMap, JsonMap extends AnyTypeMap> = {
[K in keyof TypeMap & keyof JsonMap]: GenericProvableExtended<
TypeMap[K],
JsonMap[K],
TypeMap['Field']
>;
type TypeMapProvable<
TypeMap extends AnyTypeMap,
ValueMap extends AnyTypeMap,
JsonMap extends AnyTypeMap
> = {
[K in keyof TypeMap & keyof JsonMap]: K extends keyof ValueMap
? GenericProvableExtended<
TypeMap[K],
ValueMap[K],
JsonMap[K],
TypeMap['Field']
>
: never;
};
type TypeMapSignable<TypeMap extends AnyTypeMap, JsonMap extends AnyTypeMap> = {
[K in keyof TypeMap & keyof JsonMap]: GenericSignable<
Expand Down Expand Up @@ -284,17 +291,18 @@ function SignableFromLayout<

function ProvableFromLayout<
TypeMap extends AnyTypeMap,
ValueMap extends AnyTypeMap,
JsonMap extends AnyTypeMap
>(
TypeMap: TypeMapProvable<TypeMap, JsonMap>,
TypeMap: TypeMapProvable<TypeMap, ValueMap, JsonMap>,
customTypes: Record<
string,
GenericProvableExtended<any, any, TypeMap['Field']>
GenericProvableExtended<any, any, any, TypeMap['Field']>
>
) {
type Field = TypeMap['Field'];
const Field = TypeMap.Field;
type BaseType = GenericProvableExtended<any, any, TypeMap['Field']>;
type BaseType = GenericProvableExtended<any, any, any, TypeMap['Field']>;
type HashInput = { fields?: Field[]; packed?: [Field, number][] };
type Layout = GenericLayout<TypeMap>;

Expand All @@ -306,8 +314,17 @@ function ProvableFromLayout<
function layoutFold<T, R>(spec: FoldSpec<T, R>, typeData: Layout, value?: T) {
return genericLayoutFold(TypeMap, customTypes, spec, typeData, value);
}
function layoutMap<T, R>(
map: (typeData: BaseType, value: T) => R,
typeData: Layout,
value: T
) {
return genericLayoutMap(TypeMap, customTypes, map, typeData, value);
}

function provableFromLayout<T, TJson>(typeData: Layout) {
function provableFromLayout<T, TValue, TJson>(
typeData: Layout
): GenericProvableExtended<T, TValue, TJson, Field> {
return {
sizeInFields(): number {
return sizeInFields(typeData);
Expand Down Expand Up @@ -336,6 +353,12 @@ function ProvableFromLayout<
empty(): T {
return empty(typeData);
},
toValue(value: T): TValue {
return toValue(typeData, value);
},
fromValue(value: TValue | T): T {
return fromValue(typeData, value);
},
};
}

Expand All @@ -346,7 +369,7 @@ function ProvableFromLayout<
return type.toFields(value);
},
reduceArray(array) {
return array!.flat();
return array.flat();
},
reduceObject(keys, object) {
return keys.map((key) => object![key]).flat();
Expand Down Expand Up @@ -485,6 +508,22 @@ function ProvableFromLayout<
);
}

function toValue(typeData: Layout, value: any) {
return layoutMap<any, any>(
(type, value) => type.toValue(value),
typeData,
value
);
}

function fromValue(typeData: Layout, value: any) {
return layoutMap<any, any>(
(type, value) => type.fromValue(value),
typeData,
value
);
}

return { provableFromLayout, toJSONEssential, empty };
}

Expand Down Expand Up @@ -591,6 +630,44 @@ function genericLayoutFold<
return spec.map((TypeMap as any)[typeData.type], value, typeData.type);
}

function genericLayoutMap<
BaseType,
T = any,
R = any,
TypeMap extends AnyTypeMap = AnyTypeMap,
JsonMap extends AnyTypeMap = AnyTypeMap
>(
TypeMap: TypeMapValues<TypeMap, JsonMap, BaseType>,
customTypes: Record<string, BaseType>,
map: (typeData: BaseType, value: T) => R,
typeData: GenericLayout<TypeMap>,
value: T
): R {
return genericLayoutFold<BaseType, T, any, TypeMap, JsonMap>(
TypeMap,
customTypes,
{
map(type, value) {
return map(type, value!);
},
reduceArray(array) {
return array;
},
reduceObject(_, object) {
return object;
},
reduceFlaggedOption(option) {
return option;
},
reduceOrUndefined(value) {
return value;
},
},
typeData,
value
);
}

// types

type WithChecked<TypeMap extends AnyTypeMap> = {
Expand Down
61 changes: 42 additions & 19 deletions lib/generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@ export {
EmptyVoid,
};

type GenericProvable<T, Field> = {
type GenericProvable<T, TValue, Field> = {
toFields: (x: T) => Field[];
toAuxiliary: (x?: T) => any[];
fromFields: (x: Field[], aux: any[]) => T;
sizeInFields(): number;
check: (x: T) => void;
toValue: (x: T) => TValue;
fromValue: (x: T | TValue) => T;
};
type GenericProvablePure<T, Field> = Omit<
GenericProvable<T, Field>,
type GenericProvablePure<T, TValue, Field> = Omit<
GenericProvable<T, TValue, Field>,
'fromFields'
> & {
fromFields: (x: Field[]) => T;
Expand All @@ -40,30 +42,35 @@ type GenericSignable<T, TJson, Field> = {
empty: () => T;
};

type GenericProvableExtended<T, TJson, Field> = GenericProvable<T, Field> &
type GenericProvableExtended<T, TValue, TJson, Field> = GenericProvable<
T,
TValue,
Field
> &
GenericSignable<T, TJson, Field>;

type GenericProvableExtendedPure<T, TJson, Field> = GenericProvablePure<
type GenericProvableExtendedPure<T, TValue, TJson, Field> = GenericProvablePure<
T,
TValue,
Field
> &
GenericSignable<T, TJson, Field>;

type GenericSignableField<Field> = ((
value: number | string | bigint
value: number | string | bigint | Field
) => Field) &
GenericSignable<Field, string, Field> &
Binable<Field> & { sizeInBytes: number };
Binable<Field> & { sizeInBytes: number; toBigint: (x: Field) => bigint };

type GenericField<Field> = GenericSignableField<Field> &
GenericProvable<Field, Field>;
GenericProvable<Field, bigint, Field>;

type GenericSignableBool<Field, Bool = unknown> = ((value: boolean) => Bool) &
GenericSignable<Bool, boolean, Field> &
Binable<Bool> & { sizeInBytes: number };

type GenericBool<Field, Bool = unknown> = GenericSignableBool<Field, Bool> &
GenericProvable<Bool, Field>;
GenericProvable<Bool, boolean, Field>;

type GenericHashInput<Field> = { fields?: Field[]; packed?: [Field, number][] };

Expand All @@ -77,6 +84,8 @@ const emptyType = {
toJSON: () => null,
fromJSON: () => null,
empty: () => null,
toValue: () => null,
fromValue: () => null,
};

const undefinedType = {
Expand All @@ -85,31 +94,41 @@ const undefinedType = {
toJSON: () => null,
fromJSON: () => undefined,
empty: () => undefined,
toValue: () => undefined,
fromValue: () => undefined,
};

let primitiveTypes = new Set(['number', 'string', 'null']);

function EmptyNull<Field>(): GenericProvableExtended<null, null, Field> &
GenericProvablePure<null, Field> {
function EmptyNull<Field>(): GenericProvableExtendedPure<
null,
null,
null,
Field
> {
return emptyType;
}
function EmptyUndefined<Field>(): GenericProvableExtended<
function EmptyUndefined<Field>(): GenericProvableExtendedPure<
undefined,
undefined,
null,
Field
> &
GenericProvablePure<undefined, Field> {
> {
return undefinedType;
}
function EmptyVoid<Field>(): GenericProvableExtended<void, null, Field> &
GenericProvablePure<void, Field> {
function EmptyVoid<Field>(): GenericProvableExtendedPure<
void,
void,
null,
Field
> {
return undefinedType;
}

type PrimitiveTypeMap<Field> = {
number: GenericProvableExtended<number, number, Field>;
string: GenericProvableExtended<string, string, Field>;
null: GenericProvableExtended<null, null, Field>;
number: GenericProvableExtended<number, number, number, Field>;
string: GenericProvableExtended<string, string, string, Field>;
null: GenericProvableExtended<null, null, null, Field>;
};

const primitiveTypeMap: PrimitiveTypeMap<any> = {
Expand All @@ -120,6 +139,8 @@ const primitiveTypeMap: PrimitiveTypeMap<any> = {
fromJSON: (value) => value,
fromFields: (_, [value]) => value,
empty: () => 0,
toValue: (value) => value,
fromValue: (value) => value,
},
string: {
...emptyType,
Expand All @@ -128,6 +149,8 @@ const primitiveTypeMap: PrimitiveTypeMap<any> = {
fromJSON: (value) => value,
fromFields: (_, [value]) => value,
empty: () => '',
toValue: (value) => value,
fromValue: (value) => value,
},
null: emptyType,
};
Loading
Loading