Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion spec/assets/mathml_cleanup.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
// Fix 3: Replace overlines with a border-top CSS rule for browsers that
// do not stretch the top line properly. (e.g. chromium (and forks like Brave))
for (let elem of document.body.querySelectorAll("mover mo:last-child")) {
if (isOverline(elem.textContent) && elem.getBoundingClientRect().width < elem.parentElement.getBoundingClientRect().width * 0.9) {
if (isOverline(elem.textContent) && elem.getBoundingClientRect().width < elem.parentElement.getBoundingClientRect().width * 0.8) {
elem.style.display = "none";
elem.parentElement.style.borderTop = "0.06em solid currentColor";
elem.parentElement.style.paddingTop = "0.12em";
Expand Down
83 changes: 74 additions & 9 deletions spec/chapters/recursion.typ
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#import "/meta.typ": et, aside
#import "/meta.typ": et, aside, rj

// Spaces and instances
#let (programSpace, program) = ($cal(F)$, $f$)
Expand All @@ -10,6 +10,7 @@
#let (commitmentSpace, commitment) = ($cal(C)$, $bb(c)$)
#let commit(x) = $dash(#x)$
#let comm(x) = $commit(#x)$
#let comm2(x) = $hat(#x)$

#let hash = $H$

Expand Down Expand Up @@ -44,7 +45,7 @@ This relation induces the language $language subset.eq instanceSpace$ of _solvab
where $instance in language$ if there exists a witness $witness$ for which $(instance, witness)in relation$.

Lastly, we introduce the instance commitment function $c: instanceSpace to commitmentSpace$.
Note that this commitment scheme does not involve randomness; it is a _determistic_ scheme.
Note that this commitment scheme does not involve randomness; it is a _deterministic_ scheme.
Randomness is typically required to make a commitment _hiding_.
For the purposes of this discussion, we are not concerned with this property,
as the function will only be used for committing to public information.
Expand Down Expand Up @@ -100,7 +101,7 @@ This increase in verifier computation is undesirable and should be avoided.
A solution to this, is to leverage the following variation to the verification algorithm:
$
verify': commitmentSpace^2 times proofSpace: ([comm(x), comm(y)], proof) mapsto
verify(comm(x), proof) or verify(comm(y([comm(x), comm(y)], dot)), proof)
verify(comm(x), proof) or verify(comm(y([comm(x), comm(y)], dot)), proof),
$
where it is assumed that $comm(y([comm(x), comm(y)], dot)) in commitmentSpace$ can be constructed efficiently from $comm(x), comm(y) in commitmentSpace$.
Now observe that setting $(comm(x), comm(y)) = (commit(instance), commit(verify'))$ yields
Expand All @@ -113,7 +114,7 @@ a) proof of $instance in language$, or
b) proof of _the existence of a (different) proof_ that satisfies $verify'([comm(instance), comm(verify')], dot)$.
We can now recursively expand the expression, yielding
$
verify'(commit(instance), commit(verify'), proof)
verify'([commit(instance), commit(verify')], proof)
&= verify(comm(instance), proof) or verify(comm(verify'([comm(instance), comm(verify')], dot)), proof)\
&= verify(comm(instance), proof) or verify(comm(verify(comm(instance), dot) or verify(comm(verify'([comm(instance), comm(verify')]; dot)), dot)), proof)\
&= verify(comm(instance), proof) or verify(comm(verify(comm(instance), dot) or verify(comm(verify(comm(instance), dot) or verify(comm(verify'([comm(instance), comm(verify')], dot)), dot)), dot)), proof)\
Expand All @@ -128,15 +129,15 @@ $(comm(instance), comm(verify'))$ is _constant_ in the depth of the recursion.
In fact, the size of the verification instance is typically fully determined by
$comm(instance)$, since $comm(verify')$ can often be precomputed.

#aside([Soundness])[
#aside[Soundness][
It is important to note that soundness of this construction is not yet proven.
For example, there may exist proofs that _attest to the existence of itself_ in a finite number of recursion steps.
Such a proof would be accepted by $verify'$ even if $instance in.not language$.
In practice, one might be able to prevent this particular problem by including
a recursion-level counter in the proof.
]

#aside([$comm(verify')$ absorption])[
#aside[$comm(verify')$ absorption][
Note that $commit(verify')$ must be provided to $verify'$ as a _parameter_;
absorbing it into $verify'$ would imply an object containing a cryptographic
commitment of itself at a static location, which is practically impossible.
Expand Down Expand Up @@ -238,17 +239,81 @@ The produced proof $proof'$ now attests to $instance in language$ when
$verify(comm(product(verify_0, verify_1)(comm(instance), dot)), proof') = 1$.

= Split proving
#et("todo")
To enable recursion with a split verifier, we start by considering that each part of the split may

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need split proving to achieve recursion w/ a split verifier.

Suggested change
To enable recursion with a split verifier, we start by considering that each part of the split may
To accelerate recursion with a split verifier, we now discuss how each part of the split may

be proven in a different proof system.
We consider the case for a valid split verifier $(verify_0, verify_1, d_verify)$
with proof functions $prove_0$ and $prove_1$.
Since these proof systems can potentially use different commitments,
we will write $comm(x)$ and $comm2(x)$ for a (deterministic) commitment to $x$

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This overloads $\stackrel{-}{x}$.

I think this is a problem. Later on, you write

With the right choice of proof system and commitment scheme $comm(x)$, ...

I think you want this to convey that $\stackrel{-}{x} := (\stackrel{-}{x}, \hat{x})$. The recursive definition might stump the reader, right at a crucial step that already requires some mental gymnastics to understand.

in the $prove_0$ and $prove_1$ scheme respectively.

To then coherently prove the original verifier $verify$, our goal becomes:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
To then coherently prove the original verifier $verify$, our goal becomes:
To then coherently prove the original verifier $verify$, our goal becomes to prove that:


1. $verify_0(comm(bb(x)), [proof_0, record_0]) = 1$
2. $verify_1(comm2(bb(x)), [proof_1, record_1]) = 1$
3. $[proof_0, record_0] = [proof_1, record_1]$

The first two statements would follow naturally from proving the evaluation of $verify_(b)$.
To integrate the last statement, we introduce a "checksum" of the witness, which will be checked by both halves of the split:
$ verify'_(b)([x, s], w) = verify_(b)(x, w) dot (serif("cksum")(w) =^? s). $
Comment on lines +257 to +258

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we suddenly talking about witness and using $w$, rather than proof and $\pi$?

The choice of checksum function, in turn, can depend on the choice of proof systems.
We briefly present a few trivial instantiations, before bringing the cost down further.

- $serif("cksum")(x) = serif("Hash")(x)$: compute a hash in both halves, at the cost of needing to evaluate a hash in a proof system that is likely not a good fit for that hash.
- $serif("cksum")(x) = comm2(x)$: recompute the commitment $comm2(x)$ as part of $verify^*_0$, and let $verify^*_1$ simply check that the checksum equals the public commitment.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$v^*_b$ is not defined.


Finally, we consider the instantiation $serif("chsum")(x) = chevron.l x, z chevron.r$ for a random zero-evader $z$.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Finally, we consider the instantiation $serif("chsum")(x) = chevron.l x, z chevron.r$ for a random zero-evader $z$.
Finally, we consider the instantiation $serif("cksum")(x) = chevron.l x, z chevron.r$ for a random zero-evader $z$.

This could be implemented by having an additional public input provided to $verify'_(b)$, or by having $verify'_(b)$ itself
derive it from the public inputs and context as a Fiat-Shamir challenge.
In either case, $z$ should not be known to the (recursion) prover before being bound to both $[proof_b, record_b]$ pairs used.
If the proof systems for the split verifier work over different fields, field emulation would need to happen on one side, with the other using native arithmetic,
or both sides could work in a common target field that is simpler to emulate in both.#footnote[
In a practical implementation with flock and the lambda proof system side by side, an inner product in the goldilocks extension field should
be possible in a repetitive binary circuit that fits well in flock's model.
]
Choices for the structure of $z$ include $z = (1, alpha, alpha^2, ..., alpha^(lr(|x|) - 1))$ and $z = (alpha_1, alpha_2, alpha_3, ..., alpha_(|x|))$.
The former has a soundness loss linear in the size of $x$, while the latter will require more computational
work to derive independent challenges inside of the verifier.
As a tradeoff, one could consider the mixture of both, through
$z = (alpha_1, alpha_1^2, ..., alpha_1^ell, alpha_2, alpha_2^2, ..., alpha_(lr(|x|)/ell)^ell)$, with a soundness loss $ell$ but only

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re:

... with a soundness loss $ell$ ...

is this $ell$ bits? or bits linear in $ell$? Or something else? The absence of a unit is a touch confusing to me.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe "soundness error $\frac\ell{|\mathbf{F}|}$ is more clear

$lr(|x|)/ell$ independent challenges to derive.
To avoid having to prove the derivation of the random challenges, they can be made into a public input.#footnote[
This requires a succinct description of $z$ (e.g. the single value $alpha$), as otherwise the shrinking
effect of providing $proof$ as private witness is lost.
]
Where needed, multiple independent inner products can be used to amplify soundness.
In settings where both $verify'_(b)$ are in the same proof system, with the same commitment scheme, but different representations of the committed data
(e.g. representing $proof$ as a sequence of committed bytes and as a sequence of extension field elements),
one can even use a permutation argument such as in @logup to check equality of the data.
Comment on lines +285 to +287

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This subsection starts as

To enable recursion with a split verifier, we start by considering that each part of the split may be proven in a different proof system.

These sentences here related to a different situation, namely that of a single proof system. Yet, below, (after the aside) we continue with the split proof system discussion.

So, either:

  • we don't start by considering [...], but we just consider [...] (and these sentences are superfluous), or
  • we do in fact start with a split proof system, and this is a return to the joint proof system (which would be a weird order. Why not start with this?), but then the next paragraph should be moved up above here.
  • It is even different, and I don't get it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe s/considering/realizing at the start?
The start is justifying why we're introducing two potentially distinct proof systems into the discussion, whereas this sentence is clarifying what can we can do if they are not distinct.


#aside[Minimizing the length of $z$][
A larger $[proof, record]$ comes at a cost --- whether a cost of soundness or a cost of deriving more independent random $alpha_i$ ---
and as such should be kept as short as possible.
Since values that only one $verify'_(b)$ accesses do not influence $verify'_(1-b)$, the checksummed data need only
be the subsequence of elements of $[proof, record]$ that is accessed by both $verify'_0$ and $verify'_1$.

An even stronger version of this additionally allows re-ordering of the elements of this subsequence,
so that one or both of the verifier halves can have a simpler structure or ordering of its checks.
]

= Split-recursion
Observe that considering the proof
$ proof_sans("rec") = s || prove_0(verify'_0([comm(x), s], dot); [proof, record]) || prove_1(verify'_1([comm2(x), s], dot); [proof, record]) $
still allows us to treat the final output as a single proof, with a well-defined verifier.
As such, it is possible to construct a split verifier for this combined proof system, and enable full split recursion.

= Split recursion
With proof recursion and split verification formalized, we now combine both concepts into a unified system.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
With proof recursion and split verification formalized, we now combine both concepts into a unified system.
With proof recursion, split proving, and split verification formalized, we now combine the three concepts into a unified system.

To this end, we first introduce the selector function
$
&Delta_cal(A): cal(A)^2 times BB to cal(A): (x, y, b) mapsto cases(x & text("if") b=0, y & text("if") b=1),
$
which, given a bit $b in BB$ and two elements $x,y$ of some space $cal(A)$, selects
an element based on the value of $b$.
We can now use this selector to define $verify^*$ --- a variation to $verify'$ --- as follows:
We can now use this selector to define $verify^*$ as a variation on $verify'$.
With the right choice of proof system and commitment scheme $comm(x)$, $verify'$ and by extension
$verify^*$ can be applied directly to a proof string $pi$ that is either the output of a base proof,
or a recursive proof that is potentially split as in the previous section.
Comment on lines -251 to +315

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have given it some thought, and think I know why this doesn't work as we would like it to: the split-proving section is missing some stuff.

The story up to this point has been:

  • we start this chapter with introducing recursion,
  • we bypass a growing instance, by introducing $v'$,
  • split verification: we show how to convert a verifier into a split verifier.
  • split proving: we show how to accelerate a split verifier by means of proving each split in different VMs

I would now expect this section to combine $v'$ and split-verification+split-proving. But that doesn't happen.

In fact, we don't yet have the tools to do this: the $v'$-transformation shows how to morph the verifier of a proof system $(p,v)$ into one that can prove that one of two instances is satisfied. In the split proving section, we just pull two proof system provers p0 and p1 out of thin air and don't introduce the accompanying verifiers $v0$ and $v1$ (note: I'm overloading notation here). Neither do we show how one can merge $v0$ and $v1$ into the single verifier $V$ (that can verify proofs that consist of two halves from different proof systems).

Only once we have established those things, can we

  1. apply the $v'$ transformation to $V$ (allowing it to verify two instances), creating $V'$, and
  2. then split $V'$ into $(V_0', V_1', d_{V'})$

before we arrive at a split verifier that can verify proofs for two instances using two verifiers proven by two distinct provers.

Right now, we're essentially asking the reader to take all those mental steps in these two sentences. That is too much.


$
verify^*([comm(x), comm(y)], [proof, b])
:&= Delta_BB (verify(comm(x), proof), verify(comm(y([comm(x), comm(y)], dot)), proof), b)\
Expand Down
Loading