Skip to content

Commit

Permalink
Merge pull request #1268 from o1-labs/feature/generic-to-value
Browse files Browse the repository at this point in the history
Refactor provable
  • Loading branch information
mitschabaude authored Nov 27, 2023
2 parents 61e164d + c99a923 commit f5db874
Show file tree
Hide file tree
Showing 25 changed files with 147 additions and 114 deletions.
2 changes: 1 addition & 1 deletion src/bindings
55 changes: 34 additions & 21 deletions src/build/jsLayoutToTypes.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -106,19 +106,30 @@ function writeType(typeData, isJson, withTypeMap) {
};
}

function writeTsContent(types, isJson, leavesRelPath) {
function writeTsContent({
jsLayout: types,
isJson,
isProvable,
leavesRelPath,
}) {
let output = '';
let dependencies = new Set();
let converters = {};
let exports = new Set(isJson ? [] : ['customTypes']);

let fromLayout = isProvable ? 'provableFromLayout' : 'signableFromLayout';
let FromLayout = isProvable ? 'ProvableFromLayout' : 'SignableFromLayout';
let GenericType = isProvable ? 'GenericProvableExtended' : 'GenericSignable';
let GeneratedType = isProvable ? 'ProvableExtended' : 'Signable';

for (let [Type, value] of Object.entries(types)) {
let inner = writeType(value, isJson);
exports.add(Type);
mergeSet(dependencies, inner.dependencies);
mergeObject(converters, inner.converters);
output += `type ${Type} = ${inner.output};\n\n`;
if (!isJson) {
output += `let ${Type} = provableFromLayout<${Type}, Json.${Type}>(jsLayout.${Type} as any);\n\n`;
output += `let ${Type} = ${fromLayout}<${Type}, Json.${Type}>(jsLayout.${Type} as any);\n\n`;
}
}

Expand All @@ -135,8 +146,8 @@ function writeTsContent(types, isJson, leavesRelPath) {
import { ${[...imports].join(', ')} } from '${importPath}';
${
!isJson
? "import { GenericProvableExtended } from '../../lib/generic.js';\n" +
"import { ProvableFromLayout, GenericLayout } from '../../lib/from-layout.js';\n" +
? `import { ${GenericType} } from '../../lib/generic.js';\n` +
`import { ${FromLayout}, GenericLayout } from '../../lib/from-layout.js';\n` +
"import * as Json from './transaction-json.js';\n" +
"import { jsLayout } from './js-layout.js';\n"
: ''
Expand All @@ -147,7 +158,7 @@ ${
!isJson
? 'export { Json };\n' +
`export * from '${leavesRelPath}';\n` +
'export { provableFromLayout, toJSONEssential, emptyValue, Layout, TypeMap };\n'
`export { ${fromLayout}, toJSONEssential, empty, Layout, TypeMap };\n`
: `export * from '${leavesRelPath}';\n` + 'export { TypeMap };\n'
}
Expand All @@ -158,7 +169,7 @@ ${
(!isJson || '') &&
`
const TypeMap: {
[K in keyof TypeMap]: ProvableExtended<TypeMap[K], Json.TypeMap[K]>;
[K in keyof TypeMap]: ${GeneratedType}<TypeMap[K], Json.TypeMap[K]>;
} = {
${[...typeMapKeys].join(', ')}
}
Expand All @@ -168,14 +179,14 @@ const TypeMap: {
${
(!isJson || '') &&
`
type ProvableExtended<T, TJson> = GenericProvableExtended<T, TJson, Field>;
type ${GeneratedType}<T, TJson> = ${GenericType}<T, TJson, Field>;
type Layout = GenericLayout<TypeMap>;
type CustomTypes = { ${customTypes
.map((c) => `${c.typeName}: ProvableExtended<${c.type}, ${c.jsonType}>;`)
.map((c) => `${c.typeName}: ${GeneratedType}<${c.type}, ${c.jsonType}>;`)
.join(' ')} }
let customTypes: CustomTypes = { ${customTypeNames.join(', ')} };
let { provableFromLayout, toJSONEssential, emptyValue } = ProvableFromLayout<
let { ${fromLayout}, toJSONEssential, empty } = ${FromLayout}<
TypeMap,
Json.TypeMap
>(TypeMap, customTypes);
Expand All @@ -196,25 +207,27 @@ async function writeTsFile(content, relPath) {
let genPath = '../../bindings/mina-transaction/gen';
await ensureDir(genPath);

let jsonTypesContent = writeTsContent(
let jsonTypesContent = writeTsContent({
jsLayout,
true,
'../transaction-leaves-json.js'
);
isJson: true,
leavesRelPath: '../transaction-leaves-json.js',
});
await writeTsFile(jsonTypesContent, `${genPath}/transaction-json.ts`);

let jsTypesContent = writeTsContent(
let jsTypesContent = writeTsContent({
jsLayout,
false,
'../transaction-leaves.js'
);
isJson: false,
isProvable: true,
leavesRelPath: '../transaction-leaves.js',
});
await writeTsFile(jsTypesContent, `${genPath}/transaction.ts`);

jsTypesContent = writeTsContent(
jsTypesContent = writeTsContent({
jsLayout,
false,
'../transaction-leaves-bigint.js'
);
isJson: false,
isProvable: false,
leavesRelPath: '../transaction-leaves-bigint.js',
});
await writeTsFile(jsTypesContent, `${genPath}/transaction-bigint.ts`);

await writeTsFile(
Expand Down
4 changes: 2 additions & 2 deletions src/lib/account_update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ const Body = {
tokenId?: Field,
mayUseToken?: MayUseToken
): Body {
let { body } = Types.AccountUpdate.emptyValue();
let { body } = Types.AccountUpdate.empty();
body.publicKey = publicKey;
if (tokenId) {
body.tokenId = tokenId;
Expand All @@ -463,7 +463,7 @@ const Body = {
},

dummy(): Body {
return Types.AccountUpdate.emptyValue().body;
return Types.AccountUpdate.empty().body;
},
};

Expand Down
8 changes: 5 additions & 3 deletions src/lib/bool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,10 @@ class Bool {
return 1;
}

static empty() {
return new Bool(false);
}

static toInput(x: Bool): { packed: [Field, number][] } {
return { packed: [[x.toField(), 1] as [Field, number]] };
}
Expand All @@ -314,9 +318,7 @@ class Bool {
return BoolBinable.readBytes(bytes, offset);
}

static sizeInBytes() {
return 1;
}
static sizeInBytes = 1;

static check(x: Bool): void {
Snarky.field.assertBoolean(x.value);
Expand Down
19 changes: 19 additions & 0 deletions src/lib/circuit_value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type ProvableExtension<T, TJson = any> = {
toInput: (x: T) => { fields?: Field[]; packed?: [Field, number][] };
toJSON: (x: T) => TJson;
fromJSON: (x: TJson) => T;
empty: () => T;
};

type ProvableExtended<T, TJson = any> = Provable<T> &
Expand Down Expand Up @@ -246,6 +247,15 @@ abstract class CircuitValue {
}
return Object.assign(Object.create(this.prototype), props);
}

static empty<T extends AnyConstructor>(): InstanceType<T> {
const fields: [string, any][] = (this as any).prototype._fields ?? [];
let props: any = {};
fields.forEach(([key, propType]) => {
props[key] = propType.empty();
});
return Object.assign(Object.create(this.prototype), props);
}
}

function prop(this: any, target: any, key: string) {
Expand Down Expand Up @@ -431,6 +441,15 @@ function Struct<
let struct = Object.create(this.prototype);
return Object.assign(struct, value);
}
/**
* Create an instance of this struct filled with default values
* @returns an empty instance of this struct
*/
static empty(): T {
let value = this.type.empty();
let struct = Object.create(this.prototype);
return Object.assign(struct, value);
}
/**
* This method is for internal use, you will probably not need it.
* Method to make assertions which should be always made whenever a struct of this type is created in a proof.
Expand Down
20 changes: 9 additions & 11 deletions src/lib/events.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { prefixes } from '../bindings/crypto/constants.js';
import { prefixToField } from '../bindings/lib/binable.js';
import {
GenericField,
GenericProvableExtended,
GenericSignableField,
} from '../bindings/lib/generic.js';

export { createEvents, dataAsHash };
Expand All @@ -15,7 +15,7 @@ function createEvents<Field>({
Field,
Poseidon,
}: {
Field: GenericField<Field>;
Field: GenericSignableField<Field>;
Poseidon: Poseidon<Field>;
}) {
type Event = Field[];
Expand Down Expand Up @@ -60,7 +60,7 @@ function createEvents<Field>({
const EventsProvable = {
...Events,
...dataAsHash({
emptyValue: Events.empty,
empty: Events.empty,
toJSON(data: Field[][]) {
return data.map((row) => row.map((e) => Field.toJSON(e)));
},
Expand Down Expand Up @@ -107,7 +107,7 @@ function createEvents<Field>({
const SequenceEventsProvable = {
...Actions,
...dataAsHash({
emptyValue: Actions.empty,
empty: Actions.empty,
toJSON(data: Field[][]) {
return data.map((row) => row.map((e) => Field.toJSON(e)));
},
Expand All @@ -123,26 +123,24 @@ function createEvents<Field>({
}

function dataAsHash<T, J, Field>({
emptyValue,
empty,
toJSON,
fromJSON,
}: {
emptyValue: () => { data: T; hash: Field };
empty: () => { data: T; hash: Field };
toJSON: (value: T) => J;
fromJSON: (json: J) => { data: T; hash: Field };
}): GenericProvableExtended<{ data: T; hash: Field }, J, Field> & {
emptyValue(): { data: T; hash: Field };
} {
}): GenericProvableExtended<{ data: T; hash: Field }, J, Field> {
return {
emptyValue,
empty,
sizeInFields() {
return 1;
},
toFields({ hash }) {
return [hash];
},
toAuxiliary(value) {
return [value?.data ?? emptyValue().data];
return [value?.data ?? empty().data];
},
fromFields([hash], [data]) {
return { data, hash };
Expand Down
24 changes: 8 additions & 16 deletions src/lib/field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1148,6 +1148,10 @@ class Field {

// ProvableExtended<Field>

static empty() {
return new Field(0n);
}

/**
* Serialize the {@link Field} to a JSON string, e.g. for printing. Trying to print a {@link Field} without this function will directly stringify the Field object, resulting in unreadable output.
*
Expand Down Expand Up @@ -1253,26 +1257,14 @@ class Field {
}

/**
* **Warning**: This function is mainly for internal use. Normally it is not intended to be used by a zkApp developer.
*
* As all {@link Field} elements have 32 bytes, this function returns 32.
*
* @return The size of a {@link Field} element - 32.
* The size of a {@link Field} element in bytes - 32.
*/
static sizeInBytes() {
return Fp.sizeInBytes();
}
static sizeInBytes = Fp.sizeInBytes;

/**
* **Warning**: This function is mainly for internal use. Normally it is not intended to be used by a zkApp developer.
*
* As all {@link Field} elements have 255 bits, this function returns 255.
*
* @return The size of a {@link Field} element in bits - 255.
* The size of a {@link Field} element in bits - 255.
*/
static sizeInBits() {
return Fp.sizeInBits;
}
static sizeInBits = Fp.sizeInBits;
}

const FieldBinable = defineBinable({
Expand Down
8 changes: 4 additions & 4 deletions src/lib/gadgets/bitwise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ function not(a: Field, length: number, checked: boolean = false) {

// Check that length does not exceed maximum field size in bits
assert(
length < Field.sizeInBits(),
`Length ${length} exceeds maximum of ${Field.sizeInBits()} bits.`
length < Field.sizeInBits,
`Length ${length} exceeds maximum of ${Field.sizeInBits} bits.`
);

// obtain pad length until the length is a multiple of 16 for n-bit length lookup table
Expand Down Expand Up @@ -156,8 +156,8 @@ function and(a: Field, b: Field, length: number) {

// check that length does not exceed maximum field size in bits
assert(
length <= Field.sizeInBits(),
`Length ${length} exceeds maximum of ${Field.sizeInBits()} bits.`
length <= Field.sizeInBits,
`Length ${length} exceeds maximum of ${Field.sizeInBits} bits.`
);

// obtain pad length until the length is a multiple of 16 for n-bit length lookup table
Expand Down
4 changes: 2 additions & 2 deletions src/lib/hash-generic.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GenericField } from '../bindings/lib/generic.js';
import { GenericSignableField } from '../bindings/lib/generic.js';
import { prefixToField } from '../bindings/lib/binable.js';

export { createHashHelpers, HashHelpers };
Expand All @@ -11,7 +11,7 @@ type Hash<Field> = {
type HashHelpers<Field> = ReturnType<typeof createHashHelpers<Field>>;

function createHashHelpers<Field>(
Field: GenericField<Field>,
Field: GenericSignableField<Field>,
Hash: Hash<Field>
) {
function salt(prefix: string) {
Expand Down
3 changes: 3 additions & 0 deletions src/lib/hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ const TokenSymbolPure: ProvableExtended<
toInput({ field }) {
return { packed: [[field, 48]] };
},
empty() {
return { symbol: '', field: Field(0n) };
},
};
class TokenSymbol extends Struct(TokenSymbolPure) {
static get empty() {
Expand Down
4 changes: 2 additions & 2 deletions src/lib/int.ts
Original file line number Diff line number Diff line change
Expand Up @@ -723,8 +723,8 @@ class Sign extends CircuitValue {
// x^2 === 1 <=> x === 1 or x === -1
x.value.square().assertEquals(Field(1));
}
static emptyValue(): Sign {
return Sign.one;
static empty<T extends AnyConstructor>(): InstanceType<T> {
return Sign.one as any;
}
static toInput(x: Sign): HashInput {
return { packed: [[x.isPositive().toField(), 1]] };
Expand Down
2 changes: 1 addition & 1 deletion src/lib/mina.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1240,7 +1240,7 @@ function getProofsEnabled() {
}

function dummyAccount(pubkey?: PublicKey): Account {
let dummy = Types.Account.emptyValue();
let dummy = Types.Account.empty();
if (pubkey) dummy.publicKey = pubkey;
return dummy;
}
Expand Down
Loading

0 comments on commit f5db874

Please sign in to comment.