o1js
Version:
TypeScript framework for zk-SNARKs and zkApps
766 lines (693 loc) • 20.9 kB
TypeScript
import type { Account as JsonAccount } from './bindings/mina-transaction/gen/transaction-json.js';
import type { Field } from './lib/provable/field.js';
import type {
FieldVar,
FieldConst,
VarFieldVar,
} from './lib/provable/core/fieldvar.ts';
import type { BoolVar } from './lib/provable/bool.ts';
import type { ScalarConst } from './lib/provable/scalar.js';
import type {
MlArray,
MlPair,
MlList,
MlOption,
MlBool,
MlBytes,
MlResult,
MlUnit,
MlString,
MlTuple,
MlArrayOptionalElements,
} from './lib/ml/base.js';
import type { MlHashInput } from './lib/ml/conversion.js';
import type {
SnarkKey,
SnarkKeyHeader,
MlWrapVerificationKey,
} from './lib/proof-system/prover-keys.js';
import type {
WasmFpSrs,
WasmFqSrs,
} from './bindings/compiled/node_bindings/plonk_wasm.cjs';
import * as wasm from './bindings/compiled/node_bindings/plonk_wasm.cjs';
import type { KimchiGateType } from './lib/provable/gates.ts';
import type { MlConstraintSystem } from './lib/provable/core/provable-context.ts';
import type { FieldVector } from './bindings/crypto/bindings/vector.ts';
export { Ledger, Pickles, Gate, GateType, wasm, initializeBindings };
// internal
export {
Snarky,
Test,
WasmModule,
withThreadPool,
JsonGate,
MlPublicKey,
MlPublicKeyVar,
MlFeatureFlags,
};
type WasmModule = typeof wasm;
type MlGroup = MlPair<FieldVar, FieldVar>;
declare namespace Snarky {
type Main = (publicInput: MlArray<FieldVar>) => void;
type Keypair = unknown;
type VerificationKey = unknown;
type Proof = unknown;
}
/**
* Internal interface to snarky-ml
*
* Note for devs: This module is intended to closely mirror snarky-ml's core, low-level APIs.
*/
declare const Snarky: {
/**
* APIs that have to do with running provable code
*/
run: {
/**
* Checks whether Snarky runs in "prover mode", that is, with witnesses
*/
inProver(): MlBool;
/**
* Runs code as a prover.
*/
asProver(f: () => void): void;
/**
* Check whether we are inside an asProver or exists block
*/
inProverBlock(): boolean;
/**
* Setting that controls whether snarky throws an exception on violated constraint.
*/
setEvalConstraints(value: MlBool): void;
/**
* Starts constraint system runner and returns a function to finish it.
*/
enterConstraintSystem(): () => MlConstraintSystem;
/**
* Starts witness generation and returns a function to finish it.
*/
enterGenerateWitness(): () => [
_: 0,
public_inputs: FieldVector,
auxiliary_inputs: FieldVector
];
/**
* Starts an asProver / witness block and returns a function to finish it.
*/
enterAsProver(
size: number
): (fields: MlOption<MlArray<FieldConst>>) => MlArray<VarFieldVar>;
/**
* Operations on snarky's internal state
*/
state: {
allocVar(state: SnarkyState): FieldVar;
storeFieldElt(state: SnarkyState, x: FieldConst): FieldVar;
getVariableValue(state: SnarkyState, x: FieldVar): FieldConst;
asProver(state: SnarkyState): MlBool;
setAsProver(state: SnarkyState, value: MlBool): void;
hasWitness(state: SnarkyState): MlBool;
};
};
/**
* APIs to interact with a `Backend.R1CS_constraint_system.t`
*/
constraintSystem: {
/**
* Returns the number of rows of the constraint system.
*/
rows(system: MlConstraintSystem): number;
/**
* Returns an md5 digest of the constraint system.
*/
digest(system: MlConstraintSystem): string;
/**
* Returns a JSON representation of the constraint system.
*/
toJson(system: MlConstraintSystem): JsonConstraintSystem;
};
/**
* APIs to add constraints on field variables
*/
field: {
/**
* evaluates a CVar by walking the AST and reading Vars from a list of public input + aux values
*/
readVar(x: FieldVar): FieldConst;
/**
* x === y without handling of constants
*/
assertEqual(x: FieldVar, y: FieldVar): void;
/**
* x*y === z without handling of constants
*/
assertMul(x: FieldVar, y: FieldVar, z: FieldVar): void;
/**
* x*x === y without handling of constants
*/
assertSquare(x: FieldVar, y: FieldVar): void;
/**
* x*x === x without handling of constants
*/
assertBoolean(x: FieldVar): void;
/**
* returns x truncated to the lowest `16 * lengthDiv16` bits
* => can be used to assert that x fits in `16 * lengthDiv16` bits.
*
* more efficient than `toBits()` because it uses the EC_endoscalar gate;
* does 16 bits per row (vs 1 bits per row that you can do with generic gates).
*/
truncateToBits16(lengthDiv16: number, x: FieldVar): FieldVar;
};
gates: {
zero(in1: FieldVar, in2: FieldVar, out: FieldVar): void;
generic(
sl: FieldConst,
l: FieldVar,
sr: FieldConst,
r: FieldVar,
so: FieldConst,
o: FieldVar,
sm: FieldConst,
sc: FieldConst
): void;
poseidon(state: MlArray<MlTuple<Field, 3>>): void;
/**
* Low-level Elliptic Curve Addition gate.
*/
ecAdd(
p1: MlGroup,
p2: MlGroup,
p3: MlGroup,
inf: FieldVar,
same_x: FieldVar,
slope: FieldVar,
inf_z: FieldVar,
x21_inv: FieldVar
): MlGroup;
ecScale(
state: MlArray<
[
_: 0,
accs: MlArray<MlTuple<FieldVar, 2>>,
bits: MlArray<FieldVar>,
ss: MlArray<FieldVar>,
base: MlGroup,
nPrev: Field,
nNext: Field
]
>
): void;
ecEndoscale(
state: MlArray<
[
_: 0,
xt: FieldVar,
yt: FieldVar,
xp: FieldVar,
yp: FieldVar,
nAcc: FieldVar,
xr: FieldVar,
yr: FieldVar,
s1: FieldVar,
s3: FieldVar,
b1: FieldVar,
b2: FieldVar,
b3: FieldVar,
b4: FieldVar
]
>,
xs: FieldVar,
ys: FieldVar,
nAcc: FieldVar
): void;
ecEndoscalar(
state: MlArray<
[
_: 0,
n0: FieldVar,
n8: FieldVar,
a0: FieldVar,
b0: FieldVar,
a8: FieldVar,
b8: FieldVar,
x0: FieldVar,
x1: FieldVar,
x2: FieldVar,
x3: FieldVar,
x4: FieldVar,
x5: FieldVar,
x6: FieldVar,
x7: FieldVar
]
>
): void;
lookup(input: MlTuple<FieldVar, 7>): void;
/**
* Range check gate
*
* @param v0 field var to be range checked
* @param v0p bits 16 to 88 as 6 12-bit limbs
* @param v0c bits 0 to 16 as 8 2-bit limbs
* @param compact boolean field elements -- whether to use "compact mode"
*/
rangeCheck0(
v0: FieldVar,
v0p: MlTuple<FieldVar, 6>,
v0c: MlTuple<FieldVar, 8>,
compact: FieldConst
): void;
rangeCheck1(
v2: FieldVar,
v12: FieldVar,
vCurr: MlTuple<FieldVar, 13>,
vNext: MlTuple<FieldVar, 15>
): void;
xor(
in1: FieldVar,
in2: FieldVar,
out: FieldVar,
in1_0: FieldVar,
in1_1: FieldVar,
in1_2: FieldVar,
in1_3: FieldVar,
in2_0: FieldVar,
in2_1: FieldVar,
in2_2: FieldVar,
in2_3: FieldVar,
out_0: FieldVar,
out_1: FieldVar,
out_2: FieldVar,
out_3: FieldVar
): void;
foreignFieldAdd(
left: MlTuple<FieldVar, 3>,
right: MlTuple<FieldVar, 3>,
fieldOverflow: FieldVar,
carry: FieldVar,
foreignFieldModulus: MlTuple<FieldConst, 3>,
sign: FieldConst
): void;
foreignFieldMul(
left: MlTuple<FieldVar, 3>,
right: MlTuple<FieldVar, 3>,
remainder: MlTuple<FieldVar, 2>,
quotient: MlTuple<FieldVar, 3>,
quotientHiBound: FieldVar,
product1: MlTuple<FieldVar, 3>,
carry0: FieldVar,
carry1p: MlTuple<FieldVar, 7>,
carry1c: MlTuple<FieldVar, 4>,
foreignFieldModulus2: FieldConst,
negForeignFieldModulus: MlTuple<FieldConst, 3>
): void;
rotate(
field: FieldVar,
rotated: FieldVar,
excess: FieldVar,
limbs: MlArray<FieldVar>,
crumbs: MlArray<FieldVar>,
two_to_rot: FieldConst
): void;
addFixedLookupTable(id: number, data: MlArray<MlArray<FieldConst>>): void;
addRuntimeTableConfig(id: number, firstColumn: MlArray<FieldConst>): void;
raw(
kind: KimchiGateType,
values: MlArray<FieldVar>,
coefficients: MlArray<FieldConst>
): void;
};
group: {
/**
* Computes `(2*s + 1 + 2^numBits) * P` and also returns the bits of s (which are proven correct).
*
* `numBits` must be a multiple of 5, and s must be in the range [0, 2^numBits).
* The [soundness proof](https://github.com/zcash/zcash/issues/3924) assumes
* `numBits <= n - 2` where `n` is the bit length of the scalar field.
* In our case, n=255 so numBits <= 253.
*/
scaleFastUnpack(
P: MlGroup,
shiftedValue: [_: 0, s: FieldVar],
numBits: number
): MlPair<MlGroup, MlArray<BoolVar>>;
};
/**
* The circuit API is a low level interface to create zero-knowledge proofs
*/
circuit: {
/**
* Generates a proving key and a verification key for the provable function `main`
*/
compile(main: Snarky.Main, publicInputSize: number): Snarky.Keypair;
/**
* Proves a statement using the private input, public input and the keypair of the circuit.
*/
prove(
main: Snarky.Main,
publicInputSize: number,
publicInput: MlArray<FieldConst>,
keypair: Snarky.Keypair
): Snarky.Proof;
/**
* Verifies a proof using the public input, the proof and the verification key of the circuit.
*/
verify(
publicInput: MlArray<FieldConst>,
proof: Snarky.Proof,
verificationKey: Snarky.VerificationKey
): boolean;
keypair: {
getVerificationKey(keypair: Snarky.Keypair): Snarky.VerificationKey;
/**
* Returns a low-level JSON representation of the circuit:
* a list of gates, each of which represents a row in a table, with certain coefficients and wires to other (row, column) pairs
*/
getConstraintSystemJSON(keypair: Snarky.Keypair): JsonConstraintSystem;
};
};
// TODO: implement in TS
poseidon: {
update(
state: MlArray<FieldVar>,
input: MlArray<FieldVar>
): [0, FieldVar, FieldVar, FieldVar];
hashToGroup(input: MlArray<FieldVar>): MlPair<FieldVar, FieldVar>;
sponge: {
create(isChecked: boolean): unknown;
absorb(sponge: unknown, x: FieldVar): void;
squeeze(sponge: unknown): FieldVar;
};
};
};
type MlRef<T> = [_: 0, contents: T];
type SnarkyVector = [0, [unknown, number, FieldVector]];
type ConstraintSystem = unknown;
type SnarkyState = [
_: 0,
system: MlOption<ConstraintSystem>,
input: SnarkyVector,
aux: SnarkyVector,
eval_constraints: MlBool,
num_inputs: number,
next_auxiliary: MlRef<number>,
has_witness: MlBool,
stack: MlList<MlString>,
handler: unknown,
is_running: MlBool,
as_prover: MlRef<MlBool>,
log_constraint: unknown
];
type GateType =
| 'Zero'
| 'Generic'
| 'Poseidon'
| 'CompleteAdd'
| 'VarbaseMul'
| 'EndoMul'
| 'EndoMulScalar'
| 'Lookup'
| 'RangeCheck0'
| 'RangeCheck1'
| 'ForeignFieldAdd'
| 'ForeignFieldMul'
| 'Xor16'
| 'Rot64';
type JsonGate = {
typ: GateType;
wires: { row: number; col: number }[];
coeffs: string[];
};
type JsonConstraintSystem = { gates: JsonGate[]; public_input_size: number };
type Gate = {
type: GateType;
wires: { row: number; col: number }[];
coeffs: string[];
};
// TODO: Add this when OCaml bindings are implemented:
// declare class EndoScalar {
// static toFields(x: Scalar): Field[];
// static fromFields(fields: Field[]): Scalar;
// static sizeInFields(): number;
// }
type MlPublicKey = [_: 0, x: FieldConst, isOdd: MlBool];
type MlPublicKeyVar = [_: 0, x: FieldVar, isOdd: BoolVar];
/**
* Represents the Mina ledger.
*/
declare class Ledger {
/**
* Creates a fresh ledger.
*/
static create(): Ledger;
/**
* Adds an account and its balance to the ledger.
*/
addAccount(publicKey: MlPublicKey, balance: string): void;
/**
* Applies a JSON transaction to the ledger.
*/
applyJsonTransaction(
txJson: string,
accountCreationFee: string,
networkState: string
): void;
/**
* Returns an account.
*/
getAccount(
publicKey: MlPublicKey,
tokenId: FieldConst
): JsonAccount | undefined;
}
declare function Test(): Promise<Test>;
type Test = {
encoding: {
// arbitrary base58Check encoding
toBase58(s: MlBytes, versionByte: number): string;
ofBase58(base58: string, versionByte: number): MlBytes;
// base58 encoding of some transaction types
publicKeyToBase58(publicKey: MlPublicKey): string;
publicKeyOfBase58(publicKeyBase58: string): MlPublicKey;
privateKeyToBase58(privateKey: ScalarConst): string;
privateKeyOfBase58(privateKeyBase58: string): ScalarConst;
tokenIdToBase58(field: FieldConst): string;
tokenIdOfBase58(fieldBase58: string): FieldConst;
memoToBase58(memoString: string): string;
memoHashBase58(memoBase58: string): FieldConst;
};
tokenId: {
// derive custom token ids
derive(publicKey: MlPublicKey, tokenId: FieldConst): FieldConst;
deriveChecked(publicKey: MlPublicKeyVar, tokenId: FieldVar): FieldVar;
};
poseidon: {
hashToGroup(input: MlArray<FieldConst>): MlPair<FieldConst, FieldConst>;
};
signature: {
/**
* Signs a {@link Field} element.
*/
signFieldElement(
messageHash: FieldConst,
privateKey: ScalarConst,
networkId: string
): string;
/**
* Returns a dummy signature.
*/
dummySignature(): string;
};
fieldsFromJson: {
accountUpdate(json: string): MlArray<FieldConst>;
};
hashFromJson: {
accountUpdate(json: string, networkId: string): FieldConst;
/**
* Returns the commitment of a JSON transaction.
*/
transactionCommitments(
txJson: string,
networkId: string
): {
commitment: FieldConst;
fullCommitment: FieldConst;
feePayerHash: FieldConst;
};
/**
* Returns the public input of a zkApp transaction.
*/
zkappPublicInput(
txJson: string,
accountUpdateIndex: number
): { accountUpdate: FieldConst; calls: FieldConst };
};
hashInputFromJson: {
packInput(input: MlHashInput): MlArray<FieldConst>;
timing(json: String): MlHashInput;
permissions(json: String): MlHashInput;
update(json: String): MlHashInput;
accountPrecondition(json: String): MlHashInput;
networkPrecondition(json: String): MlHashInput;
body(json: String): MlHashInput;
};
transactionHash: {
examplePayment(): string;
serializePayment(payment: string): { data: Uint8Array };
serializePaymentV1(payment: string): string;
serializeCommon(common: string): { data: Uint8Array };
hashPayment(payment: string): string;
hashPaymentV1(payment: string): string;
hashZkAppCommand(command: string): string;
};
};
type MlFeatureFlags = [
_: 0,
rangeCheck0: MlBool,
rangeCheck1: MlBool,
foreignFieldAdd: MlBool,
foreignFieldMul: MlBool,
xor: MlBool,
rot: MlBool,
lookup: MlBool,
runtimeTables: MlBool
];
declare namespace Pickles {
type Proof = unknown; // opaque to js
type Statement<F> = [_: 0, publicInput: MlArray<F>, publicOutput: MlArray<F>];
/**
* A "rule" is a circuit plus some metadata for `Pickles.compile`
*/
type Rule = {
identifier: string;
/**
* The main circuit functions
*/
main: (publicInput: MlArray<FieldVar>) => Promise<{
publicOutput: MlArray<FieldVar>;
previousStatements: MlArray<Statement<FieldVar>>;
shouldVerify: MlArray<BoolVar>;
}>;
/**
* Feature flags which enable certain custom gates
*/
featureFlags: MlArrayOptionalElements<MlFeatureFlags>;
/**
* Description of previous proofs to verify in this rule
*/
proofsToVerify: MlArray<{ isSelf: true } | { isSelf: false; tag: unknown }>;
};
/**
* Type to configure how Pickles should cache prover keys
*/
type Cache = [
_: 0,
read: (header: SnarkKeyHeader, path: string) => MlResult<SnarkKey, MlUnit>,
write: (
header: SnarkKeyHeader,
value: SnarkKey,
path: string
) => MlResult<undefined, MlUnit>,
canWrite: MlBool
];
type Prover = (
publicInput: MlArray<FieldConst>,
previousProofs: MlArray<Proof>
) => Promise<[_: 0, publicOutput: MlArray<FieldConst>, proof: Proof]>;
}
declare const Pickles: {
/**
* This is the core API of the `Pickles` library, exposed from OCaml to JS. It takes a list of circuits --
* each in the form of a function which takes a public input `{ accountUpdate: Field; calls: Field }` as argument --,
* and augments them to add the necessary circuit logic to recursively merge in earlier proofs.
*
* After forming those augmented circuits in the finite field represented by `Field`, they gets wrapped in a
* single recursive circuit in the field represented by `Scalar`. Any SmartContract proof will go through both of these steps,
* so that the final proof ends up back in `Field`.
*
* The function returns the building blocks needed for SmartContract proving:
* * `provers` - a list of prover functions, on for each input `rule`
* * `verify` - a function which can verify proofs from any of the provers
* * `getVerificationKeyArtifact` - a function which returns the verification key used in `verify`, in base58 format, usable to deploy a zkapp
*
* Internal details:
* `compile` calls each of the input rules four times, inside pickles.ml / compile:
* 1) let step_data = ... -> Pickles.Step_branch_data.create -> Pickles.Fix_domains.domains -> Impl.constraint_system
* 2) let step_keypair = ... -> log_step -> Snarky_log.Constraints.log -> constraint_count
* 3) let (wrap_pk, wrap_vk) -> log_wrap -> Snarky_log.Constraints.log -> constraint_count
* 4) let (wrap_pk, wrap_vk) -> log_wrap -> Snarky_log.Constraints.log -> constraint_count (yes, a second time)
*/
compile: (
rules: MlArray<Pickles.Rule>,
config: {
publicInputSize: number;
publicOutputSize: number;
storable?: Pickles.Cache;
overrideWrapDomain?: 0 | 1 | 2;
}
) => {
provers: MlArray<Pickles.Prover>;
verify: (
statement: Pickles.Statement<FieldConst>,
proof: Pickles.Proof
) => Promise<boolean>;
tag: unknown;
/**
* @returns (base64 vk, hash)
*/
getVerificationKey: () => Promise<[_: 0, data: string, hash: FieldConst]>;
};
verify(
statement: Pickles.Statement<FieldConst>,
proof: Pickles.Proof,
verificationKey: string
): Promise<boolean>;
loadSrsFp(): WasmFpSrs;
loadSrsFq(): WasmFqSrs;
dummyProof: <N extends 0 | 1 | 2>(
maxProofsVerified: N,
domainLog2: number
) => [N, Pickles.Proof];
/**
* @returns (base64 vk, hash)
*/
dummyVerificationKey: () => [_: 0, data: string, hash: FieldConst];
encodeVerificationKey: (vk: MlWrapVerificationKey) => string;
decodeVerificationKey: (vk: string) => MlWrapVerificationKey;
proofToBase64: (proof: [0 | 1 | 2, Pickles.Proof]) => string;
proofOfBase64: <N extends 0 | 1 | 2>(
base64: string,
maxProofsVerified: N
) => [N, Pickles.Proof];
proofToBase64Transaction: (proof: Pickles.Proof) => string;
sideLoaded: {
// Create a side-loaded key tag
create: (
name: string,
numProofsVerified: 0 | 1 | 2,
publicInputLength: number,
publicOutputLength: number,
featureFlags: MlArrayOptionalElements<MlFeatureFlags>
) => unknown /* tag */;
// Instantiate the verification key inside the circuit (required).
inCircuit: (tag: unknown, verificationKey: unknown) => undefined;
// Instantiate the verification key in prover-only logic (also required).
inProver: (tag: unknown, verificationKey: string) => undefined;
// Create an in-circuit representation of a verification key
vkToCircuit: (
verificationKey: () => string
) => unknown /* verificationKeyInCircuit */;
// Get the digest of a verification key in the circuit
vkDigest: (verificationKeyInCircuit: unknown) => MlArray<FieldVar>;
};
util: {
toMlString(s: string): MlString;
fromMlString(s: MlString): string;
};
};
/**
* A function that has to finish before any bindings exports can be used.
*/
declare function initializeBindings(): Promise<void>;
declare function withThreadPool<T>(run: () => Promise<T>): Promise<T>;