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

Fix events limit checks #758

Merged
merged 5 commits into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- this refers to the slot at which the transaction _will be included in a block_.
- there is only `currentSlot.assertBetween()`; no `currentSlot.get()` (impossible to implement, since the value is determined in the future) and `currentSlot.assertEquals()` (error-prone)

### Fixed

- Incorrect counting of limit on events and actions https://github.com/o1-labs/snarkyjs/pull/758
- Type error when using `Circuit.array` in on-chain state or events https://github.com/o1-labs/snarkyjs/pull/758

## [0.9.1](https://github.com/o1-labs/snarkyjs/compare/71b6132b...9c44b9c2)

### Fixed
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export {
provable,
provablePure,
Struct,
FlexibleProvable,
FlexibleProvablePure,
} from './lib/circuit_value.js';
export { UInt32, UInt64, Int64, Sign } from './lib/int.js';
export { Types } from './provable/types.js';
Expand Down
50 changes: 29 additions & 21 deletions src/lib/circuit_value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export {
provable,
provablePure,
Struct,
FlexibleProvable,
FlexibleProvablePure,
};

// internal API
Expand All @@ -30,11 +32,10 @@ export {
memoizeWitness,
getBlindingValue,
toConstant,
InferCircuitValue,
InferProvable,
HashInput,
FlexibleProvable,
FlexibleProvablePure,
InferJson,
InferredProvable,
};

type Constructor<T> = new (...args: any) => T;
Expand Down Expand Up @@ -262,11 +263,12 @@ function prop(this: any, target: any, key: string) {
}
}

function circuitArray<
A extends FlexibleProvable<any>,
T = InferCircuitValue<A>,
TJson = InferJson<A>
>(elementType: A, length: number): ProvableExtended<T[], TJson[]> {
function circuitArray<A extends FlexibleProvable<any>>(
elementType: A,
length: number
): InferredProvable<A[]> {
type T = InferProvable<A>;
type TJson = InferJson<A>;
let type = elementType as ProvableExtended<T>;
return {
/**
Expand Down Expand Up @@ -343,7 +345,7 @@ function circuitArray<
HashInput.empty
);
},
};
} satisfies ProvableExtended<T[], TJson[]> as any;
}

function arrayProp<T>(elementType: FlexibleProvable<T>, length: number) {
Expand Down Expand Up @@ -472,12 +474,14 @@ type ProvableExtension<T, TJson = any> = {
};
type ProvableExtended<T, TJson = any> = Provable<T> &
ProvableExtension<T, TJson>;
type ProvableExtendedPure<T, TJson = any> = ProvablePure<T> &
ProvableExtension<T, TJson>;

function provable<A>(
typeObj: A,
options?: { customObjectKeys?: string[]; isPure?: boolean }
): ProvableExtended<InferCircuitValue<A>, InferJson<A>> {
type T = InferCircuitValue<A>;
): ProvableExtended<InferProvable<A>, InferJson<A>> {
type T = InferProvable<A>;
type J = InferJson<A>;
let objectKeys =
typeof typeObj === 'object' && typeObj !== null
Expand Down Expand Up @@ -653,8 +657,8 @@ function provable<A>(
function provablePure<A>(
typeObj: A,
options: { customObjectKeys?: string[] } = {}
): ProvablePure<InferCircuitValue<A>> &
ProvableExtension<InferCircuitValue<A>, InferJson<A>> {
): ProvablePure<InferProvable<A>> &
ProvableExtension<InferProvable<A>, InferJson<A>> {
return provable(typeObj, { ...options, isPure: true }) as any;
}

Expand Down Expand Up @@ -730,7 +734,7 @@ function provablePure<A>(
*/
function Struct<
A,
T extends InferCircuitValue<A> = InferCircuitValue<A>,
T extends InferProvable<A> = InferProvable<A>,
J extends InferJson<A> = InferJson<A>,
Pure extends boolean = IsPure<A>
>(
Expand Down Expand Up @@ -1162,27 +1166,27 @@ type InferPrimitiveJson<P extends Primitive> = P extends typeof String
? null
: any;

type InferCircuitValue<A> = A extends Constructor<infer U>
type InferProvable<A> = A extends Constructor<infer U>
? A extends Provable<U>
? U
: A extends Struct<U>
? U
: InferCircuitValueBase<A>
: InferCircuitValueBase<A>;
: InferProvableBase<A>
: InferProvableBase<A>;

type InferCircuitValueBase<A> = A extends Provable<infer U>
type InferProvableBase<A> = A extends Provable<infer U>
? U
: A extends Primitive
? InferPrimitive<A>
: A extends Tuple<any>
? {
[I in keyof A]: InferCircuitValue<A[I]>;
[I in keyof A]: InferProvable<A[I]>;
}
: A extends (infer U)[]
? InferCircuitValue<U>[]
? InferProvable<U>[]
: A extends Record<any, any>
? {
[K in keyof A]: InferCircuitValue<A[K]>;
[K in keyof A]: InferProvable<A[K]>;
}
: never;

Expand Down Expand Up @@ -1219,3 +1223,7 @@ type IsPureBase<A> = A extends ProvablePure<any>
[K in keyof A]: IsPure<A[K]>;
}[keyof A]
: false;

type InferredProvable<A> = IsPure<A> extends true
? ProvableExtendedPure<InferProvable<A>, InferJson<A>>
: ProvableExtended<InferProvable<A>, InferJson<A>>;
27 changes: 14 additions & 13 deletions src/lib/mina.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
CallForest,
Authorization,
SequenceEvents,
Events,
} from './account_update.js';

import * as Fetch from './fetch.js';
Expand Down Expand Up @@ -1198,19 +1199,16 @@ function verifyTransactionLimits(accountUpdates: AccountUpdate[]) {
const costLimit = 69.45;

// constants that define the maximum number of events in one transaction
const maxSequenceEventElements = 16;
const maxActionElements = 16;
const maxEventElements = 16;

let eventElements = {
events: 0,
sequence: 0,
};
let eventElements = { events: 0, actions: 0 };

let authTypes = filterGroups(
accountUpdates.map((update) => {
let json = update.toJSON();
eventElements.events += update.body.events.data.length;
eventElements.sequence += update.body.actions.data.length;
eventElements.events += countEventElements(update.body.events);
eventElements.actions += countEventElements(update.body.actions);
return json.body.authorizationKind;
})
);
Expand All @@ -1230,9 +1228,8 @@ function verifyTransactionLimits(accountUpdates: AccountUpdate[]) {

let isWithinCostLimit = totalTimeRequired < costLimit;

let isWithinEventsLimit = eventElements['events'] <= maxEventElements;
let isWithinSequenceEventsLimit =
eventElements['sequence'] <= maxSequenceEventElements;
let isWithinEventsLimit = eventElements.events <= maxEventElements;
let isWithinActionsLimit = eventElements.actions <= maxActionElements;

let error = '';

Expand All @@ -1247,16 +1244,20 @@ ${JSON.stringify(authTypes)}
}

if (!isWithinEventsLimit) {
error += `Error: The AccountUpdates in your transaction are trying to emit too many events. The maximum allowed amount of events is ${maxEventElements}, but you tried to emit ${eventElements['events']}.\n\n`;
error += `Error: The account updates in your transaction are trying to emit too much event data. The maximum allowed number of field elements in events is ${maxEventElements}, but you tried to emit ${eventElements.events}.\n\n`;
}

if (!isWithinSequenceEventsLimit) {
error += `Error: The AccountUpdates in your transaction are trying to emit too many actions. The maximum allowed amount of actions is ${maxSequenceEventElements}, but you tried to emit ${eventElements['sequence']}.\n\n`;
if (!isWithinActionsLimit) {
error += `Error: The account updates in your transaction are trying to emit too much action data. The maximum allowed number of field elements in actions is ${maxActionElements}, but you tried to emit ${eventElements.actions}.\n\n`;
}

if (error) throw Error('Error during transaction sending:\n\n' + error);
}

function countEventElements({ data }: Events) {
return data.reduce((acc, ev) => acc + ev.length, 0);
}

type AuthorizationKind = { isProved: boolean; isSigned: boolean };

const isPair = (a: AuthorizationKind, b: AuthorizationKind) =>
Expand Down
18 changes: 8 additions & 10 deletions src/lib/proof_system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
import {
FlexibleProvable,
FlexibleProvablePure,
InferCircuitValue,
InferProvable,
provable,
toConstant,
} from './circuit_value.js';
Expand Down Expand Up @@ -92,7 +92,7 @@ class Proof<T> {
proof: proofString,
publicInput: publicInputJson,
}: JsonProof
): Proof<InferCircuitValue<S['publicInputType']>> {
): Proof<InferProvable<S['publicInputType']>> {
let [, proof] = Pickles.proofOfBase64(proofString, maxProofsVerified);
let publicInput = getPublicInputType(this).fromFields(
publicInputJson.map(Field)
Expand Down Expand Up @@ -163,22 +163,20 @@ function ZkProgram<
}: {
publicInput: PublicInputType;
methods: {
[I in keyof Types]: Method<InferCircuitValue<PublicInputType>, Types[I]>;
[I in keyof Types]: Method<InferProvable<PublicInputType>, Types[I]>;
};
}): {
name: string;
compile: () => Promise<{ verificationKey: string }>;
verify: (
proof: Proof<InferCircuitValue<PublicInputType>>
) => Promise<boolean>;
verify: (proof: Proof<InferProvable<PublicInputType>>) => Promise<boolean>;
digest: () => string;
publicInputType: PublicInputType;
} & {
[I in keyof Types]: Prover<InferCircuitValue<PublicInputType>, Types[I]>;
[I in keyof Types]: Prover<InferProvable<PublicInputType>, Types[I]>;
} {
let selfTag = { name: `Program${i++}` };

type PublicInput = InferCircuitValue<PublicInputType>;
type PublicInput = InferProvable<PublicInputType>;
class SelfProof extends Proof<PublicInput> {
static publicInputType = publicInputType;
static tag = () => selfTag;
Expand Down Expand Up @@ -594,7 +592,7 @@ function getPublicInputType<T, P extends Subclass<typeof Proof> = typeof Proof>(
ZkProgram.Proof = function <
PublicInputType extends FlexibleProvablePure<any>
>(program: { name: string; publicInputType: PublicInputType }) {
type PublicInput = InferCircuitValue<PublicInputType>;
type PublicInput = InferProvable<PublicInputType>;
return class ZkProgramProof extends Proof<PublicInput> {
static publicInputType = program.publicInputType;
static tag = () => program;
Expand Down Expand Up @@ -643,7 +641,7 @@ function inCompileMode() {

type Infer<T> = T extends Subclass<typeof Proof>
? InstanceType<T>
: InferCircuitValue<T>;
: InferProvable<T>;

type Tuple<T> = [T, ...T[]] | [];
type TupleToInstances<T> = {
Expand Down
4 changes: 2 additions & 2 deletions src/lib/zkapp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
cloneCircuitValue,
FlexibleProvablePure,
getBlindingValue,
InferCircuitValue,
InferProvable,
memoizationContext,
provable,
Struct,
Expand Down Expand Up @@ -1464,7 +1464,7 @@ function declareMethods<T extends typeof SmartContract>(

const Reducer: (<
T extends FlexibleProvablePure<any>,
A extends InferCircuitValue<T> = InferCircuitValue<T>
A extends InferProvable<T> = InferProvable<T>
>(reducer: {
actionType: T;
}) => ReducerReturn<A>) & {
Expand Down
20 changes: 10 additions & 10 deletions src/provable/provable-generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export { createProvable, createHashInput, ProvableConstructor };
type ProvableConstructor<Field> = <A>(
typeObj: A,
options?: { customObjectKeys?: string[]; isPure?: boolean }
) => GenericProvableExtended<InferCircuitValue<A, Field>, InferJson<A>, Field>;
) => GenericProvableExtended<InferProvable<A, Field>, InferJson<A>, Field>;

function createProvable<Field>(): ProvableConstructor<Field> {
type ProvableExtended<T, TJson = JSONValue> = GenericProvableExtended<
Expand All @@ -26,8 +26,8 @@ function createProvable<Field>(): ProvableConstructor<Field> {
function provable<A>(
typeObj: A,
options?: { customObjectKeys?: string[]; isPure?: boolean }
): ProvableExtended<InferCircuitValue<A, Field>, InferJson<A>> {
type T = InferCircuitValue<A, Field>;
): ProvableExtended<InferProvable<A, Field>, InferJson<A>> {
type T = InferProvable<A, Field>;
type J = InferJson<A>;
let objectKeys =
typeof typeObj === 'object' && typeObj !== null
Expand Down Expand Up @@ -278,25 +278,25 @@ type InferPrimitiveJson<P extends Primitive> = P extends typeof String
? null
: JSONValue;

type InferCircuitValue<A, Field> = A extends Constructor<infer U>
type InferProvable<A, Field> = A extends Constructor<infer U>
? A extends GenericProvable<U, Field>
? U
: InferCircuitValueBase<A, Field>
: InferCircuitValueBase<A, Field>;
: InferProvableBase<A, Field>
: InferProvableBase<A, Field>;

type InferCircuitValueBase<A, Field> = A extends GenericProvable<infer U, Field>
type InferProvableBase<A, Field> = A extends GenericProvable<infer U, Field>
? U
: A extends Primitive
? InferPrimitive<A>
: A extends Tuple<any>
? {
[I in keyof A]: InferCircuitValue<A[I], Field>;
[I in keyof A]: InferProvable<A[I], Field>;
}
: A extends (infer U)[]
? InferCircuitValue<U, Field>[]
? InferProvable<U, Field>[]
: A extends Record<any, any>
? {
[K in keyof A]: InferCircuitValue<A[K], Field>;
[K in keyof A]: InferProvable<A[K], Field>;
}
: never;

Expand Down
6 changes: 2 additions & 4 deletions src/snarky.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import type {
FlexibleProvable,
InferCircuitValue,
InferJson,
ProvableExtended,
InferredProvable,
} from './lib/circuit_value.js';
import type { Account as JsonAccount } from './provable/gen/transaction-json.js';
export {
Expand Down Expand Up @@ -818,7 +816,7 @@ declare class Circuit {
static array<A extends FlexibleProvable<any>>(
elementType: A,
length: number
): ProvableExtended<InferCircuitValue<A>[], InferJson<A>[]>;
): InferredProvable<A[]>;

/**
* Asserts that two values are equal.
Expand Down