zorch.lnp.opening¶
Π_many — ZK opening of an ABDLOP commitment with N linear relations.
The first protocol layer of the LNP framework (eprint 2022/284, Fig. 4):
prove knowledge of (s1, s2) opening t_A = A1·s1 + A2·s2 — with the
message implicitly m = t_B − B·s2 — such that ‖z_i‖ stays small and the
N relations R1·s1 + Rm·m = u over R_q hold. One masked response pair
carries all N relations, so the proof size is independent of N.
The interactive shape, made non-interactive against the ByteTranscript
seam: the prover masks with Gaussians y_i ~ D_{s_i}, absorbs
w = A1·y1 + A2·y2 and v = R1·y1 − Rm·B·y2 into the transcript, squeezes
the challenge c ∈ C (challenge.py), answers z_i = c·s_i + y_i over the
integers, and rejection-samples both responses (Rej1, Lemma 2.14-1 —
leakage-free, so plain MLWE; Rej2/Rej0 are a recorded later optimization).
The wire is (c, z1, z2): the verifier recomputes w = A1·z1 + A2·z2 − c·t_A
and v = R1·z1 + Rm·(c·t_B − B·z2) − c·u from the verification equations,
replays the absorb/squeeze, and accepts iff the recomputed challenge equals
c and both ‖z_i‖₂ ≤ s_i·√(2·m_i·d) — hashing (w, v) instead of sending
them is what makes the proof N-independent, and it is the paper's own
Fiat-Shamir shape.
The masking, the rejection budget and the [Ban93] norm bounds are not
this protocol's own — Fig. 6 masks against exactly the same ones, and
Fig. 8 runs both protocols against a single commitment. They live on the
Masking this is built over (masking.py), together with the host/device
boundary they imply. What is this module's own is the pair of first-round
messages (w, v) and the verification equations that recompute them.
The transcript arrives already bound to the statement (the caller absorbed the commitment); this protocol absorbs only its own messages.
OpeningProof
dataclass
¶
The non-interactive Π_many wire: the challenge and the two masked
responses, as signed integer coefficient vectors (int64; z_i is
(m_i, d), c is (d,)). w and v are absent by design — see the
module docstring on why the verifier recomputes them.
Source code in zorch/lnp/opening.py
46 47 48 49 50 51 52 53 54 55 | |
AbdlopOpening ¶
Π_many prove/verify over an AbdlopCommitment (Fig. 4).
The proof parameters — masking deviations, repetition rates, the
challenge point, the rejection budget — live on the Masking this is
built over, because Fig. 6 masks against the same ones and Fig. 8 runs
both protocols against a single commitment. See masking.py.
The relation count is not among them: it is r1.shape[0], which both
prove and verify already receive, so storing it would be a second
representation of one number with nothing gating the two against each
other.
Source code in zorch/lnp/opening.py
58 59 60 61 62 63 64 65 66 67 68 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 | |
prove ¶
prove(
a1: np.ndarray,
a2: np.ndarray,
b: np.ndarray,
r1: np.ndarray,
rm: np.ndarray,
s1: np.ndarray,
s2: np.ndarray,
rng: np.random.Generator,
transcript: ByteTranscript,
) -> tuple[OpeningProof, ByteTranscript]
One non-interactive proof, and the transcript advanced past it.
s1/s2 arrive as signed integer (m_i, d) arrays — the raw
witness form the samplers emit; ring.from_signed of their rows
are the columns commit was called with. The commitment
(t_a, t_b) and target u are deliberately absent: the prover's
messages never read them — the transcript arrived bound to the
statement, and the asymmetry with verify documents exactly
that convention.
Source code in zorch/lnp/opening.py
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 | |
verify ¶
verify(
a1: np.ndarray,
a2: np.ndarray,
b: np.ndarray,
r1: np.ndarray,
rm: np.ndarray,
t_a: np.ndarray,
t_b: np.ndarray,
u: np.ndarray,
proof: OpeningProof,
transcript: ByteTranscript,
) -> tuple[bool, ByteTranscript]
Fig. 4's three checks in their non-interactive shape: both norm
bounds, then the recomputed (w, v) must replay to the proof's
challenge — which is checks 2 and 3 folded into the hash.
Source code in zorch/lnp/opening.py
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 | |