Skip to main content

Deferred Computation

Let Fp\mathbb{F}_p and Fq\mathbb{F}_q be the two fields, with Ep(Fq)=p|\mathbb{E}_p(\mathbb{F}_q)| = p, Eq(Fp)=q|\mathbb{E}_q(\mathbb{F}_p)| = q for Elliptic curves Ep(Fq)\mathbb{E}_p(\mathbb{F}_q) and Eq(Fp)\mathbb{E}_q(\mathbb{F}_p). Assume q>pq > p. We have a proof system (Kimchi) over Fp\mathbb{F}_p and Fq\mathbb{F}_q, where commitments to public inputs are:

Pp=e,HEp(Fq)Pq=e,GEq(Fp)P_p = \langle \vec{e}, \vec{H} \rangle \in \mathbb{E}_p(\mathbb{F}_q) \\ P_q = \langle \vec{e}, \vec{G} \rangle \in \mathbb{E}_q(\mathbb{F}_p)

Respectively. See Pasta Curves for more details.

When referring to the Fq\mathbb{F}_q-side we mean the proof system for circuit over the field Fq\mathbb{F}_q.

Public Inputs / Why Passing

In pickles-rs we have the notion of "passing" a variable (including the transcript) from one side to the other. e.g. when a field element αFp\alpha \in \mathbb{F}_p needs to be used as a scalar on Fq\mathbb{F}_q.

This document explains what goes on "under the hood". Let us start by understanding why:

Let vFqv \in \mathbb{F}_q be a scalar which we want to use to do both:

  1. Field arithmetic in Fq\mathbb{F}_q
  2. Scalar operations on Eq(Fp)\mathbb{E}_q(\mathbb{F}_p)

In order to do so efficiently, we need to split these operations across two circuits (and therefore proofs) because:

  1. Emulating Fq\mathbb{F}_q arithmetic in Fp\mathbb{F}_p is very expensive, e.g. computing vwv \cdot w requires O(log(q)2)O(\log(q)^2) multiplications over Fp\mathbb{F}_p: 100's of gates for a single multiplication.
  2. Since Eq(Fp)Fp×Fp\mathbb{E}_q(\mathbb{F}_p) \subseteq \mathbb{F}_p \times \mathbb{F}_p we cannot compute [v]HEq(Fp)[v] \cdot H \in \mathbb{E}_q(\mathbb{F}_p) over Fq\mathbb{F}_q efficiently, because, like before, emulating Fp\mathbb{F}_p arithmetic in Fq\mathbb{F}_q is very expensive...

Solution

The solution is to "pass" a value vv between the two proofs, in other words to have two values v~Fp\tilde{v} \in \mathbb{F}_p and vFqv \in \mathbb{F}_q which are equal as integers i.e. lift(v)=lift(v~)Z\text{lift}(v) = \text{lift}(\tilde{v}) \in \mathbb{Z}: they represent "the same number". A naive first attempt would be to simply add v~Fp\tilde{v} \in \mathbb{F}_p to the witness on the Fp\mathbb{F}_p-side, however this has two problems:

Insufficient Field Size: p<qp < q hence vv cannot fit in Fp\mathbb{F}_p.

No Binding: More concerning, there is no binding between the v~\tilde{v} in the Fp\mathbb{F}_p-witness and the vv in the Fq\mathbb{F}_q-witness: a malicious prover could choose completely unrelated values. This violates soundness of the overall Fq/Fq\mathbb{F}_q/\mathbb{F}_q-relation being proved.

Problem 1: Decompose

The solution to the first problem is simple:

In the Fq\mathbb{F}_q-side decompose v=2h+lv = 2 \cdot h + l with h[0,2logp)h \in [0, 2^{\lfloor \log p \rfloor}) (high bits) and l{0,1}l \in \{ 0, 1 \} (low bit). Note l,h<pl, h < p since 2p>q2 p > q; always the case for any cycle of curves, pp is only q\approx \sqrt{q} smaller than qq, by Hasse. Now "v~\tilde{v}" is "represented" by the two values h~,l~Fp\tilde{h}, \tilde{l} \in \mathbb{F}_p.

Note that no decomposition is necessary if the "original value" vv was in Fp\mathbb{F}_p, since Fq\mathbb{F}_q is big enough to hold the lift of any element in Fp\mathbb{F}_p.

Problem 2: Compute Commitment to the Public Input of other side

To solve the binding issue we will add l,hl, h to the public inputs on the Fp\mathbb{F}_p-side, for simplicity we will describe the case where l,hl, h are the only public inputs in the Fp\mathbb{F}_p-side, which means that the commitment PpEp(Fq)P_p \in \mathbb{E}_p(\mathbb{F}_q) to the public inputs on the Fp\mathbb{F}_p side is:

Pp=[h]Gh+[l]GlEp(Fq)P_p = [h] \cdot G_h + [l] \cdot G_l \in \mathbb{E}_p(\mathbb{F}_q)

At this point it is important to note that Ep\mathbb{E}_p is defined over Fq\mathbb{F}_q!

Which means that we can compute PpEp(Fq)P_p \in \mathbb{E}_p(\mathbb{F}_q) efficiently on the Fq\mathbb{F}_q-side!

Therefore to enforce the binding, we:

  1. Add a sub-circuit which checks: Pp=[h]Gh+[l]GlEp(Fq),h=logpl=1P_p = [h] \cdot G_h + [l] \cdot G_l \in \mathbb{E}_p(\mathbb{F}_q), \\ |h| = \lfloor \log p \rfloor \\ |l| = 1
  2. Add Pp=(x,y)Fq×FqP_p = (x, y) \in \mathbb{F}_q \times \mathbb{F}_q to the public input on the Fq\mathbb{F}_q-side.

We recurse onwards...

At this point the statement of the proof in Fq\mathbb{F}_q-side is: the Fq\mathbb{F}_q-proof is sound, condition on providing an opening of PpP_p that satisfies the Fp\mathbb{F}_p-relation.

At this point you can stop and verify the proof (in the case of a "step proof" you would), by recomputing PpP_p outside the circuit while checking the Fp\mathbb{F}_p-relation manually "in the clear".

However, when recursing (e.g. wrapping in a "wrap proof") we need to "ingest" this public input PpP_p; after all, to avoid blowup in the proof size everything (proofs/accumulators/public inputs etc.) must eventually become part of the witness and every computation covered by a circuit...

To this end, the wrap proof is a proof for the Fp\mathbb{F}_p-relation with the public input PpP_p which additionally verifies the Fq\mathbb{F}_q-proof.

The next "step" proof then verifies this wrap proof which means that PpP_p then becomes part of the witness!

In Pickles

We can arbitrarily choose which side should compute the public input of the other, in pickles we let "wrap" compute the commitment to the public input.

Enforcing Equality

Enforces that the public input of the proof verified on the Fr side is equal to the Fp input computed on Fr side.