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

Move Field to JS #902

Merged
merged 55 commits into from
May 31, 2023
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
f3e42e2
Merge branch 'refactor/js-circuit' into refactor/field
mitschabaude May 8, 2023
50c3f71
fixup
mitschabaude May 8, 2023
625b04b
start on new field class
mitschabaude May 9, 2023
864dbb1
simplify
mitschabaude May 9, 2023
722ea52
fixup class redefinition
mitschabaude May 9, 2023
dae7cad
type internal field
mitschabaude May 9, 2023
670af90
implement all field methods in the constant case
mitschabaude May 9, 2023
3c7ff65
import Field from new file and fix some type errors
mitschabaude May 10, 2023
c1c0de4
temporarily switch back to ocaml FIeld
mitschabaude May 10, 2023
c7eb3a9
assertion messages
mitschabaude May 10, 2023
3cc7b20
accept field like inputs
mitschabaude May 10, 2023
bef33b8
simpler & better field constructor
mitschabaude May 10, 2023
2351566
some fixes
mitschabaude May 10, 2023
5cf185b
also check if y is constant
mitschabaude May 10, 2023
abfe250
implement variable field ops with original field
mitschabaude May 10, 2023
4a1729a
make all unit test work
mitschabaude May 10, 2023
2e8b443
Merge branch 'main' into refactor/field
mitschabaude May 10, 2023
28d23c3
low level snarky field
mitschabaude May 11, 2023
9330630
start using lower-level snarky field ops
mitschabaude May 11, 2023
72bd575
bump target bc TS messes up private class fields
mitschabaude May 11, 2023
b9f6b46
equals
mitschabaude May 11, 2023
4f2dd19
lower level exists, improve internal API
mitschabaude May 15, 2023
455f2ca
implement remaining Field API
mitschabaude May 15, 2023
91a4cfe
copy over doccomments
mitschabaude May 15, 2023
f2a80a6
bindings
mitschabaude May 22, 2023
17500a4
work around esbuild bug for web version
mitschabaude May 22, 2023
331b548
minor
mitschabaude May 22, 2023
70d740f
fix test expectations about assertEquals error
mitschabaude May 22, 2023
3a063f7
fix constructor mismatch errors
mitschabaude May 22, 2023
73e9302
move field class decl to top level to make ts output less weird
mitschabaude May 22, 2023
d81cfe7
fix voting test
mitschabaude May 22, 2023
87f55e4
fix field unit tests
mitschabaude May 22, 2023
8237a3a
unrelated
mitschabaude May 22, 2023
b0eadbf
match gates of empty contract
mitschabaude May 22, 2023
e3ae8fc
full constraint equivalence w/ old version
mitschabaude May 23, 2023
d7fb7bd
bindings
mitschabaude May 23, 2023
51cef3a
Merge branch 'main' into refactor/field
mitschabaude May 23, 2023
09e2567
revert some unnecessary changes
mitschabaude May 23, 2023
df66994
replace assertNonZero w/ more useful assertNotEquals
mitschabaude May 23, 2023
d60b958
minor
mitschabaude May 23, 2023
ca3a822
add tests
mitschabaude May 23, 2023
d55e889
Merge branch 'main' into refactor/field
mitschabaude May 23, 2023
71308e7
fix field test
mitschabaude May 23, 2023
365d71a
also test methods in provable code
mitschabaude May 23, 2023
23900c6
break out lower level provable runners from provable.ts
mitschabaude May 24, 2023
cc2d5b9
error in compare, some cleanup
mitschabaude May 24, 2023
a56fa81
changelog
mitschabaude May 24, 2023
2736eee
Merge branch 'main' into refactor/field
mitschabaude May 24, 2023
4cab7c8
Merge branch 'main' into refactor/field
mitschabaude May 25, 2023
d220950
merge field tests
mitschabaude May 25, 2023
dae4a08
comment explaining vars vs constants
mitschabaude May 25, 2023
b58938a
move over new doccomments
mitschabaude May 26, 2023
5a11762
doccomment
mitschabaude May 26, 2023
8eebafe
address feedback
mitschabaude May 31, 2023
d6b7f5b
Merge branch 'main' into refactor/field
mitschabaude May 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,36 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Breaking changes

- Rewrite of `Provable.if()` causes breaking changes to all deployed contracts https://github.com/o1-labs/snarkyjs/pull/889
- Remove all deprecated methods and properties on `Field` https://github.com/o1-labs/snarkyjs/pull/902
- The `Field(x)` constructor and other Field methods no longer accept a `boolean` as input. Instead, you can now pass in a `bigint` to all Field methods. https://github.com/o1-labs/snarkyjs/pull/902

### Added

- Add `field.assertNotEquals()` to assert that a field element does not equal some value https://github.com/o1-labs/snarkyjs/pull/902
- More efficient than `field.equals(x).assertFalse()`

### Changed

- **Make stack traces more readable** https://github.com/o1-labs/snarkyjs/pull/890
- Stack traces thrown from SnarkyJS are cleaned up by filtering out unnecessary lines and other noisy details
- Remove optional `zkappKey` argument in `smartContract.init()`, and instead assert that `provedState` is false when `init()` is called https://github.com/o1-labs/snarkyjs/pull/908
- Improve assertion error messages on `Field` methods https://github.com/o1-labs/snarkyjs/issues/743 https://github.com/o1-labs/snarkyjs/pull/902
- Publicly expose the internal details of the `Field` type https://github.com/o1-labs/snarkyjs/pull/902

### Deprecated

- Utility methods on `Circuit` are deprecated in favor of the same methods on `Provable` https://github.com/o1-labs/snarkyjs/pull/889
- `Circuit.if()`, `Circuit.witness()`, `Circuit.log()` and others replaced by `Provable.if()`, `Provable.witness()`, `Provable.log()`
- Under the hood, some of these methods were rewritten in TypeScript
- Deprecate `field.isZero()` https://github.com/o1-labs/snarkyjs/pull/902

### Fixed

- Fix running SnarkyJS in Node.js on Windows https://github.com/o1-labs/snarkyjs-bindings/pull/19 (@wizicer)[https://github.com/wizicer]
- Fix running SnarkyJS in Node.js on Windows https://github.com/o1-labs/snarkyjs-bindings/pull/19 [@wizicer](https://github.com/wizicer)
- Fix `field.greaterThan()` and other comparison methods outside provable code https://github.com/o1-labs/snarkyjs/issues/858 https://github.com/o1-labs/snarkyjs/pull/902
- Fix `field.assertBool()` https://github.com/o1-labs/snarkyjs/issues/469 https://github.com/o1-labs/snarkyjs/pull/902
- Fix `Field(bigint)` where `bigint` is larger than the field modulus https://github.com/o1-labs/snarkyjs/issues/432 https://github.com/o1-labs/snarkyjs/pull/902
- The new behaviour is to use the modular residual of the input

## [0.10.1](https://github.com/o1-labs/snarkyjs/compare/bcc666f2...a632313a)

Expand Down
2 changes: 1 addition & 1 deletion src/bindings
6 changes: 3 additions & 3 deletions src/examples/zkapps/hello_world/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ try {
await txn.prove();
await txn.sign([feePayer1.privateKey]).send();
} catch (err: any) {
handleError(err, 'assert_equal');
handleError(err, 'assertEquals');
}

if (!correctlyFails) {
Expand All @@ -120,7 +120,7 @@ try {
await txn.prove();
await txn.sign([feePayer1.privateKey]).send();
} catch (err: any) {
handleError(err, 'assert_equal');
handleError(err, 'assertEquals');
}

if (!correctlyFails) {
Expand Down Expand Up @@ -176,7 +176,7 @@ try {
await txn4.prove();
await txn4.sign([feePayer4.privateKey]).send();
} catch (err: any) {
handleError(err, 'assert_equal');
handleError(err, 'assertEquals');
}

if (!correctlyFails) {
Expand Down
41 changes: 19 additions & 22 deletions src/examples/zkapps/voting/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,10 +300,10 @@ export async function testSet(
}
}

if (sequenceOverflowSet.voterContract.reducer.getActions({}).length < 3) {
if (sequenceOverflowSet.voterContract.reducer.getActions().length < 3) {
throw Error(
`Did not emit expected actions! Only emitted ${
sequenceOverflowSet.voterContract.reducer.getActions({}).length
sequenceOverflowSet.voterContract.reducer.getActions().length
}`
);
}
Expand Down Expand Up @@ -387,7 +387,7 @@ export async function testSet(
feePayer
);

if (voterContract.reducer.getActions({}).length !== 1) {
if (voterContract.reducer.getActions().length !== 1) {
throw Error(
'Should have emitted 1 event after registering only one valid voter'
);
Expand Down Expand Up @@ -468,10 +468,10 @@ export async function testSet(
voting.voterRegistration(newVoter1);
},
feePayer,
'assert_equal'
'Member already exists!'
);
Comment on lines 470 to 472
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some changes to errors required us to update some of the tests


if (voterContract.reducer.getActions({}).length !== 1) {
if (voterContract.reducer.getActions().length !== 1) {
throw Error(
'Should have emitted 1 event after registering only one valid voter'
);
Expand Down Expand Up @@ -540,8 +540,8 @@ export async function testSet(
feePayer
);

let numberOfEvents = candidateContract.reducer.getActions({}).length;
if (candidateContract.reducer.getActions({}).length !== 2) {
let numberOfEvents = candidateContract.reducer.getActions().length;
if (candidateContract.reducer.getActions().length !== 2) {
throw Error(
`Should have emitted 2 event after registering 2 candidates. ${numberOfEvents} emitted`
);
Expand Down Expand Up @@ -636,10 +636,8 @@ export async function testSet(
);
Local.setGlobalSlot(params.electionPreconditions.startElection.add(1));

let previousEventsVoter = voterContract.reducer.getActions({}).length;
let previousEventsCandidate = candidateContract.reducer.getActions(
{}
).length;
let previousEventsVoter = voterContract.reducer.getActions().length;
let previousEventsCandidate = candidateContract.reducer.getActions().length;

let lateCandidate = Member.from(
PrivateKey.random().toPublicKey(),
Expand All @@ -654,7 +652,7 @@ export async function testSet(
voting.candidateRegistration(lateCandidate);
},
feePayer,
'assert_equal'
'Outside of election period!'
);

console.log(
Expand All @@ -674,15 +672,14 @@ export async function testSet(
voting.voterRegistration(lateVoter);
},
feePayer,
'assert_equal'
'Outside of election period!'
);

if (previousEventsVoter !== voterContract.reducer.getActions({}).length) {
if (previousEventsVoter !== voterContract.reducer.getActions().length) {
throw Error('events emitted but should not have been');
}
if (
previousEventsCandidate !==
candidateContract.reducer.getActions({}).length
previousEventsCandidate !== candidateContract.reducer.getActions().length
) {
throw Error('events emitted but should not have been');
}
Expand Down Expand Up @@ -784,7 +781,7 @@ export async function testSet(

vote(0n, votesStore, candidatesStore);

numberOfEvents = voting.reducer.getActions({}).length;
numberOfEvents = voting.reducer.getActions().length;
if (numberOfEvents !== 1) {
throw Error('Should have emitted 1 event after voting for a candidate');
}
Expand Down Expand Up @@ -825,7 +822,7 @@ export async function testSet(
voting.vote(fakeCandidate, votersStore.get(0n)!);
},
feePayer,
'assert_equal'
'Member is not a candidate!'
);

console.log('unregistered voter attempting to vote');
Expand All @@ -842,7 +839,7 @@ export async function testSet(
voting.vote(fakeVoter, votersStore.get(0n)!);
},
feePayer,
'assert_equal'
'Member is not a candidate!'
);

console.log('attempting to vote for voter...');
Expand All @@ -854,7 +851,7 @@ export async function testSet(
voting.vote(voter, votersStore.get(0n)!);
},
feePayer,
'assert_equal'
'Member is not a candidate!'
);

/*
Expand Down Expand Up @@ -934,7 +931,7 @@ export async function testSet(
voting.voterRegistration(voter);
},
feePayer,
'assert_equal'
'Outside of election period!'
);

console.log('attempting to register candidate after election has ended');
Expand All @@ -951,7 +948,7 @@ export async function testSet(
voting.candidateRegistration(candidate);
},
feePayer,
'assert_equal'
'Outside of election period!'
);
}

Expand Down
4 changes: 2 additions & 2 deletions src/examples/zkapps/voting/voting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export class Voting_ extends SmartContract {
// the check happens here because we want to see if the other contract returns a value
// if exists is true, that means the member already exists within the accumulated state
// if its false, its a new entry
exists.assertEquals(false);
exists.assertFalse('Member already exists!');
}

/**
Expand All @@ -175,7 +175,7 @@ export class Voting_ extends SmartContract {
electionPreconditions.enforce,
currentSlot.lessThanOrEqual(electionPreconditions.startElection),
Bool(true)
).assertTrue();
).assertTrue('Outside of election period!');

// can only register candidates if their balance is gte the minimum amount required
// and lte the maximum amount
Expand Down
8 changes: 0 additions & 8 deletions src/lib/account_update.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
isReady,
Ledger,
Circuit,
AccountUpdate,
PrivateKey,
shutdown,
Expand Down Expand Up @@ -56,7 +55,6 @@ describe('AccountUpdate', () => {
// TODO remove restriction "This function can't be run outside of a checked computation."
Provable.runAndCheck(() => {
let hash = accountUpdate.hash();
expect(isField(hash)).toBeTruthy();

// if we clone the accountUpdate, hash should be the same
let accountUpdate2 = AccountUpdate.clone(accountUpdate);
Expand Down Expand Up @@ -101,9 +99,3 @@ describe('AccountUpdate', () => {
expect(TokenId.fromBase58(defaultTokenId).toString()).toEqual('1');
});
});

// to check that we got something that looks like a Field
// note: `instanceof Field` doesn't work
function isField(x: any) {
return x?.constructor === Field(1).constructor;
}
12 changes: 3 additions & 9 deletions src/lib/account_update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,7 @@ import { UInt64, UInt32, Int64, Sign } from './int.js';
import * as Mina from './mina.js';
import { SmartContract } from './zkapp.js';
import * as Precondition from './precondition.js';
import {
dummyBase64Proof,
Empty,
inCheckedComputation,
Proof,
Prover,
} from './proof_system.js';
import { dummyBase64Proof, Empty, Proof, Prover } from './proof_system.js';
import { Memo } from '../mina-signer/src/memo.js';
import {
Events,
Expand Down Expand Up @@ -1084,7 +1078,7 @@ class AccountUpdate implements Types.AccountUpdate {
// consistency between JS & OCaml hashing on *every single account update
// proof* we create. It will give us 100% confidence that the two
// implementations are equivalent, and catch regressions quickly
if (inCheckedComputation()) {
if (Provable.inCheckedComputation()) {
let input = Types.AccountUpdate.toInput(this);
return hashWithPrefix(prefixes.body, packToFields(input));
} else {
Expand Down Expand Up @@ -1578,7 +1572,7 @@ const CallForest = {
return Provable.witness(Field, () => CallForest.hashChildrenBase(update));
}
let calls = CallForest.hashChildrenBase(update);
if (callsType.type === 'Equals' && inCheckedComputation()) {
if (callsType.type === 'Equals' && Provable.inCheckedComputation()) {
calls.assertEquals(callsType.value);
}
return calls;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/circuit.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { snarkContext } from './proof_system.js';
import { ProvablePure, Snarky } from '../snarky.js';
import { Field } from './core.js';
import { withThreadPool } from '../bindings/js/wrapper.js';
import { Provable, gatesFromJson } from './provable.js';
import { Provable } from './provable.js';
import { snarkContext, gatesFromJson } from './provable-context.js';

// external API
export { public_, circuitMain, Circuit, Keypair, Proof, VerificationKey };
Expand Down
3 changes: 2 additions & 1 deletion src/lib/circuit_value.unit-test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Field, isReady, shutdown } from '../snarky.js';
import { isReady, shutdown } from '../snarky.js';
import { provable, Struct } from './circuit_value.js';
import { UInt32 } from './int.js';
import { PrivateKey, PublicKey } from './signature.js';
Expand All @@ -8,6 +8,7 @@ import { LocalBlockchain, setActiveInstance, transaction } from './mina.js';
import { State, state } from './state.js';
import { AccountUpdate } from './account_update.js';
import { Provable } from './provable.js';
import { Field } from './core.js';

await isReady;

Expand Down
53 changes: 24 additions & 29 deletions src/lib/core.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,39 @@
import { bytesToBigInt } from '../bindings/crypto/bigint-helpers.js';
import { defineBinable } from '../bindings/lib/binable.js';
import { sizeInBits } from '../provable/field-bigint.js';
import { Bool, Field, Scalar, Group } from '../snarky.js';
import { Bool, Scalar, Group } from '../snarky.js';
import { Field as InternalField } from './field.js';
import { Scalar as ScalarBigint } from '../provable/curve-bigint.js';
import { mod } from '../bindings/crypto/finite_field.js';

export { Field, Bool, Scalar, Group };

type InternalConstantField = { value: [0, Uint8Array] };
/**
* An element of a finite field.
*/
const Field = toFunctionConstructor(InternalField);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is where Field is turned from a class into a "class that can also be used as a function", to support Field(x)

type Field = InternalField;

Field.toAuxiliary = () => [];
Bool.toAuxiliary = () => [];
Scalar.toAuxiliary = () => [];
Group.toAuxiliary = () => [];
function toFunctionConstructor<Class extends new (...args: any) => any>(
Class: Class
): Class & ((...args: InferArgs<Class>) => InferReturn<Class>) {
function Constructor(...args: any) {
return new Class(...args);
}
Object.defineProperties(Constructor, Object.getOwnPropertyDescriptors(Class));
return Constructor as any;
}

Field.toInput = function (x) {
return { fields: [x] };
};
type InferArgs<T> = T extends new (...args: infer Args) => any ? Args : never;
type InferReturn<T> = T extends new (...args: any) => infer Return
? Return
: never;

// binable
const FieldBinable = defineBinable({
toBytes(t: Field) {
return [...(t.toConstant() as any as InternalConstantField).value[1]];
},
readBytes(bytes, offset) {
let uint8array = new Uint8Array(32);
uint8array.set(bytes.slice(offset, offset + 32));
return [
Object.assign(Object.create(Field(1).constructor.prototype), {
value: [0, uint8array],
}) as Field,
offset + 32,
];
},
});
// patching ocaml classes

Field.toBytes = FieldBinable.toBytes;
Field.fromBytes = FieldBinable.fromBytes;
Field.readBytes = FieldBinable.readBytes;
Field.sizeInBytes = () => 32;
Bool.toAuxiliary = () => [];
Scalar.toAuxiliary = () => [];
Group.toAuxiliary = () => [];

Bool.toInput = function (x) {
return { packed: [[x.toField(), 1] as [Field, number]] };
Expand Down
3 changes: 2 additions & 1 deletion src/lib/encryption.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Group, Field, Scalar } from '../snarky.js';
import { Group, Scalar } from '../snarky.js';
import { Field } from './core.js';
import { Poseidon } from './hash.js';
import { Provable } from './provable.js';
import { PrivateKey, PublicKey } from './signature.js';
Expand Down
2 changes: 1 addition & 1 deletion src/lib/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'isomorphic-fetch';
import { Field } from '../snarky.js';
import { Field } from './core.js';
import { UInt32, UInt64 } from './int.js';
import { Actions, TokenId } from './account_update.js';
import { PublicKey } from './signature.js';
Expand Down