Skip to content

feat(tutorials): add tutorial 14, logic in a circuit - #1289

Open
dkijania wants to merge 1 commit into
mainfrom
feat/tutorial-14-circuit-logic
Open

dkijania wants to merge 1 commit into
mainfrom
feat/tutorial-14-circuit-logic

Conversation

@dkijania

@dkijania dkijania commented Oct 3, 2026

Copy link
Copy Markdown
Member

Summary

New page Tutorial 14: Logic in a Circuit (docs/zkapps/tutorials/14-circuit-logic.mdx) and its example project examples/zkapps/14-circuit-logic. The page is built test-first: each "how not to" is a test that proves the failure, and each "how to" is a test that proves the fix. Each error message on the page is the real text from o1js 3.0.0. All code on the page comes from the example through #include_code.

Also:

  • sidebars.js: page 14 after 13.
  • Page 13: front matter only, pagination_next: zkapps/tutorials/circuit-logic. Page 14 has pagination_prev to 13.
  • docs/zkapps/tutorials/index.mdx: page 14 in the list.
  • .github/workflows/test-tutorials.yml: example in the run, test and canary matrices.
  • static/llms.txt, static/llms-full.txt regenerated.

Topics, claims and tests (34 tests)

Constants and variables (constants.test.ts, real proofs)

  • Arithmetic on constants adds 0 rows; a product of two variables adds rows — adds no constraints for arithmetic on constants
  • A variable times a constant plus a constant adds 0 rows (linear combination) — adds no constraints for a variable times a constant plus a constant
  • isConstant() — marks a Field made from a JavaScript value as a constant, and a witness as a variable
  • A JS value is baked in at compile time — proves a secret more than the minimum that was compiled in, cannot prove a secret that is not more than the minimum
  • Change the JS value after compile() → the proof could not be constructed: rest of division by vanishing polynomial — cannot prove after the JavaScript value changes, until the program is compiled again
  • A new constant gives a new verification key; the proof does not verify against the old key — gives a different verification key for a different constant
  • A public input: one key for each minimum — proves against different minimums with one verification key, cannot prove a secret that is not more than the minimum input

Provable.if and a JavaScript if (branching.test.ts)

  • JS if on a Bool compiles with no error and always takes the true branch (a Bool is an object) — compiles with no error, always takes the true branch, so a score of 5 gets the bonus
  • .toBoolean() → b.toBoolean() was called on a variable Bool `b` in provable code. at compile — fails at compile time with toBoolean()
  • Provable.if — selects the bonus in the proof for each score
  • Provable.if computes both branches: x.div(0) in the unselected branch → Constraint unsatisfied — computes both branches, so a division by 0 in the branch that is not selected fails, gives 0 for y = 0 and x / y otherwise when each branch is safe

Loops (loops.test.ts)

  • Loop bound from a variable → x.toBigInt() was called on a variable field element `x` in provable code. at compile — fails at compile time when the loop bound depends on a variable
  • A JS loop unrolls: analyzeMethods() rows double when the steps double (4/8/16 steps → 2/4/8 rows) — unrolls a JavaScript loop: twice the steps give twice the rows
  • Fixed maximum + Provable.if, one circuit — sums the first n items for n from 0 to MAX_LENGTH, with one circuit, cannot prove for n more than MAX_LENGTH

Find a value in a list (#892's array.includes() case, includes.test.ts)

  • Array.includes() is false for a value in the list (reference equality) — is not found by Array.includes(), although the list has it
  • Array.some(e => e.equals(x)) is true for a value not in the list (Bool is truthy) — is found by Array.some() with equals(), although the list does not have it
  • reduce with equals()/or() — is found by includes() with or() only when the list has it

Witnesses (witness.test.ts, real proofs)

  • Honest — gives an honest prover a square root
  • Unconstrained: a malicious prover proves 7 is the square root of 9, verify() is true — accepts any witness when there is no constraint: ...
  • The witness code is not in the verification key — does not put the witness code in the verification key
  • With y.mul(y).assertEquals(x, ...) the same witness fails — rejects the same malicious witness when a constraint checks it

toBigInt() / toString() and debugging (conversions.test.ts)

  • Both fail at compile with the messages above — fails at compile time with toBigInt(), fails at compile time with toString()
  • toBits() instead — proves if a number is even with constrained bits
  • Provable.asProver runs only when proving — runs Provable.asProver only when proving, with the values
  • asProver/Provable.log add 0 rows — adds no constraints for Provable.asProver and Provable.log

Assertions and returned Bools (assertions.test.ts, real proofs)

  • A returned Bool gives a valid proof of a false result — gives a valid proof for an age of 12 when the method returns a Bool: the output is false
  • An assertion makes it impossible — cannot prove an age of 12 when the method asserts, proves an age of 30 when the method asserts

Checks run locally (Node 22)

  • Example: npm start and npm test (34/34 pass; about 11 minutes with the default jest workers).
  • Root: npm run build, npm run test:scripts, npm run check-llms-txt, npm run check-o1labs-links pass. Each o1Labs link on the page returns HTTP 200.

Part of #1249 (6.7). Closes #892, #240.

🤖 Generated with Claude Code

Each wrong way to write logic in an o1js circuit is a test that shows the
failure, and each right way is a test that shows it works: constants and
variables, Provable.if and a JavaScript if, loop bounds, finding a value in
a list, unconstrained witnesses, toBigInt()/toString(), and assertions
against returned Bools. Messages are from o1js 3.0.0.

Part of #1249 (6.7).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
docs2 Ready Ready Preview Oct 3, 2026 7:30am UTC

Request Review

This branch was successfully deployed

1 active deployment
Preview – docs2 — ad8efa69 Deployed Oct 3, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Examples for how to and how not to implement logic in a circuit

1 participant