Skip to content

zorch.pcs.ligerito.verifier

Ligerito recursive-open verifier — the verifier half of the recursive matrix PCS. Replays the prover's continuous sumcheck and per-level Fiat-Shamir: it reconstructs the running basis B (from the value basis eq(z) plus each level's induced proximity basis, folded by the same challenges), checks every sumcheck round's identity, rebuilds the queried codeword rows from the committed roots, and recomputes each level's proximity left-hand side <M_j[s], eq(c_j)> from the opened rows — inducing it into the sumcheck exactly as the prover did. The final level's proximity is checked directly against the in-clear residual, and the sumcheck's terminal claim must equal Σ_x residual(x)·B(x). It holds only the public params (the per-level codes for block geometry + proximity points, tree for the Merkle config).

LigeritoVerifier dataclass

Bases: VerifierStage[OpeningClaim[LigeritoCommitment], TrivialClaim, OpeningProof[LigeritoProof], TranscriptT], Generic[TranscriptT]

Ligerito recursive PCS verifier. Mirrors LigeritoProver's make_code / tree / config / choreography (share the choreography instance with the prover — it fixes the Fiat-Shamir wire for both sides).

Source code in zorch/pcs/ligerito/verifier.py
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
@dataclass(frozen=True)
class LigeritoVerifier(
    VerifierStage[
        OpeningClaim[LigeritoCommitment],
        TrivialClaim,
        OpeningProof[LigeritoProof],
        TranscriptT,
    ],
    Generic[TranscriptT],
):
    """Ligerito recursive PCS verifier. Mirrors `LigeritoProver`'s `make_code` /
    `tree` / `config` / `choreography` (share the choreography instance with
    the prover — it fixes the Fiat-Shamir wire for both sides)."""

    make_code: MakeCode
    tree: MerkleTree
    config: LigeritoConfig
    choreography: LigeritoChoreography[TranscriptT] = LigeritoChoreography()

    def _code(self, level: int, message_len: int) -> TensorCode:
        return self.make_code(message_len, self.config.log_inv_rates[level])

    def verify(
        self,
        claim: OpeningClaim[LigeritoCommitment],
        reduction_proof: OpeningProof[LigeritoProof],
        transcript: TranscriptT,
    ) -> VerifyResult[TrivialClaim, TranscriptT]:
        """Check the claimed evaluations against the commitment."""
        ok, transcript = self._verify_opening(
            claim.commitment,
            claim.points,
            reduction_proof.values,
            reduction_proof.proof,
            transcript,
        )
        return VerifyResult(TrivialClaim(), transcript, ok)

    def _verify_opening(
        self,
        commitment: LigeritoCommitment,
        points: Sequence[Array],
        value: Array,
        proof: LigeritoProof,
        transcript: TranscriptT,
    ) -> tuple[Array, TranscriptT]:
        """Return `(ok, transcript)` where `ok` is a scalar boolean array."""
        if len(points) != 1:
            raise ValueError(f"Ligerito opens at one point, got {len(points)}")
        z = points[0]
        if z.shape[0] != self.config.num_vars:
            raise ValueError(
                f"point dimension {z.shape[0]} must equal the variable count "
                f"{self.config.num_vars}"
            )
        self._check_shape(proof)
        one = fnp.ones((), z.dtype)
        return _verify(
            self,
            commitment,
            z,
            expand_eq_to_hypercube(z, one),
            value,
            proof,
            transcript,
        )

    def verify_with_basis(
        self,
        commitment: LigeritoCommitment,
        basis: Array,
        value: Array,
        proof: LigeritoProof,
        transcript: TranscriptT,
    ) -> tuple[Array, TranscriptT]:
        """`verify` for a RAW hypercube basis instead of a point — the dual of
        `LigeritoProver.open_with_basis` (`bind_statement` receives
        `point=None`)."""
        if basis.shape[0] != 1 << self.config.num_vars:
            raise ValueError(
                f"basis length {basis.shape[0]} must be 2^{self.config.num_vars}"
            )
        self._check_shape(proof)
        return _verify(self, commitment, None, basis, value, proof, transcript)

    def _check_shape(self, proof: LigeritoProof) -> None:
        """Fail loud on a structurally malformed proof — a short list would let
        the replay silently skip checks."""
        cfg = self.config
        num_messages = self.choreography.num_messages(cfg)
        if len(proof.sumcheck_messages) != num_messages:
            raise ValueError(
                f"malformed proof: expected {num_messages} sumcheck messages, "
                f"got {len(proof.sumcheck_messages)}"
            )
        if len(proof.recursive_roots) != cfg.num_levels - 1:
            raise ValueError(
                f"malformed proof: expected {cfg.num_levels - 1} recursive roots, "
                f"got {len(proof.recursive_roots)}"
            )
        if len(proof.component_openings) != cfg.num_levels:
            raise ValueError(
                f"malformed proof: expected {cfg.num_levels} component openings, "
                f"got {len(proof.component_openings)}"
            )
        if len(proof.ood_values) != cfg.total_ood:
            raise ValueError(
                f"malformed proof: expected {cfg.total_ood} OOD values, "
                f"got {len(proof.ood_values)}"
            )
        num_pow = self.choreography.num_pow_witnesses(cfg)
        if len(proof.pow_witnesses) != num_pow:
            raise ValueError(
                f"malformed proof: expected {num_pow} proof-of-work witnesses, "
                f"got {len(proof.pow_witnesses)}"
            )

verify

verify(
    claim: OpeningClaim[LigeritoCommitment],
    reduction_proof: OpeningProof[LigeritoProof],
    transcript: TranscriptT,
) -> VerifyResult[TrivialClaim, TranscriptT]

Check the claimed evaluations against the commitment.

Source code in zorch/pcs/ligerito/verifier.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
def verify(
    self,
    claim: OpeningClaim[LigeritoCommitment],
    reduction_proof: OpeningProof[LigeritoProof],
    transcript: TranscriptT,
) -> VerifyResult[TrivialClaim, TranscriptT]:
    """Check the claimed evaluations against the commitment."""
    ok, transcript = self._verify_opening(
        claim.commitment,
        claim.points,
        reduction_proof.values,
        reduction_proof.proof,
        transcript,
    )
    return VerifyResult(TrivialClaim(), transcript, ok)

verify_with_basis

verify_with_basis(
    commitment: LigeritoCommitment,
    basis: Array,
    value: Array,
    proof: LigeritoProof,
    transcript: TranscriptT,
) -> tuple[Array, TranscriptT]

verify for a RAW hypercube basis instead of a point — the dual of LigeritoProver.open_with_basis (bind_statement receives point=None).

Source code in zorch/pcs/ligerito/verifier.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
def verify_with_basis(
    self,
    commitment: LigeritoCommitment,
    basis: Array,
    value: Array,
    proof: LigeritoProof,
    transcript: TranscriptT,
) -> tuple[Array, TranscriptT]:
    """`verify` for a RAW hypercube basis instead of a point — the dual of
    `LigeritoProver.open_with_basis` (`bind_statement` receives
    `point=None`)."""
    if basis.shape[0] != 1 << self.config.num_vars:
        raise ValueError(
            f"basis length {basis.shape[0]} must be 2^{self.config.num_vars}"
        )
    self._check_shape(proof)
    return _verify(self, commitment, None, basis, value, proof, transcript)