Skip to content

Commit

Permalink
Merge pull request #1271 from o1-labs/feature/to-value
Browse files Browse the repository at this point in the history
Encode how to map provables to normal values
  • Loading branch information
mitschabaude committed Apr 19, 2024
2 parents 4de0a3b + 5fce355 commit f9286db
Show file tree
Hide file tree
Showing 48 changed files with 674 additions and 206 deletions.
75 changes: 54 additions & 21 deletions src/build/js-layout-to-types.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ let builtinLeafTypes = new Set([
]);
let indent = '';

function writeType(typeData, isJson, withTypeMap) {
function writeType(typeData, isValue, isJson, withTypeMap) {
let converters = {};
if (!isJson && typeData.checkedType) {
if (!(isJson || isValue) && typeData.checkedType) {
let name = typeData.checkedTypeName;
converters[name] = {
typeName: name,
type: writeType(typeData.checkedType, false, withTypeMap).output,
jsonType: writeType(typeData, true, true).output,
type: writeType(typeData.checkedType, false, false, withTypeMap).output,
valueType: writeType(typeData.checkedType, true, false, true).output,
jsonType: writeType(typeData, false, true, true).output,
};
typeData = typeData.checkedType;
}
Expand All @@ -37,7 +38,7 @@ function writeType(typeData, isJson, withTypeMap) {
output,
dependencies,
converters: j,
} = writeType(inner, isJson, withTypeMap);
} = writeType(inner, isValue, isJson, withTypeMap);
mergeObject(converters, j);
return {
output: `${output}[]`,
Expand All @@ -50,7 +51,7 @@ function writeType(typeData, isJson, withTypeMap) {
output,
dependencies,
converters: j,
} = writeType(inner, isJson, withTypeMap);
} = writeType(inner, isValue, isJson, withTypeMap);
if (optionType === 'flaggedOption' || optionType === 'closedInterval') {
dependencies ??= new Set();
dependencies.add('Bool');
Expand Down Expand Up @@ -84,7 +85,7 @@ function writeType(typeData, isJson, withTypeMap) {
value = value.inner;
questionMark = '?';
}
let inner = writeType(value, isJson, withTypeMap);
let inner = writeType(value, isValue, isJson, withTypeMap);
mergeSet(dependencies, inner.dependencies);
mergeObject(converters, inner.converters);
output += indent + `${key}${questionMark}: ${inner.output};\n`;
Expand All @@ -93,8 +94,8 @@ function writeType(typeData, isJson, withTypeMap) {
output += indent + '}';
return { output, dependencies, converters };
}
if (withTypeMap & !builtinLeafTypes.has(type)) {
type = `${isJson ? 'Json.' : ''}TypeMap["${type}"]`;
if (withTypeMap && !builtinLeafTypes.has(type)) {
type = `${isJson ? 'Json.' : isValue ? 'Value.' : ''}TypeMap["${type}"]`;
}
// built in type
if (builtinLeafTypes.has(type)) return { output: type, converters };
Expand All @@ -119,17 +120,25 @@ function writeTsContent({

let fromLayout = isProvable ? 'provableFromLayout' : 'signableFromLayout';
let FromLayout = isProvable ? 'ProvableFromLayout' : 'SignableFromLayout';
let Type = (Name, T, TValue, ...args) =>
`${Name}<${(isProvable ? [T, TValue, ...args] : [T, ...args]).join(', ')}>`;

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

Expand All @@ -149,6 +158,9 @@ ${
? `import { ${GenericType} } from '../../lib/generic.js';\n` +
`import { ${FromLayout}, GenericLayout } from '../../lib/from-layout.js';\n` +
"import * as Json from './transaction-json.js';\n" +
(isProvable
? "import * as Value from './transaction-bigint.js';\n"
: '') +
"import { jsLayout } from './js-layout.js';\n"
: ''
}
Expand All @@ -169,7 +181,12 @@ ${
(!isJson || '') &&
`
const TypeMap: {
[K in keyof TypeMap]: ${GeneratedType}<TypeMap[K], Json.TypeMap[K]>;
[K in keyof TypeMap]: ${Type(
GeneratedType,
'TypeMap[K]',
'Value.TypeMap[K]',
'Json.TypeMap[K]'
)};
} = {
${[...typeMapKeys].join(', ')}
}
Expand All @@ -179,17 +196,33 @@ const TypeMap: {
${
(!isJson || '') &&
`
type ${GeneratedType}<T, TJson> = ${GenericType}<T, TJson, Field>;
type ${Type(GeneratedType, 'T', 'TValue', 'TJson')} = ${Type(
GenericType,
'T',
'TValue',
'TJson',
'Field'
)};
type Layout = GenericLayout<TypeMap>;
type CustomTypes = { ${customTypes
.map((c) => `${c.typeName}: ${GeneratedType}<${c.type}, ${c.jsonType}>;`)
.map(
(c) =>
`${c.typeName}: ${Type(
GeneratedType,
c.type,
c.valueType,
c.jsonType
)};`
)
.join(' ')} }
let customTypes: CustomTypes = { ${customTypeNames.join(', ')} };
let { ${fromLayout}, toJSONEssential, empty } = ${FromLayout}<
TypeMap,
Json.TypeMap
>(TypeMap, customTypes);
let { ${fromLayout}, toJSONEssential, empty } = ${Type(
FromLayout,
'TypeMap',
'Value.TypeMap',
'Json.TypeMap'
)}(TypeMap, customTypes);
`
}
Expand Down
4 changes: 2 additions & 2 deletions src/examples/constraint-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ let hash = Poseidon.hash([Field(1), Field(-1)]);

let { rows, digest, publicInputSize, print } = await Provable.constraintSystem(
() => {
let x = Provable.witness(Field, () => Field(1));
let y = Provable.witness(Field, () => Field(-1));
let x = Provable.witness(Field, () => 1);
let y = Provable.witness(Field, () => -1);
x.add(y).assertEquals(Field(0));
let z = Poseidon.hash([x, y]);
z.assertEquals(hash);
Expand Down
4 changes: 2 additions & 2 deletions src/examples/zkapps/dex/dex-with-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,13 +258,13 @@ class DexTokenHolder extends SmartContract {

// get total supply of liquidity tokens _before_ applying these actions
// (each redeem action _decreases_ the supply, so we increase it here)
let l = Provable.witness(UInt64, (): UInt64 => {
let l = Provable.witness(UInt64, () => {
let l = dex.totalSupply.get().toBigInt();
// dex.totalSupply.assertNothing();
for (let action of actions.data.get()) {
l += action.element.data.get()[0].element.dl.toBigInt();
}
return UInt64.from(l);
return l;
});

// get our token balance
Expand Down
12 changes: 6 additions & 6 deletions src/examples/zkprogram/gadgets.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Field, Provable, Gadgets, ZkProgram } from 'o1js';

let cs = await Provable.constraintSystem(() => {
let f = Provable.witness(Field, () => Field(12));
let f = Provable.witness(Field, () => 12);

let res1 = Gadgets.rotate64(f, 2, 'left');
let res2 = Gadgets.rotate64(f, 2, 'right');
Expand All @@ -20,7 +20,7 @@ const BitwiseProver = ZkProgram({
rot: {
privateInputs: [],
async method() {
let a = Provable.witness(Field, () => Field(48));
let a = Provable.witness(Field, () => 48);
let actualLeft = Gadgets.rotate64(a, 2, 'left');
let actualRight = Gadgets.rotate64(a, 2, 'right');

Expand All @@ -34,8 +34,8 @@ const BitwiseProver = ZkProgram({
xor: {
privateInputs: [],
async method() {
let a = Provable.witness(Field, () => Field(5));
let b = Provable.witness(Field, () => Field(2));
let a = Provable.witness(Field, () => 5);
let b = Provable.witness(Field, () => 2);
let actual = Gadgets.xor(a, b, 4);
let expected = Field(7);
actual.assertEquals(expected);
Expand All @@ -44,8 +44,8 @@ const BitwiseProver = ZkProgram({
and: {
privateInputs: [],
async method() {
let a = Provable.witness(Field, () => Field(3));
let b = Provable.witness(Field, () => Field(5));
let a = Provable.witness(Field, () => 3);
let b = Provable.witness(Field, () => 5);
let actual = Gadgets.and(a, b, 4);
let expected = Field(1);
actual.assertEquals(expected);
Expand Down
17 changes: 14 additions & 3 deletions src/lib/mina/account-update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { Pickles, Test } from '../../snarky.js';
import { jsLayout } from '../../bindings/mina-transaction/gen/js-layout.js';
import {
Types,
TypesBigint,
toJSONEssential,
} from '../../bindings/mina-transaction/types.js';
import { PrivateKey, PublicKey } from '../provable/crypto/signature.js';
Expand Down Expand Up @@ -82,7 +81,7 @@ import {
} from './smart-contract-context.js';
import { assert } from '../util/assert.js';
import { RandomId } from '../provable/types/auxiliary.js';
import { NetworkId } from '../../mina-signer/src/types.js';
import { From } from '../../bindings/lib/provable-generic.js';

// external API
export {
Expand Down Expand Up @@ -1068,6 +1067,14 @@ class AccountUpdate implements Types.AccountUpdate {
other
);
}
static toValue = Types.AccountUpdate.toValue;
static fromValue(
value: From<typeof Types.AccountUpdate> | AccountUpdate
): AccountUpdate {
if (value instanceof AccountUpdate) return value;
let accountUpdate = Types.AccountUpdate.fromValue(value);
return new AccountUpdate(accountUpdate.body, accountUpdate.authorization);
}

static witness<T>(
type: FlexibleProvable<T>,
Expand Down Expand Up @@ -1810,7 +1817,11 @@ function addMissingSignatures(
): ZkappCommandSigned {
let additionalPublicKeys = privateKeys.map((sk) => sk.toPublicKey());
let { commitment, fullCommitment } = transactionCommitments(
TypesBigint.ZkappCommand.fromJSON(ZkappCommand.toJSON(zkappCommand)),
{
...Types.ZkappCommand.toValue(zkappCommand),
// TODO: represent memo in encoded form already?
memo: Memo.toBase58(Memo.fromString(zkappCommand.memo)),
},
activeInstance.getNetworkId()
);

Expand Down
47 changes: 39 additions & 8 deletions src/lib/mina/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,23 @@ function createEvents<Field>({
};
const EventsProvable = {
...Events,
...dataAsHash({
...dataAsHash<Field[][], bigint[][], string[][], Field>({
empty: Events.empty,
toJSON(data: Field[][]) {
toValue(data) {
return data.map((row) => row.map((e) => Field.toBigint(e)));
},
fromValue(value) {
return value.map((row) => row.map((e) => Field(e)));
},
toJSON(data) {
return data.map((row) => row.map((e) => Field.toJSON(e)));
},
fromJSON(json: string[][]) {
fromJSON(json) {
let data = json.map((row) => row.map((e) => Field.fromJSON(e)));
let hash = Events.hash(data);
return { data, hash };
},
Field,
}),
};

Expand Down Expand Up @@ -104,10 +111,16 @@ function createEvents<Field>({
},
};

const SequenceEventsProvable = {
const ActionsProvable = {
...Actions,
...dataAsHash({
...dataAsHash<Field[][], bigint[][], string[][], Field>({
empty: Actions.empty,
toValue(data) {
return data.map((row) => row.map((e) => Field.toBigint(e)));
},
fromValue(value) {
return value.map((row) => row.map((e) => Field(e)));
},
toJSON(data: Field[][]) {
return data.map((row) => row.map((e) => Field.toJSON(e)));
},
Expand All @@ -116,21 +129,33 @@ function createEvents<Field>({
let hash = Actions.hash(data);
return { data, hash };
},
Field,
}),
};

return { Events: EventsProvable, Actions: SequenceEventsProvable };
return { Events: EventsProvable, Actions: ActionsProvable };
}

function dataAsHash<T, J, Field>({
function dataAsHash<T, V, J, Field>({
empty,
toValue,
fromValue,
toJSON,
fromJSON,
Field,
}: {
empty: () => { data: T; hash: Field };
toValue: (value: T) => V;
fromValue: (value: V | T) => T;
toJSON: (value: T) => J;
fromJSON: (json: J) => { data: T; hash: Field };
}): GenericProvableExtended<{ data: T; hash: Field }, J, Field> {
Field: GenericSignableField<Field>;
}): GenericProvableExtended<
{ data: T; hash: Field },
{ data: V; hash: bigint },
J,
Field
> {
return {
empty,
sizeInFields() {
Expand All @@ -145,6 +170,12 @@ function dataAsHash<T, J, Field>({
fromFields([hash], [data]) {
return { data, hash };
},
toValue({ data, hash }) {
return { data: toValue(data), hash: Field.toBigint(hash) };
},
fromValue({ data, hash }) {
return { data: fromValue(data), hash: Field(hash) };
},
toJSON({ data }) {
return toJSON(data);
},
Expand Down
14 changes: 8 additions & 6 deletions src/lib/mina/hash-input.unit-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,22 @@ type NetworkPrecondition = Body['preconditions']['network'];

// provables
let bodyLayout = jsLayout.AccountUpdate.entries.body;
let Timing = provableFromLayout<Timing, any>(
let Timing = provableFromLayout<Timing, any, any>(
bodyLayout.entries.update.entries.timing.inner as any
);
let Permissions_ = provableFromLayout<Permissions, any>(
let Permissions_ = provableFromLayout<Permissions, any, any>(
bodyLayout.entries.update.entries.permissions.inner as any
);
let Update = provableFromLayout<Update, any>(bodyLayout.entries.update as any);
let AccountPrecondition = provableFromLayout<AccountPrecondition, any>(
let Update = provableFromLayout<Update, any, any>(
bodyLayout.entries.update as any
);
let AccountPrecondition = provableFromLayout<AccountPrecondition, any, any>(
bodyLayout.entries.preconditions.entries.account as any
);
let NetworkPrecondition = provableFromLayout<NetworkPrecondition, any>(
let NetworkPrecondition = provableFromLayout<NetworkPrecondition, any, any>(
bodyLayout.entries.preconditions.entries.network as any
);
let Body = provableFromLayout<Body, any>(bodyLayout as any);
let Body = provableFromLayout<Body, any, any>(bodyLayout as any);

// test with random account updates
test(Random.json.accountUpdate, (accountUpdateJson) => {
Expand Down
Loading

0 comments on commit f9286db

Please sign in to comment.