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

Looking up values in runtime tables #1858

Open
wants to merge 7 commits into
base: v1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased](https://github.com/o1-labs/o1js/compare/f15293a69...HEAD)

### Added

- Configuration of runtime tables https://github.com/o1-labs/o1js/pull/1858

### Fixes

- Decouple offchain state instances from their definitions https://github.com/o1-labs/o1js/pull/1834
Expand Down
24 changes: 23 additions & 1 deletion src/lib/provable/gadgets/gadgets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
} from './foreign-field.js';
import { divMod32, addMod32 } from './arithmetic.js';
import { SHA256 } from './sha256.js';
import { rangeCheck3x12 } from './lookup.js';
import { rangeCheck3x12, inTable } from './lookup.js';
import { arrayGet } from './basic.js';

export { Gadgets, Field3, ForeignFieldSum };
Expand Down Expand Up @@ -547,6 +547,28 @@ const Gadgets = {
return rangeCheck3x12(v0, v1, v2);
},

/**
* In-circuit check that up to 3 pairs of index and value are in the runtime
* table given by the identifier. Each given pair is a tuple composed of a
* bigint and a Field.
*
* Internally, it creates a lookup gate for the three pairs. If fewer pairs are
* given, the remaining pairs are duplicates of the first one.
*
* @param id
* @param pair0
* @param pair1
* @param pair2
*/
inTable(
id: number,
pair0: [bigint, Field],
pair1?: [bigint, Field] | undefined,
pair2?: [bigint, Field] | undefined
) {
return inTable(id, pair0, pair1, pair2);
},

/**
* Gadgets for foreign field operations.
*
Expand Down
33 changes: 32 additions & 1 deletion src/lib/provable/gadgets/lookup.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Field } from '../field.js';
import { Gates } from '../gates.js';

export { rangeCheck3x12 };
export { rangeCheck3x12, inTable };

function rangeCheck3x12(v0: Field, v1: Field, v2: Field) {
// Checks that all three input values exist in the RANGE_CHECK_TABLE (tableId: 1)
Expand All @@ -18,3 +18,34 @@ function rangeCheck3x12(v0: Field, v1: Field, v2: Field) {
Field.from(0)
);
}

/**
* In-circuit check that up to 3 pairs of index and value are in the runtime
* table given by the identifier. Each given pair is a tuple composed of a
* bigint and a Field.
*
* @param id
* @param pair0
* @param pair1
* @param pair2
*/
function inTable(
id: number,
pair0: [bigint, Field],
pair1?: [bigint, Field] | undefined,
pair2?: [bigint, Field] | undefined
) {
let [idx0, v0] = pair0;
let [idx1, v1] = pair1 === undefined ? pair0 : pair1;
let [idx2, v2] = pair2 === undefined ? pair0 : pair2;

Gates.lookup(
Field.from(id),
Field.from(idx0),
v0,
Field.from(idx1),
v1,
Field.from(idx2),
v2
);
}
9 changes: 9 additions & 0 deletions src/lib/provable/gates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export {
foreignFieldAdd,
foreignFieldMul,
KimchiGateType,
addRuntimeTableConfig,
};

export { fieldVar };
Expand All @@ -32,6 +33,7 @@ const Gates = {
foreignFieldAdd,
foreignFieldMul,
raw,
addRuntimeTableConfig,
};

function rangeCheck0(
Expand Down Expand Up @@ -289,6 +291,13 @@ function raw(kind: KimchiGateType, values: Field[], coefficients: bigint[]) {
);
}

function addRuntimeTableConfig(id: number, first_column: bigint[]) {
Snarky.gates.addRuntimeTableConfig(
id,
MlArray.to(first_column.map((x) => FieldConst.fromBigint(x)))
);
}

enum KimchiGateType {
Zero,
Generic,
Expand Down
67 changes: 37 additions & 30 deletions src/lib/provable/test/lookup.unit-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,39 +23,46 @@ let maybeUint = (n: number | bigint): Spec<bigint, Field> => {
);
};

let Lookup = ZkProgram({
name: 'lookup',
methods: {
three12Bit: {
privateInputs: [Field, Field, Field],
async method(v0: Field, v1: Field, v2: Field) {
// Dummy range check to make sure the lookup table is initialized
// It should never fail because 64 > 12
Gadgets.rangeCheck64(v0);
Gadgets.rangeCheck3x12(v0, v1, v2);
// Range-check tests
{
let RangeCheck = ZkProgram({
name: 'range-check',
methods: {
three12Bit: {
privateInputs: [Field, Field, Field],
async method(v0: Field, v1: Field, v2: Field) {
// Dummy range check to make sure the lookup table is initialized
// It should never fail because 64 > 12
Gadgets.rangeCheck64(v0);
Gadgets.rangeCheck3x12(v0, v1, v2);
},
},
},
},
});
});

// constraint system sanity check
// constraint system sanity check

constraintSystem.fromZkProgram(Lookup, 'three12Bit', contains(['Lookup']));
constraintSystem.fromZkProgram(
RangeCheck,
'three12Bit',
contains(['Lookup'])
);

await Lookup.compile();
await RangeCheck.compile();

await equivalentAsync(
{ from: [uint(12), uint(12), maybeUint(12)], to: boolean },
{ runs: 3 }
)(
(x, y, z) => {
assert(x < 1n << 12n);
assert(y < 1n << 12n);
assert(z < 1n << 12n);
return true;
},
async (x, y, z) => {
let proof = await Lookup.three12Bit(x, y, z);
return await Lookup.verify(proof);
}
);
await equivalentAsync(
{ from: [uint(12), uint(12), maybeUint(12)], to: boolean },
{ runs: 3 }
)(
(x, y, z) => {
assert(x < 1n << 12n);
assert(y < 1n << 12n);
assert(z < 1n << 12n);
return true;
},
async (x, y, z) => {
let proof = await RangeCheck.three12Bit(x, y, z);
return await RangeCheck.verify(proof);
}
);
}
Loading