Skip to main content

Analyzing Constraints

Optimizing o1js code is often not a matter of copying over optimized functions traditional programming languages, but instead a matter of minimizing the number of constraints required to model the code. It's important to analyze the functions you write to understand the constraint system that they generate. o1js provides the tools you need to understand how many, and what types of constraints your program generates.

Example provable function

For the following examples, let's define a dummy function that we can reuse in different contexts:

function exampleProvableFunction() {
const x = Provable.witness(Field, () => Field(10));
const y = Provable.witness(Field, () => Field(20));

y.assertGreaterThan(x);
return Poseidon.hash([x, y]);
}

Using Provable.constraintSystem

Provable.constraintSystem is a way to create something like a "snippet" of provable code. o1js can convert the snippet into a constraint system, which can then be analyzed. This simple wrapper is the lightest-weight way to quickly analyze some provable code. You can also use it to analyze specific pieces of a larger program, or specific functions.

note

This will not check correctness of the code. It will only convert the code into a constraint system, which is essentially a list of steps to execute. It will not execute the steps, or provide any guarantees that the constraint system generated is correct or secure.

const cs = await Provable.constraintSystem(() => {
exampleProvableFunction();
});

cs.summary(); // Count of constraints, grouped by gate type
cs.rows; // Count of total constraints
cs.gates; // Array of gates that make up the constraint system (essentially a serialization of the raw system)

The summary looks like this:

{
"ForeignFieldAdd": 3,
"Generic": 3,
"Poseidon": 11,
"RangeCheck0": 8,
"RangeCheck1": 4,
"Total rows": 36,
"Zero": 7
}

Read more at the language reference: Provable.constraintSystem.

Using ZkProgram

ZkProgram is a more complete provable implementation of a program. It has proper inputs and outputs, and methods that can be called to create a proof. The ZkProgram itself can be analyzed, which is a handy shortcut to simply get the analysis output for each method in the program. ZkProgram has type requirements and more configuration required than Provable.constraintSystem, so to check just some pieces of code, Provable.constraintSystem is the better choice, but to analyze your methods in the context of a full program that's subject to specific return types, inputs, and outputs, ZkProgram is the better choice.

info

ZkProgram.analyzeMethods uses Provable.constraintSystem under the hood, so ultimately, they expose the same functionality. The main difference is the ergonomics of the API. Provable.constraintSystem is a pure constraint system generation tool, while ZkProgram is a more complete provable implementation, of which constraint system generation is one part.

const zkp = ZkProgram({
name: "Example",
publicOutput: Field,
methods: {
example: {
privateInputs: [],
method: async () => {
const publicOutput = exampleProvableFunction();
return { publicOutput };
},
},
},
});

const zkpAnalysis = await zkp.analyzeMethods();

zkpAnalysis.example.summary();
zkpAnalysis.example.rows;
zkpAnalysis.example.gates;

Note how the same function results in the same constraint system summary:

{
"ForeignFieldAdd": 3,
"Generic": 3,
"Poseidon": 11,
"RangeCheck0": 8,
"RangeCheck1": 4,
"Total rows": 36,
"Zero": 7
}

Read more at the language reference: ZkProgram.

Using SmartContract

SmartContract is a specialized class for writing proofs that interact with the Mina blockchain. For the purposes of analyzing the constraint system, it works like ZkProgram. Keep in mind, that not every method on the SmartContract actually has to be provable! Non-provable methods will not be converted into constraint systems. As a class, SmartContracts are just a JavaScript object, so you can add any methods you want, just like any class. Only methods decorated with @method will be marked as provable, and included in the resulting constraint system.

info

SmartContract.analyzeMethods uses Provable.constraintSystem under the hood, so ultimately, they expose the same functionality. Just like ZkProgram, SmartContract is a complete provable implementation of a program, but it layers on even more extra functionality in the form of compatibility with the Mina blockchain.

So Provable.constraintSystem is strictly a constraint system generation tool. It's extremely generic and can't be used to create a proof. ZkProgram is a complete provable program, including constraint system generation, but also proof generation and verification. SmartContract is a specialized wrapper around ZkProgram that is designed to work with the Mina blockchain. All of these tools can expose generated constraint system analyses.

class ExampleContract extends SmartContract {
@state(Field) x = State<Field>();

@method async example(y: Field, knownHash: Field) {
const x = this.x.getAndRequireEquals();
y.assertGreaterThan(x);
Poseidon.hash([x, y]).assertEquals(knownHash);
this.x.set(y);
}

// Helper function, totally out of scope for analyzeMethods
get stringX() {
return this.x.get().toString();
}
}

const scAnalysis = await ExampleContract.analyzeMethods();

scAnalysis.example.rows;
scAnalysis.example.gates;

Read more at the language reference: SmartContract.