scroll-tech / scroll-tech/ceno

PublicValuesAir underconstrains tidx across AIR boundaries

Open
#1,383 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
153
Forks
43
Avg merge
3d 13h
Merged PRs (30d)
4

Description

Summary

In ceno_recursion_v2, PublicValuesAir only anchors the first public-value row of each proof to public_values_start_tidx, and only enforces consecutive tidx values while adjacent rows belong to the same AIR. At AIR boundaries, the next row's tidx is not tied to the VK-derived public-value offset.

This leaves non-first AIR public-value transcript indices underconstrained.

Details

Current behavior in src/proof_shape/pvs/air.rs:

  • The first public-value row is constrained with local.tidx == public_values_start_tidx.
  • For rows in the same AIR, next.tidx == local.tidx + 1 and next.pv_idx == local.pv_idx + 1.
  • At an AIR boundary, the AIR only constrains next.is_first_in_air == 1; it does not constrain the new AIR's tidx to the expected start offset or prove the previous AIR had exactly the expected number of public-value rows.

The commented-out NumPublicValuesBus receive indicates this was likely intended to be checked structurally, but it is currently inactive.

Ceno-specific expected behavior

Unlike OpenVM, Ceno stores public values in one global public-value namespace. Each AIR's instance list stores absolute global public-value indices. The recursion circuit then projects those into (air_idx, pv_idx) rows for bus interactions.

A fix should restore a Ceno-adapted NumPublicValuesBus check:

  • Proof-shape sends (air_idx, start_tidx, num_pvs) using VK-derived public-value metadata.
  • start_tidx = public_values_start_tidx + sum_{j < air_idx} per_air[j].num_public_values.
  • num_pvs = per_air[air_idx].num_public_values.
  • The sender should be enabled for AIRs with public values in the global public-value block, not gated by trace presence.
  • PublicValuesAir receives this message on each AIR's final PV row using tidx = local.tidx - local.pv_idx and num_pvs = local.pv_idx + 1.
  • Also constrain is_first_in_air => pv_idx == 0.

This would bind each AIR's public-value row range to the VK-declared global public-value layout and close the AIR-boundary tidx gap.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/proof_shape/pvs/air.rs and inspect the active public-value constraints alongside the commented-out NumPublicValuesBus receive. Trace the VK-derived per-AIR public-value metadata used for proof-shape messages. Done means each AIR’s final public-value row is checked against its global start offset and count, including AIR-boundary rows and the first-row pv_idx condition.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
cryptography
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.