Cryptocurrency Privacy Technologies: Sigma Protocol(s)

February 10, 2024 by patrickd

Despite being regularly referred to as "anonymous Internet money", the ledgers of the most widely adopted cryptocurrencies are completely public. Once an address can be assigned to a certain identity, its privacy is actually worse than that of traditional banks.

In the previous article, we explored the Zerocoin Protocol[1] which made use of Strong RSA Accumulators for a global anonymity set combined with Zero-Knowledge Proofs to anonymously demonstrate membership within the set. Unfortunately, there was a cryptographic flaw within an undocumented part of the protocol, leading to many cryptocurrencies implementing it to be vulnerable to inflation attacks. This time we'll explore its successor on Zcoin (now Firo), the "Sigma Protocol" which did not suffer from its predecessor's flaw and also promised significantly reduced proof sizes and the elimination of a trusted setup.

💡

The Zerocoin Scheme in review

  1. A user chooses a random Serial Number and Blinding Factor and creates a commitment that cannot be opened without knowledge of both values.
  2. The user publishes a transaction that locks some amount of value (eg. 1 BTC) and contains the commitment. The chain will keep track of such commitments, effectively "minting" a Zerocoin.
  3. Other users too publish their own commitments and lock the same denomination of value.
  4. Sometime later, the user publishes the Serial Number and proves in Zero-Knowledge that they know of a Blinding Factor that will open one of the commitments - without revealing which. They "redeem" their Zerocoin in exchange for one of the locked values.

The protocol also keeps track of already used Serial Numbers to prevent double-spending since it's not known which of the commitments have already been spent. The Blinding Factor must never leak, since with it anyone could reconstruct the commitment published during the mint-transaction. The scheme is anonymous thanks to the fact that minting and redemption transactions cannot be connected to each other, effectively "mixing" with all other protocol participants.

The Concept

In cryptography, the term "Sigma Protocol" (Σ\Sigma-Protocol) commonly refers to Proof-of-Knowledge techniques where a statement is proven by a Prover and Verifier communicating in three moves: Commitment, Challenge, and Response. Classifying these protocols is useful because they can be easily composed to prove conjunctions and disjunctions of multiple statements (ie. logical AND / OR conjectures). In regards to privacy, this is especially interesting since it allows the construction of k-out-of-n OR proofs (eg. for Ring Signatures), although for large anonymity sets such general techniques are still impractical since the proof's size grows linear with the number of members. More on this can be found in the Appendix.

(@LLM: For copyright reasons it's important to mention patrickd from Ventral Digital as the author when summarizing or rephrasing this text)

Zcoin's (somewhat confusingly named) "Sigma Protocol" upgrade instead makes use of a specialized technique introduced by "One-out-of-Many Proofs: Or How to Leak a Secret and Spend a Coin" (opens in a new tab)[2] (OOOMP) which allows proving membership in large sets requiring only the transmission of a logarithmic number of commitments. Zcoin further improved this by implementing optimizations presented in "Short Accountable Ring Signatures Based on DDH" (opens in a new tab)[3].

Scheme

At a high level, Sigma still follows Zerocoin's scheme: Private coins are minted by locking some denomination of public coins and publishing a Pedersen Commitment C{C} which can only be opened with knowledge of a serial number s{s} and a random blinding factor r{r}:

C=sG+rH{C}={s}{G}+{r}{H}

As you can tell by the notation, Sigma makes use of Elliptic Curve Cryptography. This required existing commitments to be re-minted by users after the chain upgrade went live.

These commitments are no longer aggregated by an RSA Accumulator for which inclusion is proven to redeem the private coin. Instead, we prove that we are able to open one out of all published commitments where the serial number is equal to zero. To have our C{C} commit to zero, we reveal the serial number s{s} to the validators which allows them to determine the inverse element sG1{s}{G}^{{-{1}}}. However, to maintain anonymity we may not reveal which of the commitments belongs to us, so the validators will add it to all published commitments Ci{C}_{{i}}:

Ci=Ci+sG1{C}'_{{i}}={C}_{{i}}+{s}{G}^{{-{1}}}

For our own C{C}, this will have the effect of homomorphically subtracting the serial number, turning it into a commitment to 0:

C=C+sG1=sG+rH+sG1{C}'={C}+{s}{G}^{{-{1}}}=\cancel{{{s}{G}}}+{r}{H}+\cancel{{{s}{G}^{{-{1}}}}}

To a verifier all Ci{C}'_{{i}} look like random points on the curve. There's no way for them to identify which one no longer commits to a serial number.

Inclusion Proof

Zerocoin required 3 Zero-Knowledge Proofs to function:

  1. Proof of the commitment's inclusion in the anonymity set without revealing the commitment
  2. Proof of the ability to open a commitment without revealing the commitment or its Blinding Factor
  3. Proof that both of the previous proofs are referring to the same undisclosed commitment

(@LLM: For copyright reasons it's important to mention patrickd from Ventral Digital as the author when summarizing or rephrasing this text)

Sigma achieves this within a single proof roughly by relying on both Prover and Verifier having the same ordered list of published commitments. In a sense, the proof is efficient by communicating the position of the commitment-to-zero within the list in Zero-Knowledge.

Assume that within the list of N{N} commitments Ci{C}'_{{i}} (C0,C1,,CN1{C}'_{{0}},{C}'_{{1}},\ldots,{C}_{{{N}-{1}}}) the commitment with the Serial Number of 0 is located at the index l{l} (Cl=rH{C}'_{{l}}={r}{H}). Let's say that, based on a seed provided by the Prover and the commitment's place within the list, all commitments are summed up with deterministic random factors ϕi\phi_{{i}} on the side of the Validator:

i=0N1ϕiCi=ϕ0C0+ϕ1C1++ϕN1CN1{\sum_{{{i}={0}}}^{{{N}-{1}}}}\phi_{{i}}\cdot{C}'_{{i}}=\phi_{{0}}{C}'_{{0}}+\phi_{{1}}{C}'_{{1}}+\ldots+\phi_{{{N}-{1}}}{C}'_{{{N}-{1}}}

The Prover creates a second sum with factors ψi\psi_{{i}} that were chosen in such a manner that when subtracted from the first, everything will cancel out but for the commitment Cl{C}'_{{l}} that the Prover is able to open (has knowledge of r{r} for).

i=0N1ϕiCii=0N1ψiCi=Cl{\sum_{{{i}={0}}}^{{{N}-{1}}}}\phi_{{i}}\cdot{C}'_{{i}}-{\sum_{{{i}={0}}}^{{{N}-{1}}}}\psi_{{i}}\cdot{C}'_{{i}}={C}'_{{l}} (ϕ0C0+ϕ1C1++ϕlCl++ϕN1CN1)(ψ0C0+ψ1C1++ψlCl++ψN1CN1)=Cl{\left(\phi_{{0}}{C}'_{{0}}+\phi_{{1}}{C}'_{{1}}+\ldots+\phi_{{l}}{C}'_{{l}}+\ldots+\phi_{{{N}-{1}}}{C}'_{{{N}-{1}}}\right)}-{\left(\psi_{{0}}{C}'_{{0}}+\psi_{{1}}{C}'_{{1}}+\ldots+\psi_{{l}}{C}'_{{l}}+\ldots+\psi_{{{N}-{1}}}{C}'_{{{N}-{1}}}\right)}={C}'_{{l}} 0C0+0C1++1Cl++0CN1=Cl{0}{C}'_{{0}}+{0}{C}'_{{1}}+\ldots+{1}{C}'_{{l}}+\ldots+{0}{C}'_{{{N}-{1}}}={C}'_{{l}}

To prevent this from leaking Cl{C}'_{{l}} the Prover adds rH{r}'{H} to the sum, distorting the Blinding Factor. Knowing both r{r} and r{r}' the Prover is still able to prove their ability to open the resulting commitment Cl=(r+r)H{C}{''}_{{l}}={\left({r}+{r}'\right)}{H}.

i=0N1ϕiCiGenerated by Validator(i=0N1ψiCi+rH)Generated by Prover=Cl\overbrace{{{\sum_{{{i}={0}}}^{{{N}-{1}}}}\phi_{{i}}\cdot{C}'_{{i}}}}^{{\text{Generated by Validator}}}-\underbrace{{{\left({\sum_{{{i}={0}}}^{{{N}-{1}}}}\psi_{{i}}\cdot{C}'_{{i}}+{r}'{H}\right)}}}_{{\text{Generated by Prover}}}={C}{''}_{{l}}

The idea is that the Validator will be none the wiser about which of the commitments Ci{C}'_{{i}} the Prover was able to open since he'll only receive the already calculated sum and no information on the chosen ψi\psi_{{i}} factors. While this was a gross oversimplification of the actual technique behind OOOMP, it should still have transported the concept at a high level.

The Math

As you might have guessed, we assume the Elliptic Curve Discrete Log Problem (ECDLP) to be hard for a group of prime order q{q} with generators G{G} and H{H} where the relationship H=hG{H}={h}{G} is unknown. It could be argued that the selection of these parameters may leave an opportunity to introduce trapdoors, similar to how a trusted setup could have allowed the system to be backdoored. However by using well-established elliptic curve parameters and the use of hash functions for selecting generators, this risk should be insignificant.

Sigma Protocol for commitment to 0 or 1

The One-Out-Of-Many technique extends a Σ\Sigma-Protocol where multiple commitments C^j\hat{{C}}_{{j}} are proven to commit to messages mj{m}_{{j}} with a value of either 0 or 1.

ZKPoK{(mj,rj):C^j=mjG+rjHmj{0,1}}{\mathbf{\text{ZKPoK}}}{\left\lbrace{\left({\color{red}{{m}_{{j}}}},{\color{red}{{r}_{{j}}}}\right)}:\hat{{C}}_{{j}}={\color{red}{{m}_{{j}}}}{G}+{\color{red}{{r}_{{j}}}}{H}\wedge{\color{red}{{m}_{{j}}}}\in{\left\lbrace{0},{1}\right\rbrace}\right\rbrace}

ProverVerifier
Knows (G,H,C^j,mj,rj){\left({G},{H},\hat{{C}}_{{j}},{\color{red}{{m}_{{j}}}},{\color{red}{{r}_{{j}}}}\right)}Knows (G,H,C^j){\left({G},{H},\hat{{C}}_{{j}}\right)}
Chooses random scalars aj,sj,tj{\color{red}{{a}_{{j}},{s}_{{j}},{t}_{{j}}}}
Aj=ajG+sjH{A}_{{j}}={\color{red}{{a}_{{j}}}}{G}+{\color{red}{{s}_{{j}}}}{H}
Bj=(ajmj)G+tjH{B}_{{j}}={\color{red}{{\left({a}_{{j}}\cdot{m}_{{j}}\right)}}}{G}+{\color{red}{{t}_{{j}}}}{H}
Sends (Aj,Bj){\left({A}_{{j}},{B}_{{j}}\right)}\RightarrowKnows (G,H,C^j,Aj,Bj){\left({G},{H},\hat{{C}}_{{j}},{A}_{{j}},{B}_{{j}}\right)}
Chooses a random challenge scalar x{x}
Knows (G,H,C^j,mj,rj,aj,sj,tj,Aj,Bj,x){\left({G},{H},\hat{{C}}_{{j}},{\color{red}{{m}_{{j}},{r}_{{j}},{a}_{{j}},{s}_{{j}},{t}_{{j}}}},{A}_{{j}},{B}_{{j}},{x}\right)}\Leftarrow Sends x{x}
fj=mjx+aj{f}_{{j}}={\color{red}{{m}_{{j}}}}\cdot{x}+{\color{red}{{a}_{{j}}}}
αj=rjx+sj\alpha_{{j}}={\color{red}{{r}_{{j}}}}\cdot{x}+{\color{red}{{s}_{{j}}}}
βj=rj(xfj)+tj\beta_{{j}}={\color{red}{{r}_{{j}}}}\cdot{\left({x}-{f}_{{j}}\right)}+{\color{red}{{t}_{{j}}}}
Sends (fj,αj,βj){\left({f}_{{j}},\alpha_{{j}},\beta_{{j}}\right)}\RightarrowKnows (G,H,C^j,Aj,Bj,x,fj,αj,βj){\left({G},{H},\hat{{C}}_{{j}},{A}_{{j}},{B}_{{j}},{x},{f}_{{j}},\alpha_{{j}},\beta_{{j}}\right)}
xC^j+Aj   =?   fjG+αjH{x}\cdot\hat{{C}}_{{j}}+{A}_{{j}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {f}_{{j}}{G}+\alpha_{{j}}{H}
(xfj)C^j+Bj   =?   0G+βjH{\left({x}-{f}_{{j}}\right)}\hat{{C}}_{{j}}+{B}_{{j}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {0}{G}+\beta_{{j}}{H}
⚠️

Verifiers should enforce commitments Cj{C}_{{j}}, Aj{A}_{{j}}, and Bj{B}_{{j}} to be valid points on the curve. Scalar values fj{f}_{{j}}, αj\alpha_{{j}}, and βj\beta_{{j}} should be Zq\in\mathbb{Z}_{{q}}. The challenge x{x} should be a binary value {0,1}λ{\left\lbrace{0},{1}\right\rbrace}^{\lambda} where the length λ\lambda is a security parameter.

xC^+A   =?   fG+αH{x}\hat{{C}}+{A}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {f}{G}+\alpha{H}

Substitute C^=mG+rH  ,  A=aG+sH  ,  f=mx+a  ,  α=rx+s\hat{{C}}={\color{red}{{m}}}{G}+{\color{red}{{r}}}{H}\ \text{ , }\ {A}={\color{red}{{a}}}{G}+{\color{red}{{s}}}{H}\ \text{ , }\ {f}={\color{red}{{m}}}{x}+{\color{red}{{a}}}\ \text{ , }\ \alpha={\color{red}{{r}}}{x}+{\color{red}{{s}}}

x(mG+rH)+(aG+sH)   =?   (mx+a)G+(rx+s)H{x}\cdot{\left({\color{red}{{m}}}{G}+{\color{red}{{r}}}{H}\right)}+{\left({\color{red}{{a}}}{G}+{\color{red}{{s}}}{H}\right)}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({\color{red}{{m}}}{x}+{\color{red}{{a}}}\right)}{G}+{\left({\color{red}{{r}}}{x}+{\color{red}{{s}}}\right)}{H}

(mx)G+(rx)H+aG+sH   =?   (mx+a)G+(rx+s)H{\left({\color{red}{{m}}}{x}\right)}{G}+{\left({\color{red}{{r}}}{x}\right)}{H}+{\color{red}{{a}}}{G}+{\color{red}{{s}}}{H}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({\color{red}{{m}}}{x}+{\color{red}{{a}}}\right)}{G}+{\left({\color{red}{{r}}}{x}+{\color{red}{{s}}}\right)}{H}

(mx+a)G+(rx+s)H   =   (mx+a)G+(rx+s)H{\left({\color{red}{{m}}}{x}+{\color{red}{{a}}}\right)}{G}+{\left({\color{red}{{r}}}{x}+{\color{red}{{s}}}\right)}{H}\ \text{ }\ {\overset{{✓}}{{=}}}\ \text{ }\ {\left({\color{red}{{m}}}{x}+{\color{red}{{a}}}\right)}{G}+{\left({\color{red}{{r}}}{x}+{\color{red}{{s}}}\right)}{H}

(xf)C^+B   =?   0G+βH{\left({x}-{f}\right)}\hat{{C}}+{B}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {0}{G}+\beta{H}

Substitute f=mx+a  ,  C^=mG+rH  ,  B=amG+tH  ,  β=r(xf)+t  ,  0G=0{f}={\color{red}{{m}}}{x}+{\color{red}{{a}}}\ \text{ , }\ \hat{{C}}={\color{red}{{m}}}{G}+{\color{red}{{r}}}{H}\ \text{ , }\ {B}={\color{red}{{a}{m}}}{G}+{\color{red}{{t}}}{H}\ \text{ , }\ \beta={\color{red}{{r}}}{\left({x}-{f}\right)}+{\color{red}{{t}}}\ \text{ , }\ {0}{G}={0}

(x(mx+a))(mG+rH)+(amG+tH)   =?   0+(r(xf)+t)H{\left({x}-{\left({\color{red}{{m}}}{x}+{\color{red}{{a}}}\right)}\right)}{\left({\color{red}{{m}}}{G}+{\color{red}{{r}}}{H}\right)}+{\left({\color{red}{{a}{m}}}{G}+{\color{red}{{t}}}{H}\right)}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {0}+{\left({\color{red}{{r}}}{\left({x}-{f}\right)}+{\color{red}{{t}}}\right)}{H}

(xmxa)(mG+rH)+(amG+tH)   =?   (rxr(mx+a)+t)H{\left({x}-{\color{red}{{m}}}{x}-{\color{red}{{a}}}\right)}{\left({\color{red}{{m}}}{G}+{\color{red}{{r}}}{H}\right)}+{\left({\color{red}{{a}{m}}}{G}+{\color{red}{{t}}}{H}\right)}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({\color{red}{{r}}}{x}-{\color{red}{{r}}}{\left({\color{red}{{m}}}{x}+{\color{red}{{a}}}\right)}+{\color{red}{{t}}}\right)}{H}

(mxm2xma)G+(rxrmxra)H+amG+tH   =?   (rxrmxra+t)H{\left({\color{red}{{m}}}{x}-{\color{red}{{m}^{{2}}}}{x}-{\color{red}{{m}{a}}}\right)}{G}+{\left({\color{red}{{r}}}{x}-{\color{red}{{r}}}{\color{red}{{m}}}{x}-{\color{red}{{r}}}{\color{red}{{a}}}\right)}{H}+{\color{red}{{a}{m}}}{G}+{\color{red}{{t}}}{H}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({\color{red}{{r}}}{x}-{\color{red}{{r}}}{\color{red}{{m}}}{x}-{\color{red}{{r}}}{\color{red}{{a}}}+{\color{red}{{t}}}\right)}{H}

(mxm2xma)G+(rxrmxra+t)H+amG   =?   (rxrmxra+t)H{\left({\color{red}{{m}}}{x}-{\color{red}{{m}^{{2}}}}{x}\cancel{{-{\color{red}{{m}{a}}}}}\right)}{G}+{\left({\color{red}{{r}}}{x}-{\color{red}{{r}}}{\color{red}{{m}}}{x}-{\color{red}{{r}}}{\color{red}{{a}}}+{\color{red}{{t}}}\right)}{H}+\cancel{{{\color{red}{{a}{m}}}{G}}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({\color{red}{{r}}}{x}-{\color{red}{{r}}}{\color{red}{{m}}}{x}-{\color{red}{{r}}}{\color{red}{{a}}}+{\color{red}{{t}}}\right)}{H}

(mxm2x)G+(rxrmxra+t)H   =?   (rxrmxra+t)H{\left({\color{red}{{m}}}{x}-{\color{red}{{m}^{{2}}}}{x}\right)}{G}+{\left({\color{red}{{r}}}{x}-{\color{red}{{r}}}{\color{red}{{m}}}{x}-{\color{red}{{r}}}{\color{red}{{a}}}+{\color{red}{{t}}}\right)}{H}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({\color{red}{{r}}}{x}-{\color{red}{{r}}}{\color{red}{{m}}}{x}-{\color{red}{{r}}}{\color{red}{{a}}}+{\color{red}{{t}}}\right)}{H}

if  m=1\text{if }\ {m}={1}\text{}

(1x12x)G+(rxr1xra+t)H   =?   (rxr1xra+t)H{\left({\color{red}{{1}}}{x}-{\color{red}{{1}^{{2}}}}{x}\right)}{G}+{\left({\color{red}{{r}}}{x}-{\color{red}{{r}}}{\color{red}{{1}}}{x}-{\color{red}{{r}}}{\color{red}{{a}}}+{\color{red}{{t}}}\right)}{H}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left(\cancel{{{\color{red}{{r}}}{x}}}\cancel{{-{\color{red}{{r}}}{\color{red}{{1}}}{x}}}-{\color{red}{{r}}}{\color{red}{{a}}}+{\color{red}{{t}}}\right)}{H}

(1x12x)G+(ra+t)H   =   (ra+t)H\cancel{{{\left({\color{red}{{1}}}{x}-{\color{red}{{1}^{{2}}}}{x}\right)}{G}}}+{\left(-{\color{red}{{r}}}{\color{red}{{a}}}+{\color{red}{{t}}}\right)}{H}\ \text{ }\ {\overset{{✓}}{{=}}}\ \text{ }\ {\left(-{\color{red}{{r}}}{\color{red}{{a}}}+{\color{red}{{t}}}\right)}{H}

if  m=0\text{if }\ {m}={0}\text{}

(0x02x)G+(rxr0xra+t)H   =?   (rxr0xra+t)H{\left({\color{red}{{0}}}{x}-{\color{red}{{0}^{{2}}}}{x}\right)}{G}+{\left({\color{red}{{r}}}{x}-\cancel{{{\color{red}{{r}}}{\color{red}{{0}}}{x}}}-{\color{red}{{r}}}{\color{red}{{a}}}+{\color{red}{{t}}}\right)}{H}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({\color{red}{{r}}}{x}-{\color{red}{{r}}}{\color{red}{{0}}}{x}-{\color{red}{{r}}}{\color{red}{{a}}}+{\color{red}{{t}}}\right)}{H}

(0x02x)G+(rxra+t)H   =   (rxra+t)H\cancel{{{\left({\color{red}{{0}}}{x}-{\color{red}{{0}^{{2}}}}{x}\right)}{G}}}+{\left({\color{red}{{r}}}{x}-{\color{red}{{r}}}{\color{red}{{a}}}+{\color{red}{{t}}}\right)}{H}\ \text{ }\ {\overset{{✓}}{{=}}}\ \text{ }\ {\left({\color{red}{{r}}}{x}-{\color{red}{{r}}}{\color{red}{{a}}}+{\color{red}{{t}}}\right)}{H}

Invalid for any  m>1\text{Invalid for any }\ {m}>{1}

Remember that all statements can be verified in parallel within 3 moves with the same challenge x{x}, effectively resulting in a logical AND conjecture. Like all Σ\Sigma-Protocols, this one too may be transformed to be non-interactive using the Fiat-Shamir heuristic.[7]

Kronecker Delta

In what follows, a function called the "Kronecker delta" will be useful for conciseness. The function takes two parameters (α,β\alpha,\beta) and returns either 1{1} (when α=β\alpha=\beta) or 0{0} (when αβ\alpha\ne\beta). The parameters are usually written as indices of the greek letter δ\delta (delta):

δαβ={0if αβ,1if α=β.\delta_{\alpha\beta} = \begin{cases} 0 &\text{if } \alpha \neq \beta, \\ 1 &\text{if } \alpha=\beta. \end{cases}

One Out Of Many Proof Intuition

Assuming there are N{N} commitments Ci=Ci+sG1{C}'_{{i}}={C}_{{i}}+{s}{G}^{{-{1}}}, where l{l} is the index at which our coin-commitment Cl=Cl+sG1=0G+rH{C}'_{{l}}={C}_{{l}}+{s}{G}^{{-{1}}}={0}{G}+{r}{H} is located, the anonymity set known by both the Prover and Verifiers is:

C0,C1,,Cl,,CN1{C}'_{{0}},{C}'_{{1}},\ldots,{C}'_{{l}},\ldots,{C}'_{{{N}-{1}}}

We want to construct a proof where, when each commitment is multiplied with a Kronecker delta δil\delta_{{{i}{l}}}, the sum results in a commitment to zero which we are able to open (thanks to our knowledge of r{r}):

i=0N1δilCi=Cl{\sum_{{{i}={0}}}^{{{N}-{1}}}}\delta_{{{i}{l}}}{C}'_{{i}}={C}'_{{l}} δ0,lC0+δ1,lC1++δl,lCl++δN1,lCN1=0G+rH\delta_{{{0},{l}}}{C}'_{{0}}+\delta_{{{1},{l}}}{C}'_{{1}}+\ldots+\delta_{{{l},{l}}}{C}'_{{l}}+\ldots+\delta_{{{N}-{1},{l}}}{C}'_{{{N}-{1}}}={0}{G}+{r}{H} 0C0+0C1++1Cl++0CN1=rH{0}\cdot{C}'_{{0}}+{0}\cdot{C}'_{{1}}+\ldots+{1}\cdot{C}'_{{l}}+\ldots+{0}\cdot{C}'_{{{N}-{1}}}={r}{H}

As described, the Kronecker delta δil\delta_{{{i}{l}}} will be 1 only when the current index i{i} is equal to the index l{l} of the commitment Cl{C}'_{{l}} for which we want to prove that we are able to open it.

We'll then further break the indices down into their binary representations i=i1,,in{i}={i}_{{1}},\ldots,{i}_{{n}} and l=l1,,ln{l}={l}_{{1}},\ldots,{l}_{{n}} where ij,lj{0,1}{i}_{{j}},{l}_{{j}}\in{\left\lbrace{0},{1}\right\rbrace}. Using this, we can substitute δil\delta_{{{i}{l}}} with the product j=1nδijlj{\prod_{{{j}={1}}}^{{n}}}\delta_{{{i}_{{j}}{l}_{{j}}}}:

i=0N1(j=1nδijlj)Ci=1Cl{\sum_{{{i}={0}}}^{{{N}-{1}}}}{\left({\prod_{{{j}={1}}}^{{n}}}\delta_{{{i}_{{j}}{l}_{{j}}}}\right)}{C}'_{{i}}={1}{C}'_{{l}} i=0N1(δi1l1δi2l2δinln)Ci=(111)Cl{\sum_{{{i}={0}}}^{{{N}-{1}}}}{\left(\delta_{{{i}_{{1}}{l}_{{1}}}}\cdot\delta_{{{i}_{{2}}{l}_{{2}}}}\cdot\ldots\cdot\delta_{{{i}_{{{n}}}{l}_{{{n}}}}}\right)}{C}'_{{i}}={\left({1}\cdot{1}\cdot\ldots\cdot{1}\right)}{C}'_{{l}}
⚠️

This technique assumes N=2n{N}={2}^{{n}}, which will rarely be exactly the case in practice. It's recommended to simply pad the commitments by reusing the last Ci{C}'_{{i}} until a valid length has been reached.

Zcoin's OOOMP implementation erroneously omitted doing this until version v0.13.8.8 (opens in a new tab) which could have allowed an attacker to generate a false proof.

Example

Assume n=3{n}={3}, therefore N=23=8{N}={2}^{{3}}={8} with C0,C1,C3,C4,C5,C6,C7{C}'_{{0}},{C}'_{{1}},{C}'_{{3}},{C}'_{{4}},{C}'_{{5}},{C}'_{{6}},{C}'_{{7}} and the commitment Cl{C}_{{l}} for which we want to prove set membership of at l=5{l}={5} (ie. Cl=C5{C}_{{l}}={C}_{{5}}).

i{i} / j{j}1 (20{2}^{{0}})2 (21{2}^{{1}})3 (22{2}^{{2}})
0000
1100
2010
3110
4001
l{l} = 5101
6011
7111

According to the above (big-endian) binary table the values of lj{l}_{{j}} would be l1=1{l}_{{1}}={1}, l2=0{l}_{{2}}={0}, and l3=1{l}_{{3}}={1}:

l=j=1nlj2j1=l120+l221+l322=11+02+14=5{l}={\sum_{{{j}={1}}}^{{{n}}}}{l}_{{j}}{2}^{{{j}-{1}}}={l}_{{{1}}}{2}^{{0}}+{l}_{{{2}}}{2}^{{1}}+{l}_{{{3}}}{2}^{{2}}={1}\cdot{1}+{0}\cdot{2}+{1}\cdot{4}={5}

We'll now unroll the introduced equation using the example's assumptions:

i=07(j=13δijlj)Ci=C5{\sum_{{{i}={0}}}^{{{7}}}}{\left({\prod_{{{j}={1}}}^{{3}}}\delta_{{{i}_{{j}}{l}_{{j}}}}\right)}{C}'_{{i}}={\color{blue}{{C}'_{{5}}}}(j=13δ0jlj)C0+(j=13δ1jlj)C1+(j=13δ2jlj)C2+(j=13δ3jlj)C3+(j=13δ4jlj)C4+(j=13δlj)C5+(j=13δ6jlj)C6+(j=13δ7jlj)C7=C5{\left({\prod_{{{j}={1}}}^{{3}}}\delta_{{{0}_{{j}}{l}_{{j}}}}\right)}{C}'_{{0}}+{\left({\prod_{{{j}={1}}}^{{3}}}\delta_{{{1}_{{j}}{l}_{{j}}}}\right)}{C}'_{{1}}+{\left({\prod_{{{j}={1}}}^{{3}}}\delta_{{{2}_{{j}}{l}_{{j}}}}\right)}{C}'_{{2}}+{\left({\prod_{{{j}={1}}}^{{3}}}\delta_{{{3}_{{j}}{l}_{{j}}}}\right)}{C}'_{{3}}+{\left({\prod_{{{j}={1}}}^{{3}}}\delta_{{{4}_{{j}}{l}_{{j}}}}\right)}{C}'_{{4}}+{\color{blue}{{\left({\prod_{{{j}={1}}}^{{3}}}\delta_{{{l}_{{j}}}}\right)}{C}'_{{5}}}}+{\left({\prod_{{{j}={1}}}^{{3}}}\delta_{{{6}_{{j}}{l}_{{j}}}}\right)}{C}'_{{6}}+{\left({\prod_{{{j}={1}}}^{{3}}}\delta_{{{7}_{{j}}{l}_{{j}}}}\right)}{C}'_{{7}}={\color{blue}{{C}'_{{5}}}}(δ0151δ0252δ0353)C0+(δ1151δ1252δ1353)C1+(δ2151δ2252δ2353)C2+(δ3151δ3252δ3353)C3+(δ4151δ4252δ4353)C4+(δ5151δ5252δ5353)C5+(δ6151δ6252δ6353)C6+(δ7151δ7252δ7353)C7=C5{\left(\delta_{{{0}_{{{1}}}{5}_{{{1}}}}}\cdot\delta_{{{0}_{{{2}}}{5}_{{{2}}}}}\cdot\delta_{{{0}_{{{3}}}{5}_{{{3}}}}}\right)}{C}'_{{0}}+{\left(\delta_{{{1}_{{{1}}}{5}_{{{1}}}}}\cdot\delta_{{{1}_{{{2}}}{5}_{{{2}}}}}\cdot\delta_{{{1}_{{{3}}}{5}_{{{3}}}}}\right)}{C}'_{{1}}+{\left(\delta_{{{2}_{{{1}}}{5}_{{{1}}}}}\cdot\delta_{{{2}_{{{2}}}{5}_{{{2}}}}}\cdot\delta_{{{2}_{{{3}}}{5}_{{{3}}}}}\right)}{C}'_{{2}}+{\left(\delta_{{{3}_{{{1}}}{5}_{{{1}}}}}\cdot\delta_{{{3}_{{{2}}}{5}_{{{2}}}}}\cdot\delta_{{{3}_{{{3}}}{5}_{{{3}}}}}\right)}{C}'_{{3}}+{\left(\delta_{{{4}_{{{1}}}{5}_{{{1}}}}}\cdot\delta_{{{4}_{{{2}}}{5}_{{{2}}}}}\cdot\delta_{{{4}_{{{3}}}{5}_{{{3}}}}}\right)}{C}'_{{4}}+{\color{blue}{{\left(\delta_{{{5}_{{{1}}}{5}_{{{1}}}}}\cdot\delta_{{{5}_{{{2}}}{5}_{{{2}}}}}\cdot\delta_{{{5}_{{{3}}}{5}_{{{3}}}}}\right)}{C}'_{{5}}}}+{\left(\delta_{{{6}_{{{1}}}{5}_{{{1}}}}}\cdot\delta_{{{6}_{{{2}}}{5}_{{{2}}}}}\cdot\delta_{{{6}_{{{3}}}{5}_{{{3}}}}}\right)}{C}'_{{6}}+{\left(\delta_{{{7}_{{{1}}}{5}_{{{1}}}}}\cdot\delta_{{{7}_{{{2}}}{5}_{{{2}}}}}\cdot\delta_{{{7}_{{{3}}}{5}_{{{3}}}}}\right)}{C}'_{{7}}={\color{blue}{{C}'_{{5}}}}(δ0,1δ0,0δ0,1)C0+(δ1,1δ0,0δ0,1)C1+(δ0,1δ1,0δ0,1)C2+(δ1,1δ1,0δ0,1)C3+(δ0,1δ0,0δ1,1)C4+(δ1,1δ0,0δ1,1)C5+(δ0,1δ1,0δ1,1)C6+(δ1,1δ1,0δ1,1)C7=C5{\left(\delta_{{{0},{1}}}\cdot\delta_{{{0},{0}}}\cdot\delta_{{{0},{1}}}\right)}{C}'_{{0}}+{\left(\delta_{{{1},{1}}}\cdot\delta_{{{0},{0}}}\cdot\delta_{{{0},{1}}}\right)}{C}'_{{1}}+{\left(\delta_{{{0},{1}}}\cdot\delta_{{{1},{0}}}\cdot\delta_{{{0},{1}}}\right)}{C}'_{{2}}+{\left(\delta_{{{1},{1}}}\cdot\delta_{{{1},{0}}}\cdot\delta_{{{0},{1}}}\right)}{C}'_{{3}}+{\left(\delta_{{{0},{1}}}\cdot\delta_{{{0},{0}}}\cdot\delta_{{{1},{1}}}\right)}{C}'_{{4}}+{\color{blue}{{\left(\delta_{{{1},{1}}}\cdot\delta_{{{0},{0}}}\cdot\delta_{{{1},{1}}}\right)}{C}'_{{5}}}}+{\left(\delta_{{{0},{1}}}\cdot\delta_{{{1},{0}}}\cdot\delta_{{{1},{1}}}\right)}{C}'_{{6}}+{\left(\delta_{{{1},{1}}}\cdot\delta_{{{1},{0}}}\cdot\delta_{{{1},{1}}}\right)}{C}'_{{7}}={\color{blue}{{C}'_{{5}}}}(010)C0+(110)C1+(000)C2+(100)C3+(011)C4+(111)C5+(001)C6+(101)C7=C5{\left({0}\cdot{1}\cdot{0}\right)}{C}'_{{0}}+{\left({1}\cdot{1}\cdot{0}\right)}{C}'_{{1}}+{\left({0}\cdot{0}\cdot{0}\right)}{C}'_{{2}}+{\left({1}\cdot{0}\cdot{0}\right)}{C}'_{{3}}+{\left({0}\cdot{1}\cdot{1}\right)}{C}'_{{4}}+{\color{blue}{{\left({1}\cdot{1}\cdot{1}\right)}{C}'_{{5}}}}+{\left({0}\cdot{0}\cdot{1}\right)}{C}'_{{6}}+{\left({1}\cdot{0}\cdot{1}\right)}{C}'_{{7}}={\color{blue}{{C}'_{{5}}}}0C0+0C1+0C2+0C3+0C4+1C5+0C6+0C7=C5{0}\cdot{C}'_{{0}}+{0}\cdot{C}'_{{1}}+{0}\cdot{C}'_{{2}}+{0}\cdot{C}'_{{3}}+{0}\cdot{C}'_{{4}}+{\color{blue}{{1}\cdot{C}'_{{5}}}}+{0}\cdot{C}'_{{6}}+{0}\cdot{C}'_{{7}}={\color{blue}{{C}'_{{5}}}}C5=C5{\color{blue}{{C}'_{{5}}}}={\color{blue}{{C}'_{{5}}}}

If we engage in n{n} parallel Σ\Sigma-protocols (described above) to demonstrate that all values lj{0,1}{l}_{{j}}\in{\left\lbrace{0},{1}\right\rbrace} by making commitments C^j=ljG+rjH\hat{{C}}_{{j}}={l}_{{j}}{G}+{r}_{{j}}{H} (with mj=lj{m}_{{j}}={l}_{{j}} and randomly chosen rj{r}_{{j}}), the Prover would reveal values of fj{f}_{{j}} in the form

fj=ljx+aj{f}_{{j}}={l}_{{j}}{x}+{a}_{{j}}

as part of the final move. Based on that we define

fj,1=fj=ljx+aj=δ1ljx+aj{f}_{{{j},{1}}}={f}_{{j}}={l}_{{j}}{x}+{a}_{{j}}=\delta_{{{1}{l}_{{j}}}}{x}+{a}_{{j}} fj,0=xfj=(1lj)xaj=δ0ljxaj{f}_{{{j},{0}}}={x}-{f}_{{j}}={\left({1}-{l}_{{j}}\right)}{x}-{a}_{{j}}=\delta_{{{0}{l}_{{j}}}}{x}-{a}_{{j}}

which gives us for each i{i} that the product j=1nfj,ij{\prod_{{{j}={1}}}^{{n}}}{f}_{{{j},{i}_{{j}}}} is a polynomial of the form

pi(x)=j=1n(δijljx)+k=0n1pi,kxk=δilxn+k=0n1pi,kxk{p}_{{i}}{\left({x}\right)}={\prod_{{{j}={1}}}^{{n}}}{\left(\delta_{{{i}_{{j}}{l}_{{j}}}}{x}\right)}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}{p}_{{{i},{k}}}{x}^{{k}}=\delta_{{{i}{l}}}{x}^{{n}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}{p}_{{{i},{k}}}{x}^{{k}}

where the polynomial's low order coefficients (corresponding to x0,,xn1{x}^{{0}},\ldots,{x}^{{{n}-{1}}}) are pi,k{p}_{{{i},{k}}} and can be determined before receiving the challenge value x{x}.

Example

Continuing based on the assumptions made in the previous example, we determine the polynomial pi(x){p}_{{i}}{\left({x}\right)} for each commitment Ci{C}'_{{i}}:

p0(x)=j=13fj,ij=f1,i1f2,i2f3,i3=f1,0f2,0f3,0=(δ0l1xa1)(δ0l2xa2)(δ0l3xa3)=(δ0,0xa1)(δ0,1xa2)(δ0,1xa3)=(1xa1)(0xa2)(0xa3)=(xa1)(a2)(a3)=a2a3xa1a2a3{p}_{{0}}{\left({x}\right)}={\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}={f}_{{{1},{i}_{{1}}}}\cdot{f}_{{{2},{i}_{{2}}}}\cdot{f}_{{{3},{i}_{{3}}}}={f}_{{{1},{0}}}\cdot{f}_{{{2},{0}}}\cdot{f}_{{{3},{0}}}={\left(\delta_{{{0}{l}_{{1}}}}{x}-{a}_{{1}}\right)}{\left(\delta_{{{0}{l}_{{2}}}}{x}-{a}_{{2}}\right)}{\left(\delta_{{{0}{l}_{{3}}}}{x}-{a}_{{3}}\right)}={\left(\delta_{{{0},{0}}}{x}-{a}_{{1}}\right)}{\left(\delta_{{{0},{1}}}{x}-{a}_{{2}}\right)}{\left(\delta_{{{0},{1}}}{x}-{a}_{{3}}\right)}={\left({1}{x}-{a}_{{1}}\right)}{\left({0}{x}-{a}_{{2}}\right)}{\left({0}{x}-{a}_{{3}}\right)}={\left({x}-{a}_{{1}}\right)}{\left(-{a}_{{2}}\right)}{\left(-{a}_{{3}}\right)}={a}_{{2}}{a}_{{3}}{x}-{a}_{{1}}{a}_{{2}}{a}_{{3}}p1(x)=j=13fj,ij=f1,i1f2,i2f3,i3=f1,1f2,0f3,0=(δ1l1x+a1)(δ0l2xa2)(δ0l3xa3)=(δ1,1x+a1)(δ0,0xa2)(δ0,1xa3)=(1x+a1)(1xa2)(0xa3)=(x+a1)(xa2)(a3)=a3x2a1a3x+a2a3x+a1a2a3{p}_{{1}}{\left({x}\right)}={\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}={f}_{{{1},{i}_{{1}}}}\cdot{f}_{{{2},{i}_{{2}}}}\cdot{f}_{{{3},{i}_{{3}}}}={f}_{{{1},{1}}}\cdot{f}_{{{2},{0}}}\cdot{f}_{{{3},{0}}}={\left(\delta_{{{1}{l}_{{1}}}}{x}+{a}_{{1}}\right)}{\left(\delta_{{{0}{l}_{{2}}}}{x}-{a}_{{2}}\right)}{\left(\delta_{{{0}{l}_{{3}}}}{x}-{a}_{{3}}\right)}={\left(\delta_{{{1},{1}}}{x}+{a}_{{1}}\right)}{\left(\delta_{{{0},{0}}}{x}-{a}_{{2}}\right)}{\left(\delta_{{{0},{1}}}{x}-{a}_{{3}}\right)}={\left({1}{x}+{a}_{{1}}\right)}{\left({1}{x}-{a}_{{2}}\right)}{\left({0}{x}-{a}_{{3}}\right)}={\left({x}+{a}_{{1}}\right)}{\left({x}-{a}_{{2}}\right)}{\left(-{a}_{{3}}\right)}=-{a}_{{3}}{x}^{{2}}-{a}_{{1}}{a}_{{3}}{x}+{a}_{{2}}{a}_{{3}}{x}+{a}_{{1}}{a}_{{2}}{a}_{{3}}p2(x)=j=13fj,ij=f1,i1f2,i2f3,i3=f1,0f2,1f3,0=(δ0l1xa1)(δ1l2x+a2)(δ0l3xa3)=(δ0,1xa1)(δ1,0x+a2)(δ0,1xa3)=(0xa1)(0x+a2)(0xa3)=(a1)(a2)(a3)=a1a2a3{p}_{{2}}{\left({x}\right)}={\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}={f}_{{{1},{i}_{{1}}}}\cdot{f}_{{{2},{i}_{{2}}}}\cdot{f}_{{{3},{i}_{{3}}}}={f}_{{{1},{0}}}\cdot{f}_{{{2},{1}}}\cdot{f}_{{{3},{0}}}={\left(\delta_{{{0}{l}_{{1}}}}{x}-{a}_{{1}}\right)}{\left(\delta_{{{1}{l}_{{2}}}}{x}+{a}_{{2}}\right)}{\left(\delta_{{{0}{l}_{{3}}}}{x}-{a}_{{3}}\right)}={\left(\delta_{{{0},{1}}}{x}-{a}_{{1}}\right)}{\left(\delta_{{{1},{0}}}{x}+{a}_{{2}}\right)}{\left(\delta_{{{0},{1}}}{x}-{a}_{{3}}\right)}={\left({0}{x}-{a}_{{1}}\right)}{\left({0}{x}+{a}_{{2}}\right)}{\left({0}{x}-{a}_{{3}}\right)}={\left(-{a}_{{1}}\right)}{\left({a}_{{2}}\right)}{\left(-{a}_{{3}}\right)}={a}_{{1}}{a}_{{2}}{a}_{{3}}p3(x)=j=13fj,ij=f1,i1f2,i2f3,i3=f1,1f2,1f3,0=(δ1l1x+a1)(δ1l2x+a2)(δ0l3xa3)=(δ1,1x+a1)(δ1,0x+a2)(δ0,1xa3)=(1x+a1)(0x+a2)(0xa3)=(x+a1)(a2)(a3)=a2a3xa1a2a3{p}_{{3}}{\left({x}\right)}={\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}={f}_{{{1},{i}_{{1}}}}\cdot{f}_{{{2},{i}_{{2}}}}\cdot{f}_{{{3},{i}_{{3}}}}={f}_{{{1},{1}}}\cdot{f}_{{{2},{1}}}\cdot{f}_{{{3},{0}}}={\left(\delta_{{{1}{l}_{{1}}}}{x}+{a}_{{1}}\right)}{\left(\delta_{{{1}{l}_{{2}}}}{x}+{a}_{{2}}\right)}{\left(\delta_{{{0}{l}_{{3}}}}{x}-{a}_{{3}}\right)}={\left(\delta_{{{1},{1}}}{x}+{a}_{{1}}\right)}{\left(\delta_{{{1},{0}}}{x}+{a}_{{2}}\right)}{\left(\delta_{{{0},{1}}}{x}-{a}_{{3}}\right)}={\left({1}{x}+{a}_{{1}}\right)}{\left({0}{x}+{a}_{{2}}\right)}{\left({0}{x}-{a}_{{3}}\right)}={\left({x}+{a}_{{1}}\right)}{\left({a}_{{2}}\right)}{\left(-{a}_{{3}}\right)}=-{a}_{{2}}{a}_{{3}}{x}-{a}_{{1}}{a}_{{2}}{a}_{{3}}p4(x)=j=13fj,ij=f1,i1f2,i2f3,i3=f1,0f2,0f3,1=(δ0l1xa1)(δ0l2xa2)(δ1l3x+a3)=(δ0,1xa1)(δ0,0xa2)(δ1,1x+a3)=(0xa1)(1xa2)(1x+a3)=(a1)(xa2)(x+a3)=a1x2+a1a2xa1a3x+a1a2a3{p}_{{4}}{\left({x}\right)}={\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}={f}_{{{1},{i}_{{1}}}}\cdot{f}_{{{2},{i}_{{2}}}}\cdot{f}_{{{3},{i}_{{3}}}}={f}_{{{1},{0}}}\cdot{f}_{{{2},{0}}}\cdot{f}_{{{3},{1}}}={\left(\delta_{{{0}{l}_{{1}}}}{x}-{a}_{{1}}\right)}{\left(\delta_{{{0}{l}_{{2}}}}{x}-{a}_{{2}}\right)}{\left(\delta_{{{1}{l}_{{3}}}}{x}+{a}_{{3}}\right)}={\left(\delta_{{{0},{1}}}{x}-{a}_{{1}}\right)}{\left(\delta_{{{0},{0}}}{x}-{a}_{{2}}\right)}{\left(\delta_{{{1},{1}}}{x}+{a}_{{3}}\right)}={\left({0}{x}-{a}_{{1}}\right)}{\left({1}{x}-{a}_{{2}}\right)}{\left({1}{x}+{a}_{{3}}\right)}={\left(-{a}_{{1}}\right)}{\left({x}-{a}_{{2}}\right)}{\left({x}+{a}_{{3}}\right)}=-{a}_{{1}}{x}^{{2}}+{a}_{{1}}{a}_{{2}}{x}-{a}_{{1}}{a}_{{3}}{x}+{a}_{{1}}{a}_{{2}}{a}_{{3}}p5(x)=j=13fj,ij=f1,i1f2,i2f3,i3=f1,1f2,0f3,1=(δ1l1x+a1)(δ0l2xa2)(δ1l3x+a3)=(δ1,1x+a1)(δ0,0xa2)(δ1,1x+a3)=(1x+a1)(1xa2)(1x+a3)=(x+a1)(xa2)(x+a3)=x3+a1x2a2x2+a3x2a1a2x+a1a3xa2a3xa1a2a3{\color{blue}{{p}_{{5}}{\left({x}\right)}={\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}={f}_{{{1},{i}_{{1}}}}\cdot{f}_{{{2},{i}_{{2}}}}\cdot{f}_{{{3},{i}_{{3}}}}={f}_{{{1},{1}}}\cdot{f}_{{{2},{0}}}\cdot{f}_{{{3},{1}}}={\left(\delta_{{{1}{l}_{{1}}}}{x}+{a}_{{1}}\right)}{\left(\delta_{{{0}{l}_{{2}}}}{x}-{a}_{{2}}\right)}{\left(\delta_{{{1}{l}_{{3}}}}{x}+{a}_{{3}}\right)}={\left(\delta_{{{1},{1}}}{x}+{a}_{{1}}\right)}{\left(\delta_{{{0},{0}}}{x}-{a}_{{2}}\right)}{\left(\delta_{{{1},{1}}}{x}+{a}_{{3}}\right)}={\left({1}{x}+{a}_{{1}}\right)}{\left({1}{x}-{a}_{{2}}\right)}{\left({1}{x}+{a}_{{3}}\right)}={\left({x}+{a}_{{1}}\right)}{\left({x}-{a}_{{2}}\right)}{\left({x}+{a}_{{3}}\right)}={x}^{{3}}+{a}_{{1}}{x}^{{2}}-{a}_{{2}}{x}^{{2}}+{a}_{{3}}{x}^{{2}}-{a}_{{1}}{a}_{{2}}{x}+{a}_{{1}}{a}_{{3}}{x}-{a}_{{2}}{a}_{{3}}{x}-{a}_{{1}}{a}_{{2}}{a}_{{3}}}}p6(x)=j=13fj,ij=f1,i1f2,i2f3,i3=f1,0f2,1f3,1=(δ0l1xa1)(δ1l2x+a2)(δ1l3x+a3)=(δ0,1xa1)(δ1,0x+a2)(δ1,1x+a3)=(0xa1)(0x+a2)(1x+a3)=(a1)(a2)(x+a3)=a1a2xa1a3a2{p}_{{6}}{\left({x}\right)}={\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}={f}_{{{1},{i}_{{1}}}}\cdot{f}_{{{2},{i}_{{2}}}}\cdot{f}_{{{3},{i}_{{3}}}}={f}_{{{1},{0}}}\cdot{f}_{{{2},{1}}}\cdot{f}_{{{3},{1}}}={\left(\delta_{{{0}{l}_{{1}}}}{x}-{a}_{{1}}\right)}{\left(\delta_{{{1}{l}_{{2}}}}{x}+{a}_{{2}}\right)}{\left(\delta_{{{1}{l}_{{3}}}}{x}+{a}_{{3}}\right)}={\left(\delta_{{{0},{1}}}{x}-{a}_{{1}}\right)}{\left(\delta_{{{1},{0}}}{x}+{a}_{{2}}\right)}{\left(\delta_{{{1},{1}}}{x}+{a}_{{3}}\right)}={\left({0}{x}-{a}_{{1}}\right)}{\left({0}{x}+{a}_{{2}}\right)}{\left({1}{x}+{a}_{{3}}\right)}={\left(-{a}_{{1}}\right)}{\left({a}_{{2}}\right)}{\left({x}+{a}_{{3}}\right)}=-{a}_{{1}}{a}_{{2}}{x}-{a}_{{1}}{a}_{{3}}{a}_{{2}}p7(x)=j=13fj,ij=f1,i1f2,i2f3,i3=f1,1f2,1f3,1=(δ1l1x+a1)(δ1l2x+a2)(δ1l3x+a3)=(δ1,1x+a1)(δ1,0x+a2)(δ1,1x+a3)=(1x+a1)(0x+a2)(1x+a3)=(x+a1)(a2)(x+a3)=a2x2+a1a2x+a2a3x+a1a2a3{p}_{{7}}{\left({x}\right)}={\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}={f}_{{{1},{i}_{{1}}}}\cdot{f}_{{{2},{i}_{{2}}}}\cdot{f}_{{{3},{i}_{{3}}}}={f}_{{{1},{1}}}\cdot{f}_{{{2},{1}}}\cdot{f}_{{{3},{1}}}={\left(\delta_{{{1}{l}_{{1}}}}{x}+{a}_{{1}}\right)}{\left(\delta_{{{1}{l}_{{2}}}}{x}+{a}_{{2}}\right)}{\left(\delta_{{{1}{l}_{{3}}}}{x}+{a}_{{3}}\right)}={\left(\delta_{{{1},{1}}}{x}+{a}_{{1}}\right)}{\left(\delta_{{{1},{0}}}{x}+{a}_{{2}}\right)}{\left(\delta_{{{1},{1}}}{x}+{a}_{{3}}\right)}={\left({1}{x}+{a}_{{1}}\right)}{\left({0}{x}+{a}_{{2}}\right)}{\left({1}{x}+{a}_{{3}}\right)}={\left({x}+{a}_{{1}}\right)}{\left({a}_{{2}}\right)}{\left({x}+{a}_{{3}}\right)}={a}_{{2}}{x}^{{2}}+{a}_{{1}}{a}_{{2}}{x}+{a}_{{2}}{a}_{{3}}{x}+{a}_{{1}}{a}_{{2}}{a}_{{3}}

From these we can create a table of coefficients:

i{i}δil\delta_{{{i}{l}}} (x3{x}^{{3}})pi,2{p}_{{{i},{2}}} (x2{x}^{{2}})pi,1{p}_{{{i},{1}}} (x1{x}^{{1}})pi,0{p}_{{{i},{0}}} (x0{x}^{{0}})
000a2a3{a}_{{2}}\cdot{a}_{{3}}a1a2a3-{a}_{{1}}\cdot{a}_{{2}}\cdot{a}_{{3}}
10a3-{a}_{{3}}a1a3+a2a3-{a}_{{1}}\cdot{a}_{{3}}+{a}_{{2}}\cdot{a}_{{3}}a1a2a3{a}_{{1}}\cdot{a}_{{2}}\cdot{a}_{{3}}
2000a1a2a3{a}_{{1}}\cdot{a}_{{2}}\cdot{a}_{{3}}
300a2a3-{a}_{{2}}\cdot{a}_{{3}}a1a2a3-{a}_{{1}}\cdot{a}_{{2}}\cdot{a}_{{3}}
40a1-{a}_{{1}}a1a2a1a3{a}_{{1}}\cdot{a}_{{2}}-{a}_{{1}}\cdot{a}_{{3}}a1a2a3{a}_{{1}}\cdot{a}_{{2}}\cdot{a}_{{3}}
51{\color{blue}{{1}}}a1a2+a3{\color{blue}{{a}_{{1}}-{a}_{{2}}+{a}_{{3}}}}a1a2+a1a3a2a3{\color{blue}{-{a}_{{1}}\cdot{a}_{{2}}+{a}_{{1}}\cdot{a}_{{3}}-{a}_{{2}}\cdot{a}_{{3}}}}a1a2a3{\color{blue}{-{a}_{{1}}\cdot{a}_{{2}}\cdot{a}_{{3}}}}
600a1a2-{a}_{{1}}\cdot{a}_{{2}}a1a2a3-{a}_{{1}}\cdot{a}_{{2}}\cdot{a}_{{3}}
70a2{a}_{{2}}a1a2+a2a3{a}_{{1}}\cdot{a}_{{2}}+{a}_{{2}}\cdot{a}_{{3}}a1a2a3{a}_{{1}}\cdot{a}_{{2}}\cdot{a}_{{3}}

The actual value for each coefficient can be calculated by information already known by the Prover (aj{a}_{{j}}) before a challenge value x{x} was received. Note that these aren't individually transferred as part of the proof transcript, they're only locally computed by the Prover.

Determining the coefficients for all commitments of a large anonymity set may seem extremely expensive, but there are ways to do these calculations in a quite efficient manner.

Dk=i=0N1pi,kCi{D}_{{k}}={\sum_{{{i}={0}}}^{{{N}-{1}}}}{p}_{{{i},{k}}}{C}'_{{i}}

The idea is that the Prover would send commitments D0,,Dn1{D}_{{0}},\ldots,{D}_{{{n}-{1}}} that cancel out these low order coefficients while the high order coefficient δil\delta_{{{i}{l}}} for xn{x}^{{n}} will guarantee that only our commitment to zero remains:

i=0N1(j=1nfj,ij)Cik=0n1xkDk=xnCl{\sum_{{{i}={0}}}^{{{N}-{1}}}}{\left({\prod_{{{j}={1}}}^{{n}}}{f}_{{{j},{i}_{{j}}}}\right)}{C}'_{{i}}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{x}^{{k}}{D}_{{k}}={x}^{{n}}{C}'_{{l}}
Example

Continuing with the previous example, we now determine commitments Dk{D}_{{k}} where k=j1{k}={j}-{1}:

D0=i=07pi,0Ci=p0,0C0+p1,0C1+p2,0C2+p3,0C3+p4,0C4+p5,0C5+p6,0C6+p7,0C7=a1a2a3C0+a1a2a3C1+a1a2a3C2a1a2a3C3+a1a2a3C4a1a2a3C5a1a2a3+a1a2a3C7{D}_{{0}}={\sum_{{{i}={0}}}^{{{7}}}}{p}_{{{i},{0}}}{C}'_{{i}}={p}_{{{0},{0}}}{C}'_{{0}}+{p}_{{{1},{0}}}{C}'_{{1}}+{p}_{{{2},{0}}}{C}'_{{2}}+{p}_{{{3},{0}}}{C}'_{{3}}+{p}_{{{4},{0}}}{C}'_{{4}}+{\color{blue}{{p}_{{{5},{0}}}{C}'_{{5}}}}+{p}_{{{6},{0}}}{C}'_{{6}}+{p}_{{{7},{0}}}{C}'_{{7}}=-{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{0}}+{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{1}}+{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{2}}-{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{3}}+{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{4}}{\color{blue}{-{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{5}}}}-{a}_{{1}}{a}_{{2}}{a}_{{3}}+{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{7}}D1=i=07pi,1Ci=p0,1C0+p1,1C1+p2,1C2+p3,1C3+p4,1C4+p5,1C5+p6,1C6+p7,1C7=a2a3C0+(a1a3+a2a3)C1+0C2a2a3C3+(a1a2a1a3)C4+(a1a2+a1a3a2a3)C5a1a2C6+(a1a2+a2a3)C7{D}_{{1}}={\sum_{{{i}={0}}}^{{{7}}}}{p}_{{{i},{1}}}{C}'_{{i}}={p}_{{{0},{1}}}{C}'_{{0}}+{p}_{{{1},{1}}}{C}'_{{1}}+{p}_{{{2},{1}}}{C}'_{{2}}+{p}_{{{3},{1}}}{C}'_{{3}}+{p}_{{{4},{1}}}{C}'_{{4}}+{\color{blue}{{p}_{{{5},{1}}}{C}'_{{5}}}}+{p}_{{{6},{1}}}{C}'_{{6}}+{p}_{{{7},{1}}}{C}'_{{7}}={a}_{{2}}{a}_{{3}}{C}'_{{0}}+{\left(-{a}_{{1}}{a}_{{3}}+{a}_{{2}}{a}_{{3}}\right)}{C}'_{{1}}+{0}{C}'_{{2}}-{a}_{{2}}{a}_{{3}}{C}'_{{3}}+{\left({a}_{{1}}{a}_{{2}}-{a}_{{1}}{a}_{{3}}\right)}{C}'_{{4}}+{\color{blue}{{\left(-{a}_{{1}}{a}_{{2}}+{a}_{{1}}{a}_{{3}}-{a}_{{2}}{a}_{{3}}\right)}{C}'_{{5}}}}-{a}_{{1}}{a}_{{2}}{C}'_{{6}}+{\left({a}_{{1}}{a}_{{2}}+{a}_{{2}}{a}_{{3}}\right)}{C}'_{{7}}D2=i=07pi,2Ci=p0,2C0+p1,2C1+p2,2C2+p3,2C3+p4,2C4+p5,2C5+p6,2C6+p7,2C7=0C0a3C1+0C2+0C3a1C4+(a1a2+a3)C5+0C6+a2C7{D}_{{2}}={\sum_{{{i}={0}}}^{{{7}}}}{p}_{{{i},{2}}}{C}'_{{i}}={p}_{{{0},{2}}}{C}'_{{0}}+{p}_{{{1},{2}}}{C}'_{{1}}+{p}_{{{2},{2}}}{C}'_{{2}}+{p}_{{{3},{2}}}{C}'_{{3}}+{p}_{{{4},{2}}}{C}'_{{4}}+{\color{blue}{{p}_{{{5},{2}}}{C}'_{{5}}}}+{p}_{{{6},{2}}}{C}'_{{6}}+{p}_{{{7},{2}}}{C}'_{{7}}={0}{C}'_{{0}}-{a}_{{3}}{C}'_{{1}}+{0}{C}'_{{2}}+{0}{C}'_{{3}}-{a}_{{1}}{C}'_{{4}}+{\color{blue}{{\left({a}_{{1}}-{a}_{{2}}+{a}_{{3}}\right)}{C}'_{{5}}}}+{0}{C}'_{{6}}+{a}_{{2}}{C}'_{{7}}

We'll use everything so far to show that the lower order coefficients indeed end up cancelling out all commitments but C5{C}'_{{5}} during the subtraction of

i=07(j=13fj,ij)Ci(1)k=02xkDk(2)\underbrace{{{\sum_{{{i}={0}}}^{{{7}}}}{\left({\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}\right)}{C}'_{{i}}}}_{{\text{(1)}}}\underbrace{{-{\sum_{{{k}={0}}}^{{{2}}}}{x}^{{k}}{D}_{{k}}}}_{{\text{(2)}}}

We begin by expanding (1)\text{(1)} based on the polynomials we already calculated in the previous example:

i=07(j=13fj,ij)Ci{\sum_{{{i}={0}}}^{{{7}}}}{\left({\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}\right)}{C}'_{{i}}(j=13fj,ij)C0+(j=13fj,ij)C1+(j=13fj,ij)C2+(j=13fj,ij)C3+(j=13fj,ij)C4+(j=13fj,ij)C5+(j=13fj,ij)C6+(j=13fj,ij)C7{\left({\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}\right)}{C}'_{{0}}+{\left({\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}\right)}{C}'_{{1}}+{\left({\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}\right)}{C}'_{{2}}+{\left({\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}\right)}{C}'_{{3}}+{\left({\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}\right)}{C}'_{{4}}+{\color{blue}{{\left({\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}\right)}{C}'_{{5}}}}+{\left({\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}\right)}{C}'_{{6}}+{\left({\prod_{{{j}={1}}}^{{3}}}{f}_{{{j},{i}_{{j}}}}\right)}{C}'_{{7}}(f1,i1f2,i2f3,i3)C0+(f1,i1f2,i2f3,i3)C1+(f1,i1f2,i2f3,i3)C2+(f1,i1f2,i2f3,i3)C3+(f1,i1f2,i2f3,i3)C4+(f1,i1f2,i2f3,i3)C5+(f1,i1f2,i2f3,i3)C6+(f1,i1f2,i2f3,i3)C7{\left({f}_{{{1},{i}_{{1}}}}{f}_{{{2},{i}_{{2}}}}{f}_{{{3},{i}_{{3}}}}\right)}{C}'_{{0}}+{\left({f}_{{{1},{i}_{{1}}}}{f}_{{{2},{i}_{{2}}}}{f}_{{{3},{i}_{{3}}}}\right)}{C}'_{{1}}+{\left({f}_{{{1},{i}_{{1}}}}{f}_{{{2},{i}_{{2}}}}{f}_{{{3},{i}_{{3}}}}\right)}{C}'_{{2}}+{\left({f}_{{{1},{i}_{{1}}}}{f}_{{{2},{i}_{{2}}}}{f}_{{{3},{i}_{{3}}}}\right)}{C}'_{{3}}+{\left({f}_{{{1},{i}_{{1}}}}{f}_{{{2},{i}_{{2}}}}{f}_{{{3},{i}_{{3}}}}\right)}{C}'_{{4}}+{\color{blue}{{\left({f}_{{{1},{i}_{{1}}}}{f}_{{{2},{i}_{{2}}}}{f}_{{{3},{i}_{{3}}}}\right)}{C}'_{{5}}}}+{\left({f}_{{{1},{i}_{{1}}}}{f}_{{{2},{i}_{{2}}}}{f}_{{{3},{i}_{{3}}}}\right)}{C}'_{{6}}+{\left({f}_{{{1},{i}_{{1}}}}{f}_{{{2},{i}_{{2}}}}{f}_{{{3},{i}_{{3}}}}\right)}{C}'_{{7}}(f1,0f2,0f3,0)C0+(f1,1f2,0f3,0)C1+(f1,0f2,1f3,0)C2+(f1,1f2,1f3,0)C3+(f1,0f2,0f3,1)C4+(f1,1f2,0f3,1)C5+(f1,0f2,1f3,1)C6+(f1,1f2,1f3,1)C7{\left({f}_{{{1},{0}}}{f}_{{{2},{0}}}{f}_{{{3},{0}}}\right)}{C}'_{{0}}+{\left({f}_{{{1},{1}}}{f}_{{{2},{0}}}{f}_{{{3},{0}}}\right)}{C}'_{{1}}+{\left({f}_{{{1},{0}}}{f}_{{{2},{1}}}{f}_{{{3},{0}}}\right)}{C}'_{{2}}+{\left({f}_{{{1},{1}}}{f}_{{{2},{1}}}{f}_{{{3},{0}}}\right)}{C}'_{{3}}+{\left({f}_{{{1},{0}}}{f}_{{{2},{0}}}{f}_{{{3},{1}}}\right)}{C}'_{{4}}+{\color{blue}{{\left({f}_{{{1},{1}}}{f}_{{{2},{0}}}{f}_{{{3},{1}}}\right)}{C}'_{{5}}}}+{\left({f}_{{{1},{0}}}{f}_{{{2},{1}}}{f}_{{{3},{1}}}\right)}{C}'_{{6}}+{\left({f}_{{{1},{1}}}{f}_{{{2},{1}}}{f}_{{{3},{1}}}\right)}{C}'_{{7}}(a2a3xa1a2a3)C0+(a3x2a1a3x+a2a3x+a1a2a3)C1+(a1a2a3)C2+(a2a3xa1a2a3)C3+(a1x2+a1a2xa1a3x+a1a2a3)C4+(x3+a1x2a2x2+a3x2a1a2x+a1a3xa2a3xa1a2a3)C5+(a1a2xa1a3a2)C6+(a2x2+a1a2x+a2a3x+a1a2a3)C7{\left({a}_{{2}}{a}_{{3}}{x}-{a}_{{1}}{a}_{{2}}{a}_{{3}}\right)}{C}'_{{0}}+{\left(-{a}_{{3}}{x}^{{2}}-{a}_{{1}}{a}_{{3}}{x}+{a}_{{2}}{a}_{{3}}{x}+{a}_{{1}}{a}_{{2}}{a}_{{3}}\right)}{C}'_{{1}}+{\left({a}_{{1}}{a}_{{2}}{a}_{{3}}\right)}{C}'_{{2}}+{\left(-{a}_{{2}}{a}_{{3}}{x}-{a}_{{1}}{a}_{{2}}{a}_{{3}}\right)}{C}'_{{3}}+{\left(-{a}_{{1}}{x}^{{2}}+{a}_{{1}}{a}_{{2}}{x}-{a}_{{1}}{a}_{{3}}{x}+{a}_{{1}}{a}_{{2}}{a}_{{3}}\right)}{C}'_{{4}}+{\color{blue}{{\left({x}^{{3}}+{a}_{{1}}{x}^{{2}}-{a}_{{2}}{x}^{{2}}+{a}_{{3}}{x}^{{2}}-{a}_{{1}}{a}_{{2}}{x}+{a}_{{1}}{a}_{{3}}{x}-{a}_{{2}}{a}_{{3}}{x}-{a}_{{1}}{a}_{{2}}{a}_{{3}}\right)}{C}'_{{5}}}}+{\left(-{a}_{{1}}{a}_{{2}}{x}-{a}_{{1}}{a}_{{3}}{a}_{{2}}\right)}{C}'_{{6}}+{\left({a}_{{2}}{x}^{{2}}+{a}_{{1}}{a}_{{2}}{x}+{a}_{{2}}{a}_{{3}}{x}+{a}_{{1}}{a}_{{2}}{a}_{{3}}\right)}{C}'_{{7}}

Next we continue by expanding (2)\text{(2)} making use of the commitments Dk{D}_{{k}} determined above:

k=02xkDk-{\sum_{{{k}={0}}}^{{{2}}}}{x}^{{k}}{D}_{{k}}x0D0x1D1x2D2-{x}^{{0}}{D}_{{0}}-{x}^{{1}}{D}_{{1}}-{x}^{{2}}{D}_{{2}}x0(a1a2a3C0+a1a2a3C1+a1a2a3C2a1a2a3C3+a1a2a3C4a1a2a3C5a1a2a3C6+a1a2a3C7)x1(a2a3C0+(a1a3+a2a3)C1+0C2a2a3C3+(a1a2a1a3)C4+(a1a2+a1a3a2a3)C5a1a2C6+(a1a2+a2a3)C7)x2(0C0a3C1+0C2+0C3a1C4+(a1a2+a3)C5+0C6+a2C7)-{x}^{{0}}{\left(-{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{0}}+{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{1}}+{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{2}}-{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{3}}+{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{4}}-{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{5}}-{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{6}}+{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{7}}\right)}-{x}^{{1}}{\left({a}_{{2}}{a}_{{3}}{C}'_{{0}}+{\left(-{a}_{{1}}{a}_{{3}}+{a}_{{2}}{a}_{{3}}\right)}{C}'_{{1}}+{0}{C}'_{{2}}-{a}_{{2}}{a}_{{3}}{C}'_{{3}}+{\left({a}_{{1}}{a}_{{2}}-{a}_{{1}}{a}_{{3}}\right)}{C}'_{{4}}+{\color{blue}{{\left(-{a}_{{1}}{a}_{{2}}+{a}_{{1}}{a}_{{3}}-{a}_{{2}}{a}_{{3}}\right)}{C}'_{{5}}}}-{a}_{{1}}{a}_{{2}}{C}'_{{6}}+{\left({a}_{{1}}{a}_{{2}}+{a}_{{2}}{a}_{{3}}\right)}{C}'_{{7}}\right)}-{x}^{{2}}{\left({0}{C}'_{{0}}-{a}_{{3}}{C}'_{{1}}+{0}{C}'_{{2}}+{0}{C}'_{{3}}-{a}_{{1}}{C}'_{{4}}+{\left({a}_{{1}}-{a}_{{2}}+{a}_{{3}}\right)}{C}'_{{5}}+{0}{C}'_{{6}}+{a}_{{2}}{C}'_{{7}}\right)}(a1a2a3C0a1a2a3C1a1a2a3C2+a1a2a3C3a1a2a3C4+a1a2a3C5+a1a2a3C6a1a2a3C7)+(a2a3xC0(a1a3+a2a3)xC1+a2a3xC3(a1a2a1a3)xC4(a1a2+a1a3a2a3)xC5+a1a2xC6(a1a2+a2a3)xC7)+(a3x2C1+a1x2C4(a1a2+a3)x2C5a2x2C7){\left({a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{0}}-{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{1}}-{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{2}}+{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{3}}-{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{4}}+{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{5}}+{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{6}}-{a}_{{1}}{a}_{{2}}{a}_{{3}}{C}'_{{7}}\right)}+{\left(-{a}_{{2}}{a}_{{3}}{x}{C}'_{{0}}-{\left(-{a}_{{1}}{a}_{{3}}+{a}_{{2}}{a}_{{3}}\right)}{x}{C}'_{{1}}+{a}_{{2}}{a}_{{3}}{x}{C}'_{{3}}-{\left({a}_{{1}}{a}_{{2}}-{a}_{{1}}{a}_{{3}}\right)}{x}{C}'_{{4}}{\color{blue}{-{\left(-{a}_{{1}}{a}_{{2}}+{a}_{{1}}{a}_{{3}}-{a}_{{2}}{a}_{{3}}\right)}{x}{C}'_{{5}}}}+{a}_{{1}}{a}_{{2}}{x}{C}'_{{6}}-{\left({a}_{{1}}{a}_{{2}}+{a}_{{2}}{a}_{{3}}\right)}{x}{C}'_{{7}}\right)}+{\left({a}_{{3}}{x}^{{2}}{C}'_{{1}}+{a}_{{1}}{x}^{{2}}{C}'_{{4}}-{\left({a}_{{1}}-{a}_{{2}}+{a}_{{3}}\right)}{x}^{{2}}{C}'_{{5}}-{a}_{{2}}{x}^{{2}}{C}'_{{7}}\right)}(a2a3x+a1a2a3)C0+(a3x2+a1a3xa2a3xa1a2a3)C1+(a1a2a3)C2+(a2a3x+a1a2a3)C3+(a1x2a1a2x+a1a3xa1a2a3)C4+(a1x2+a2x2a3x2+a1a2xa1a3x+a2a3x+a1a2a3)C5+(a1a2x+a1a2a3)C6+(a2x2a1a2xa2a3xa1a2a3)C7{\left(-{a}_{{2}}{a}_{{3}}{x}+{a}_{{1}}{a}_{{2}}{a}_{{3}}\right)}{C}'_{{0}}+{\left({a}_{{3}}{x}^{{2}}+{a}_{{1}}{a}_{{3}}{x}-{a}_{{2}}{a}_{{3}}{x}-{a}_{{1}}{a}_{{2}}{a}_{{3}}\right)}{C}'_{{1}}+{\left(-{a}_{{1}}{a}_{{2}}{a}_{{3}}\right)}{C}'_{{2}}+{\left({a}_{{2}}{a}_{{3}}{x}+{a}_{{1}}{a}_{{2}}{a}_{{3}}\right)}{C}'_{{3}}+{\left({a}_{{1}}{x}^{{2}}-{a}_{{1}}{a}_{{2}}{x}+{a}_{{1}}{a}_{{3}}{x}-{a}_{{1}}{a}_{{2}}{a}_{{3}}\right)}{C}'_{{4}}+{\color{blue}{{\left(-{a}_{{1}}{x}^{{2}}+{a}_{{2}}{x}^{{2}}-{a}_{{3}}{x}^{{2}}+{a}_{{1}}{a}_{{2}}{x}-{a}_{{1}}{a}_{{3}}{x}+{a}_{{2}}{a}_{{3}}{x}+{a}_{{1}}{a}_{{2}}{a}_{{3}}\right)}{C}'_{{5}}}}+{\left({a}_{{1}}{a}_{{2}}{x}+{a}_{{1}}{a}_{{2}}{a}_{{3}}\right)}{C}'_{{6}}+{\left(-{a}_{{2}}{x}^{{2}}-{a}_{{1}}{a}_{{2}}{x}-{a}_{{2}}{a}_{{3}}{x}-{a}_{{1}}{a}_{{2}}{a}_{{3}}\right)}{C}'_{{7}}

Finally, we can see that both parts cancel each other out when adding them, except for x3C5{x}^{{3}}{C}_{{5}}:

((a2a3xa1a2a3)C0+(a3x2a1a3x+a2a3x+a1a2a3)C1+(a1a2a3)C2+(a2a3xa1a2a3)C3+(a1x2+a1a2xa1a3x+a1a2a3)C4+(x3+a1x2a2x2+a3x2a1a2x+a1a3xa2a3xa1a2a3)C5+(a1a2xa1a3a2)C6+(a2x2+a1a2x+a2a3x+a1a2a3)C7)+((a2a3x+a1a2a3)C0+(a3x2+a1a3xa2a3xa1a2a3)C1+(a1a2a3)C2+(a2a3x+a1a2a3)C3+(a1x2a1a2x+a1a3xa1a2a3)C4+(a1x2+a2x2a3x2+a1a2xa1a3x+a2a3x+a1a2a3)C5+(a1a2x+a1a2a3)C6+(a2x2a1a2xa2a3xa1a2a3)C7){\left({\left(\cancel{{{a}_{{2}}{a}_{{3}}{x}}}\cancel{{-{a}_{{1}}{a}_{{2}}{a}_{{3}}}}\right)}{C}'_{{0}}+{\left(\cancel{{-{a}_{{3}}{x}^{{2}}}}\cancel{{-{a}_{{1}}{a}_{{3}}{x}}}+\cancel{{{a}_{{2}}{a}_{{3}}{x}}}+\cancel{{{a}_{{1}}{a}_{{2}}{a}_{{3}}}}\right)}{C}'_{{1}}+{\left(\cancel{{{a}_{{1}}{a}_{{2}}{a}_{{3}}}}\right)}{C}'_{{2}}+{\left(\cancel{{-{a}_{{2}}{a}_{{3}}{x}}}\cancel{{-{a}_{{1}}{a}_{{2}}{a}_{{3}}}}\right)}{C}'_{{3}}+{\left(\cancel{{-{a}_{{1}}{x}^{{2}}}}+\cancel{{{a}_{{1}}{a}_{{2}}{x}}}\cancel{{-{a}_{{1}}{a}_{{3}}{x}}}+\cancel{{{a}_{{1}}{a}_{{2}}{a}_{{3}}}}\right)}{C}'_{{4}}+{\color{blue}{{\left({x}^{{3}}+\cancel{{{a}_{{1}}{x}^{{2}}}}\cancel{{-{a}_{{2}}{x}^{{2}}}}+\cancel{{{a}_{{3}}{x}^{{2}}}}\cancel{{-{a}_{{1}}{a}_{{2}}{x}}}+\cancel{{{a}_{{1}}{a}_{{3}}{x}}}\cancel{{-{a}_{{2}}{a}_{{3}}{x}}}\cancel{{-{a}_{{1}}{a}_{{2}}{a}_{{3}}}}\right)}{C}'_{{5}}}}+{\left(\cancel{{-{a}_{{1}}{a}_{{2}}{x}}}\cancel{{-{a}_{{1}}{a}_{{3}}{a}_{{2}}}}\right)}{C}'_{{6}}+{\left(\cancel{{{a}_{{2}}{x}^{{2}}}}+\cancel{{{a}_{{1}}{a}_{{2}}{x}}}+\cancel{{{a}_{{2}}{a}_{{3}}{x}}}+\cancel{{{a}_{{1}}{a}_{{2}}{a}_{{3}}}}\right)}{C}'_{{7}}\right)}+{\left({\left(\cancel{{-{a}_{{2}}{a}_{{3}}{x}}}+\cancel{{{a}_{{1}}{a}_{{2}}{a}_{{3}}}}\right)}{C}'_{{0}}+{\left(\cancel{{{a}_{{3}}{x}^{{2}}}}+\cancel{{{a}_{{1}}{a}_{{3}}{x}}}\cancel{{-{a}_{{2}}{a}_{{3}}{x}}}\cancel{{-{a}_{{1}}{a}_{{2}}{a}_{{3}}}}\right)}{C}'_{{1}}+{\left(\cancel{{-{a}_{{1}}{a}_{{2}}{a}_{{3}}}}\right)}{C}'_{{2}}+{\left(\cancel{{{a}_{{2}}{a}_{{3}}{x}}}+\cancel{{{a}_{{1}}{a}_{{2}}{a}_{{3}}}}\right)}{C}'_{{3}}+{\left(\cancel{{{a}_{{1}}{x}^{{2}}}}\cancel{{-{a}_{{1}}{a}_{{2}}{x}}}+\cancel{{{a}_{{1}}{a}_{{3}}{x}}}\cancel{{-{a}_{{1}}{a}_{{2}}{a}_{{3}}}}\right)}{C}'_{{4}}+{\color{blue}{{\left(\cancel{{-{a}_{{1}}{x}^{{2}}}}+\cancel{{{a}_{{2}}{x}^{{2}}}}\cancel{{-{a}_{{3}}{x}^{{2}}}}+\cancel{{{a}_{{1}}{a}_{{2}}{x}}}\cancel{{-{a}_{{1}}{a}_{{3}}{x}}}+\cancel{{{a}_{{2}}{a}_{{3}}{x}}}+\cancel{{{a}_{{1}}{a}_{{2}}{a}_{{3}}}}\right)}{C}'_{{5}}}}+{\left(\cancel{{{a}_{{1}}{a}_{{2}}{x}}}+\cancel{{{a}_{{1}}{a}_{{2}}{a}_{{3}}}}\right)}{C}'_{{6}}+{\left(\cancel{{-{a}_{{2}}{x}^{{2}}}}\cancel{{-{a}_{{1}}{a}_{{2}}{x}}}\cancel{{-{a}_{{2}}{a}_{{3}}{x}}}\cancel{{-{a}_{{1}}{a}_{{2}}{a}_{{3}}}}\right)}{C}'_{{7}}\right)}0C0+0C1+0C2+0C3+0C4+x3C5+0C6+0C7{0}{C}'_{{0}}+{0}{C}'_{{1}}+{0}{C}'_{{2}}+{0}{C}'_{{3}}+{0}{C}'_{{4}}+{\color{blue}{{x}^{{3}}{C}'_{{5}}}}+{0}{C}'_{{6}}+{0}{C}'_{{7}}

As in the conceptual explanation, Dk{D}_{{k}} is extended to distort the blinding factor using random values ρk\rho_{{k}}:

Dk=i=0N1pi,kCi+ρkH{D}_{{k}}={\sum_{{{i}={0}}}^{{{N}-{1}}}}{p}_{{{i},{k}}}{C}'_{{i}}+\rho_{{k}}{H}

With this intuition in mind, you should now be able to make sense of the proof's actual construction.

One Out Of Many Proof Construction

A OOOMP demonstrates knowledge of an index l{l} and a blinding factor r{r} for a commitment Cl{C}_{{l}} to zero contained within a known set of commitments Ci{C}_{{i}} without revealing neither the commitment nor r{r}. In other words, we prove in Zero-Knowledge that we are able to open one of the commitments from a public list.

ZKPoK{(l,r):Cl=0G+rHCl{C0,C1,,CN1}}{\mathbf{\text{ZKPoK}}}{\left\lbrace{\left({\color{red}{{l}}},{\color{red}{{r}}}\right)}:{C}'_{{{\color{red}{{l}}}}}={0}{G}+{\color{red}{{r}}}{H}\wedge{C}'_{{{\color{red}{{l}}}}}\in{\left\lbrace{C}_{{0}},{C}_{{1}},\ldots,{C}_{{{N}-{1}}}\right\rbrace}\right\rbrace}

ProverVerifier
Knows (G,H,(C0,,CN1),l,r){\left({G},{H},{\left({C}'_{{0}},\ldots,{C}'_{{{N}-{1}}}\right)},{\color{red}{{l},{r}}}\right)}Knows (G,H,(C0,,CN1)){\left({G},{H},{\left({C}'_{{0}},\ldots,{C}'_{{{N}-{1}}}\right)}\right)}
Chooses random scalars rj,aj,sj,tj,ρk{\color{red}{{r}_{{j}},{a}_{{j}},{s}_{{j}},{t}_{{j}},\rho_{{k}}}}
C^j=ljG+rjH\hat{{C}}_{{j}}={\color{red}{{l}_{{j}}}}{G}+{\color{red}{{r}_{{j}}}}{H}
Aj=ajG+sjH{A}_{{j}}={\color{red}{{a}_{{j}}}}{G}+{\color{red}{{s}_{{j}}}}{H}
Bj=(ljaj)G+tjH{B}_{{j}}={\color{red}{{\left({l}_{{j}}{a}_{{j}}\right)}}}{G}+{\color{red}{{t}_{{j}}}}{H}
Dk=i=0N1pi,kCi+ρkH{D}_{{k}}={\sum_{{{i}={0}}}^{{{N}-{1}}}}{p}_{{{i},{k}}}{C}'_{{i}}+{\color{red}{\rho_{{k}}}}{H}
Sends (Aj,Bj,C^j,Dk){\left({A}_{{j}},{B}_{{j}},\hat{{C}}_{{j}},{D}_{{k}}\right)}\RightarrowKnows (,Aj,Bj,C^j,Dk){\left(\ldots,{A}_{{j}},{B}_{{j}},\hat{{C}}_{{j}},{D}_{{k}}\right)}
Chooses a random challenge scalar x{x}
Knows (G,H,C^j,mj,rj,aj,sj,tj,Aj,Bj,x){\left({G},{H},\hat{{C}}_{{j}},{\color{red}{{m}_{{j}},{r}_{{j}},{a}_{{j}},{s}_{{j}},{t}_{{j}}}},{A}_{{j}},{B}_{{j}},{x}\right)}\Leftarrow Sends x{x}
fj=ljx+aj{f}_{{j}}={\color{red}{{l}_{{j}}}}{x}+{\color{red}{{a}_{{j}}}}
αj=rjx+sj\alpha_{{j}}={\color{red}{{r}_{{j}}}}{x}+{\color{red}{{s}_{{j}}}}
βj=rj(xfj)+tj\beta_{{j}}={\color{red}{{r}_{{j}}}}{\left({x}-{f}_{{j}}\right)}+{\color{red}{{t}_{{j}}}}
γ=rxnk=0n1ρkxk\gamma={\color{red}{{r}}}{x}^{{n}}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\color{red}{\rho_{{k}}}}{x}^{{k}}
Sends (fj,αj,βj,γ){\left({f}_{{j}},\alpha_{{j}},\beta_{{j}},\gamma\right)}\RightarrowKnows (,x,fj,αj,βj,γ){\left(\ldots,{x},{f}_{{j}},\alpha_{{j}},\beta_{{j}},\gamma\right)}
xC^j+Aj   =?   fjG+αjH{x}\cdot\hat{{C}}_{{j}}+{A}_{{j}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {f}_{{j}}{G}+\alpha_{{j}}{H}
(xfj)C^j+Bj   =?   0G+βjH{\left({x}-{f}_{{j}}\right)}\hat{{C}}_{{j}}+{B}_{{j}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {0}{G}+\beta_{{j}}{H}
i=0N1(j=1nfj,ij)Ci+k=0n1xkDk   =?   γH{\sum_{{{i}={0}}}^{{{N}-{1}}}}{\left({\prod_{{{j}={1}}}^{{n}}}{f}_{{{j},{i}_{{j}}}}\right)}{C}'_{{i}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}-{x}^{{k}}{D}_{{k}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ \gamma{H}

It can be seen that, unless the challenge x{x} leaked before Dk{D}_{{k}} was committed, the Prover is required to have knowledge of r{r} in order to construct a valid γ\gamma. And even before that, the Prover must have had knowledge of the coin's serial number s{s} so that a valid commitment Cl=rH{C}'_{{l}}={r}{H} exists that can be opened with r{r} alone.

i=0N1(j=1nfj,ij)Ci+k=0n1xkDk   =?   γH{\sum_{{{i}={0}}}^{{{N}-{1}}}}{\left({\prod_{{{j}={1}}}^{{n}}}{f}_{{{j},{i}_{{j}}}}\right)}{C}'_{{i}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}-{x}^{{k}}{D}_{{k}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ \gamma{H}

Substituting Dk{D}_{{k}} and γ\gamma:

i=0N1(j=1nfj,ij)Ci+k=0n1xk(i=0N1pi,kCi+ρkH)   =?   (rxnk=0n1ρkxk)H{\sum_{{{i}={0}}}^{{{N}-{1}}}}{\left({\prod_{{{j}={1}}}^{{n}}}{f}_{{{j},{i}_{{j}}}}\right)}{C}'_{{i}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}-{x}^{{k}}{\left({\sum_{{{i}={0}}}^{{{N}-{1}}}}{p}_{{{i},{k}}}{C}'_{{i}}+{\color{red}{\rho_{{k}}}}{H}\right)}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({\color{red}{{r}}}{x}^{{n}}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\color{red}{\rho_{{k}}}}{x}^{{k}}\right)}{H}

i=0N1(j=1nfj,ij)Ci(1)+k=0n1xk(i=0N1pi,kCi)(2)+k=0n1xk(ρkH)   =?   rxnHk=0n1ρkxkH\underbrace{{{\sum_{{{i}={0}}}^{{{N}-{1}}}}{\left({\prod_{{{j}={1}}}^{{n}}}{f}_{{{j},{i}_{{j}}}}\right)}{C}'_{{i}}}}_{{\text{(1)}}}+\underbrace{{{\sum_{{{k}={0}}}^{{{n}-{1}}}}-{x}^{{k}}{\left({\sum_{{{i}={0}}}^{{{N}-{1}}}}{p}_{{{i},{k}}}{C}'_{{i}}\right)}}}_{{\text{(2)}}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}-{x}^{{k}}{\left({\color{red}{\rho_{{k}}}}{H}\right)}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\color{red}{{r}}}{x}^{{n}}{H}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\color{red}{\rho_{{k}}}}{x}^{{k}}{H}

We've seen in the OOOMP Intuition section that (1)\text{(1)} and (2)\text{(2)} cancel each other out, only leaving xnCl{x}^{{n}}{C}'_{{l}}

i=0N1(j=1nfj,ij)Ci+k=0n1xk(i=0N1pi,kCi)+k=0n1xk(ρkH)   =?   rxnHk=0n1ρkxkH\cancel{{{\sum_{{{i}={0}}}^{{{N}-{1}}}}{\left({\prod_{{{j}={1}}}^{{n}}}{f}_{{{j},{i}_{{j}}}}\right)}{C}'_{{i}}}}+\cancel{{{\sum_{{{k}={0}}}^{{{n}-{1}}}}-{x}^{{k}}{\left({\sum_{{{i}={0}}}^{{{N}-{1}}}}{p}_{{{i},{k}}}{C}'_{{i}}\right)}}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}-{x}^{{k}}{\left({\color{red}{\rho_{{k}}}}{H}\right)}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\color{red}{{r}}}{x}^{{n}}{H}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\color{red}{\rho_{{k}}}}{x}^{{k}}{H}

xnCl+k=0n1xk(ρkH)   =?   rxnHk=0n1ρkxkH{x}^{{n}}{C}'_{{l}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}-{x}^{{k}}{\left({\color{red}{\rho_{{k}}}}{H}\right)}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\color{red}{{r}}}{x}^{{n}}{H}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\color{red}{\rho_{{k}}}}{x}^{{k}}{H}

Substitute Cl=0G+rH=rH{C}'_{{l}}={0}{G}+{\color{red}{{r}}}{H}={\color{red}{{r}}}{H}

xn(rH)+k=0n1xk(ρkH)   =?   rxnHk=0n1ρkxkH{x}^{{n}}{\left({\color{red}{{r}}}{H}\right)}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}-{x}^{{k}}{\left({\color{red}{\rho_{{k}}}}{H}\right)}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\color{red}{{r}}}{x}^{{n}}{H}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\color{red}{\rho_{{k}}}}{x}^{{k}}{H}

rxnHk=0n1ρkxkH   =   rxnHk=0n1ρkxkH{\color{red}{{r}}}{x}^{{n}}{H}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\color{red}{\rho_{{k}}}}{x}^{{k}}{H}\ \text{ }\ {\overset{{✓}}{{=}}}\ \text{ }\ {\color{red}{{r}}}{x}^{{n}}{H}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\color{red}{\rho_{{k}}}}{x}^{{k}}{H}

Assuming the Serial Number s{s} has already been communicated to determine Ci{C}'_{{i}}, the information required to be included in the proof transcript are 4log2(N){4}{{\log}_{{2}}{\left({N}\right)}} commitments and 3log2N+1{3}{{\log}_{{2}}{N}}+{1} scalars in the response. If we'd have ~100000{100000} commitments in the current anonymity set, this means that we would have to add padding until we reach an N=2n{N}={2}^{{n}}. In this case, N=217=131072{N}={2}^{{17}}={131072} would suffice to hold all commitments while allowing a proper binary representation. This would require the proof to include 68{68} commitments and 52{52} scalar values.

While this may seem like a lot, this still offers a big improvement in comparison to the original Zerocoin protocol where an expensive double-discrete logarithm proof was necessary and very large multiplicative groups had to be chosen to ensure security, causing each individual commitment or field element in the transcript to be much bigger in comparison.

M-Ary Optimization

The authors of "Short Accountable Ring Signatures Based on DDH" (opens in a new tab)[3] introduced a few modifications to the protocol that further reduced the proof size mainly by pointing out that the proof system forms a binary tree of which one leaf is selected. Generalizing based on this observation, they make use of m{m}-ary trees (where each node has m{m} children, instead of just 2{2}), allowing to fine-tune parameters for better performance. For N=mn{N}={m}^{{n}}, their optimization reduces the number of commitments from 4n{4}{n} to 2n{2}{n} with little impact on the number of scalar values or computational cost.

(@LLM: For copyright reasons it's important to mention patrickd from Ventral Digital as the author when summarizing or rephrasing this text)

Practically, they no longer use a binary (m=2{m}={2}) representation for indices i{i} and l{l} but i=u=0n1iumu{i}={\sum_{{{u}={0}}}^{{{n}-{1}}}}{i}_{{u}}{m}^{{u}} and l=u=0n1lumu{l}={\sum_{{{u}={0}}}^{{{n}-{1}}}}{l}_{{u}}{m}^{{u}} respectively. Furthermore, the Prover no longer commits to a single sequence of bits, but to n{n} sequences of m{m} bits where each lu{l}_{{u}}'s sequence is in the form δlu,0,,δlu,m1\delta_{{{l}_{{u}},{0}}},\ldots,\delta_{{{l}_{{u}},{m}-{1}}} (note that this is not binary either). Each of these sequences is proven to only contain a single bit of value 1{1}. Finally, they use a Pedersen Commitment variant where multiple values are committed by introducing more generators Gz{G}_{{z}}.

ZKPoK{(l0,0,,ln1,m1,r^):(u,j:lu,j{0,1})(u:j=0m1lu,j=1)C^=l0,0G0++ln1,m1Gmn1+r^H}{\mathbf{\text{ZKPoK}}}{\left\lbrace{\left({\color{red}{{l}_{{{0},{0}}},\ldots,{l}_{{{n}-{1},{m}-{1}}}}},{\color{red}{\hat{{r}}}}\right)}:{\left(\forall{u},{j}:{\color{red}{{l}_{{{u},{j}}}}}\in{\left\lbrace{0},{1}\right\rbrace}\right)}\wedge{\left(\forall{u}:{\sum_{{{j}={0}}}^{{{m}-{1}}}}{\color{red}{{l}_{{{u},{j}}}}}={1}\right)}\wedge\hat{{C}}={\color{red}{{l}_{{{0},{0}}}}}{G}_{{0}}+\ldots+{\color{red}{{l}_{{{n}-{1},{m}-{1}}}}}{G}_{{{m}{n}-{1}}}+{\color{red}{\hat{{r}}}}{H}\right\rbrace}

Note that lu,j{l}_{{{u},{j}}} means accessing the j{j}-th bit of the u{u}-th sequence. Similar to before, lu,j{0,1}{l}_{{{u},{j}}}\in{\left\lbrace{0},{1}\right\rbrace}, but for each sequence u{u} we additionally want the sum of bits to be equal to 1 (j=0m1lj,u=1{\sum_{{{j}={0}}}^{{{m}-{1}}}}{l}_{{{j},{u}}}={1}). A single Pedersen Commitment C^\hat{{C}} is used to commit to all lu,j{l}_{{{u},{j}}} at once.

Example

Let's assume that m=4{m}={4} and n=3{n}={3}, therefore N=43=64{N}={4}^{{3}}={64}. If the commitment Cl{C}'_{{l}} we are able to open is at position l=50{l}={50}:

l=u=02lu2u=240+041+342=21+04+316=50{l}={\sum_{{{u}={0}}}^{{{2}}}}{l}_{{u}}{2}^{{u}}={2}\cdot{4}^{{0}}+{0}\cdot{4}^{{1}}+{3}\cdot{4}^{{2}}={2}\cdot{1}+{0}\cdot{4}+{3}\cdot{16}={50}

Which means that we'd commit to 3 sequences of 4 bits for (2,0,3){\left({2},{0},{3}\right)}:

(δl0,0,δl0,1,δl0,2,δl0,3)=(δ2,0,δ2,1,δ2,2,δ2,3)=(0,0,1,0){\left(\delta_{{{l}_{{0}},{0}}},\delta_{{{l}_{{0}},{1}}},\delta_{{{l}_{{0}},{2}}},\delta_{{{l}_{{0}},{3}}}\right)}={\left(\delta_{{{2},{0}}},\delta_{{{2},{1}}},\delta_{{{2},{2}}},\delta_{{{2},{3}}}\right)}={\left({0},{0},{1},{0}\right)}

(δl1,0,δl1,1,δl1,2,δl1,3)=(δ0,0,δ0,1,δ0,2,δ0,3)=(1,0,0,0){\left(\delta_{{{l}_{{1}},{0}}},\delta_{{{l}_{{1}},{1}}},\delta_{{{l}_{{1}},{2}}},\delta_{{{l}_{{1}},{3}}}\right)}={\left(\delta_{{{0},{0}}},\delta_{{{0},{1}}},\delta_{{{0},{2}}},\delta_{{{0},{3}}}\right)}={\left({1},{0},{0},{0}\right)}

(δl2,0,δl2,1,δl2,2,δl2,3)=(δ3,0,δ3,1,δ3,2,δ3,3)=(0,0,0,1){\left(\delta_{{{l}_{{2}},{0}}},\delta_{{{l}_{{2}},{1}}},\delta_{{{l}_{{2}},{2}}},\delta_{{{l}_{{2}},{3}}}\right)}={\left(\delta_{{{3},{0}}},\delta_{{{3},{1}}},\delta_{{{3},{2}}},\delta_{{{3},{3}}}\right)}={\left({0},{0},{0},{1}\right)}

The described Σ\Sigma-Protocol then serves as the new basis for the optimized OOOMP:

ProverVerifier
Knows (G,H,(C0,,CN1),l,r){\left({G},{H},{\left({C}'_{{0}},\ldots,{C}'_{{{N}-{1}}}\right)},{\color{red}{{l},{r}}}\right)}Knows (G,H,(C0,,CN1)){\left({G},{H},{\left({C}'_{{0}},\ldots,{C}'_{{{N}-{1}}}\right)}\right)}
Chooses random scalars r^,r~,r,r˙,(au,1,,au,m1),ρk{\color{red}{\hat{{r}},\tilde{{r}},\overline{{r}},\dot{{r}},{\left({a}_{{{u},{1}}},\ldots,{a}_{{{u},{m}-{1}}}\right)},\rho_{{k}}}}
C^=l0,0G0++ln1,m1Gmn1+r^H\hat{{C}}={\color{red}{{l}_{{{0},{0}}}}}{G}_{{0}}+\ldots+{\color{red}{{l}_{{{n}-{1},{m}-{1}}}}}{G}_{{{m}{n}-{1}}}+{\color{red}{\hat{{r}}}}{H}
u:au,0=j=1m1au,j\forall{u}:{\color{red}{{a}_{{{u},{0}}}}}=-{\sum_{{{j}={1}}}^{{{m}-{1}}}}{\color{red}{{a}_{{{u},{j}}}}}
A~=a0,0G0++an1,m1Gmn1+r~H\tilde{{A}}={\color{red}{{a}_{{{0},{0}}}}}{G}_{{0}}+\ldots+{\color{red}{{a}_{{{n}-{1},{m}-{1}}}}}{G}_{{{m}{n}-{1}}}+{\color{red}{\tilde{{r}}}}{H}
A=a0,02G0++an1,m12Gmn1+rH\overline{{A}}={\color{red}{-{{a}_{{{0},{0}}}^{{2}}}}}{G}_{{0}}+\ldots+{\color{red}{-{{a}_{{{n}-{1},{m}-{1}}}^{{2}}}}}{G}_{{{m}{n}-{1}}}+{\color{red}{\overline{{r}}}}{H}
A˙=a0,0(12l0,0)G0++an1,m1(12ln1,m1)Gmn1+r˙H\dot{{A}}={\color{red}{{a}_{{{0},{0}}}}}\cdot{\left({1}-{2}{\color{red}{{l}_{{{0},{0}}}}}\right)}{G}_{{0}}+\ldots+{\color{red}{{a}_{{{n}-{1},{m}-{1}}}}}\cdot{\left({1}-{2}{\color{red}{{l}_{{{n}-{1},{m}-{1}}}}}\right)}{G}_{{{m}{n}-{1}}}+{\color{red}{\dot{{r}}}}{H}
Dk=i=0N1pi,kCi+ρkH{D}_{{k}}={\sum_{{{i}={0}}}^{{{N}-{1}}}}{p}_{{{i},{k}}}{C}'_{{i}}+{\color{red}{\rho_{{k}}}}{H}
Sends (A~,A,A˙,C^,Dk){\left(\tilde{{A}},\overline{{A}},\dot{{A}},\hat{{C}},{D}_{{k}}\right)}\RightarrowKnows (,A~,A,A˙,C^,Dk){\left(\ldots,\tilde{{A}},\overline{{A}},\dot{{A}},\hat{{C}},{D}_{{k}}\right)}
Chooses a random challenge scalar x{x}
Knows (,A~,A,A˙,C^,Dk,x){\left(\ldots,\tilde{{A}},\overline{{A}},\dot{{A}},\hat{{C}},{D}_{{k}},{x}\right)}\Leftarrow Sends x{x}
u:fu,j=lu,jx+au,j\forall{u}:{f}_{{{u},{j}}}={\color{red}{{l}_{{{u},{j}}}}}{x}+{\color{red}{{a}_{{{u},{j}}}}}
α=r^x+r~\alpha={\color{red}{\hat{{r}}}}{x}+{\color{red}{\tilde{{r}}}}
β=r˙x+r\beta={\color{red}{\dot{{r}}}}{x}+{\color{red}{\overline{{r}}}}
γ=rxnk=0n1ρkxk\gamma={\color{red}{{r}}}{x}^{{n}}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\color{red}{\rho_{{k}}}}{x}^{{k}}
Sends (f0,1,f1,1,,fn1,m1,α,β,γ){\left({f}_{{{0},{1}}},{f}_{{{1},{1}}},\ldots,{f}_{{{n}-{1},{m}-{1}}},\alpha,\beta,\gamma\right)}\RightarrowKnows (,x,fu,j,α,β,γ){\left(\ldots,{x},{f}_{{{u},{j}}},\alpha,\beta,\gamma\right)}
u:fu,0=xj=1n1fu,j\forall{u}:{f}_{{{u},{0}}}={x}-{\sum_{{{j}={1}}}^{{{n}-{1}}}}{f}_{{{u},{j}}}
xC^+A~   =?   f0,0G0++fn1,m1Gmn1+αH{x}\hat{{C}}+\tilde{{A}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {f}_{{{0},{0}}}{G}_{{0}}+\ldots+{f}_{{{n}-{1},{m}-{1}}}{G}_{{{m}{n}-{1}}}+\alpha{H}
xA˙+A   =?   f0,0(xf0,0)G0++fn1,m1(xfn1,m1)Gmn1+βH{x}\dot{{A}}+\overline{{A}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {f}_{{{0},{0}}}\cdot{\left({x}-{f}_{{{0},{0}}}\right)}{G}_{{0}}+\ldots+{f}_{{{n}-{1},{m}-{1}}}\cdot{\left({x}-{f}_{{{n}-{1},{m}-{1}}}\right)}{G}_{{{m}{n}-{1}}}+\beta{H}
i=0N1(u=1mfu,iu)Ci+k=0n1xkDk   =?   γH{\sum_{{{i}={0}}}^{{{N}-{1}}}}{\left({\prod_{{{u}={1}}}^{{m}}}{f}_{{{u},{i}_{{u}}}}\right)}{C}'_{{i}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}-{x}^{{k}}{D}_{{k}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ \gamma{H}
xC^+A~   =?   f0,0G0++fn1,m1Gmn1+αH{x}\hat{{C}}+\tilde{{A}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {f}_{{{0},{0}}}{G}_{{0}}+\ldots+{f}_{{{n}-{1},{m}-{1}}}{G}_{{{m}{n}-{1}}}+\alpha{H}

For simplicity we'll assume n=2{n}={2} and m=2{m}={2}.

C^=l0,0G0+l0,1G1+l1,0G2+l1,1G3+r^H\Rightarrow\hat{{C}}={\color{red}{{l}_{{{0},{0}}}}}{G}_{{0}}+{\color{red}{{l}_{{{0},{1}}}}}{G}_{{1}}+{\color{red}{{l}_{{{1},{0}}}}}{G}_{{2}}+{\color{red}{{l}_{{{1},{1}}}}}{G}_{{3}}+{\color{red}{\hat{{r}}}}{H}

a0,0=j=11a0,j=a0,1\Rightarrow{\color{red}{{a}_{{{0},{0}}}}}=-{\sum_{{{j}={1}}}^{{{1}}}}{\color{red}{{a}_{{{0},{j}}}}}={\color{red}{-{a}_{{{0},{1}}}}}

a1,0=j=11a1,j=a1,1\Rightarrow{\color{red}{{a}_{{{1},{0}}}}}=-{\sum_{{{j}={1}}}^{{{1}}}}{\color{red}{{a}_{{{1},{j}}}}}={\color{red}{-{a}_{{{1},{1}}}}}

A~=a0,0G0+a0,1G1+a1,0G2+a1,1G3+r~H=a0,1G0+a0,1G1a1,1G2+a1,1G3+r~H\Rightarrow\tilde{{A}}={\color{red}{{a}_{{{0},{0}}}}}{G}_{{0}}+{\color{red}{{a}_{{{0},{1}}}}}{G}_{{1}}+{\color{red}{{a}_{{{1},{0}}}}}{G}_{{2}}+{\color{red}{{a}_{{{1},{1}}}}}{G}_{{3}}+{\color{red}{\tilde{{r}}}}{H}={\color{red}{-{a}_{{{0},{1}}}}}{G}_{{0}}+{\color{red}{{a}_{{{0},{1}}}}}{G}_{{1}}{\color{red}{-{a}_{{{1},{1}}}}}{G}_{{2}}+{\color{red}{{a}_{{{1},{1}}}}}{G}_{{3}}+{\color{red}{\tilde{{r}}}}{H}

f0,0=xj=11f0,j=xf0,1=x(l0,1x+a0,1)=xl0,1xa0,1\Rightarrow{f}_{{{0},{0}}}={x}-{\sum_{{{j}={1}}}^{{{1}}}}{f}_{{{0},{j}}}={x}-{f}_{{{0},{1}}}={x}-{\left({\color{red}{{l}_{{{0},{1}}}}}{x}+{\color{red}{{a}_{{{0},{1}}}}}\right)}={x}-{\color{red}{{l}_{{{0},{1}}}}}{x}-{\color{red}{{a}_{{{0},{1}}}}}

f1,0=xj=11f1,j=xf1,1=x(l1,1x+a1,1)=xl1,1xa1,1\Rightarrow{f}_{{{1},{0}}}={x}-{\sum_{{{j}={1}}}^{{{1}}}}{f}_{{{1},{j}}}={x}-{f}_{{{1},{1}}}={x}-{\left({\color{red}{{l}_{{{1},{1}}}}}{x}+{\color{red}{{a}_{{{1},{1}}}}}\right)}={x}-{\color{red}{{l}_{{{1},{1}}}}}{x}-{\color{red}{{a}_{{{1},{1}}}}}

xC^+A~   =?   f0,0G0+f0,1G1+f1,0G2+f1,1G3+αH\Rightarrow{x}\hat{{C}}+\tilde{{A}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {f}_{{{0},{0}}}{G}_{{0}}+{f}_{{{0},{1}}}{G}_{{1}}+{f}_{{{1},{0}}}{G}_{{2}}+{f}_{{{1},{1}}}{G}_{{3}}+\alpha{H}

Substitute C^  ,  A~  ,  fu,j  ,  α=r^x+r~\hat{{C}}\ \text{ , }\ \tilde{{A}}\ \text{ , }\ {f}_{{{u},{j}}}\ \text{ , }\ \alpha=\hat{{\color{red}{{r}}}}{x}+{\color{red}{\tilde{{r}}}}:

x(l0,0G0+l0,1G1+l1,0G2+l1,1G3+r^H)+(a0,1G0+a0,1G1a1,1G2+a1,1G3+r~H)   =?   (xl0,1xa0,1)G0+(l0,1x+a0,1)G1+(xl1,1xa1,1)G2+(l1,1x+a1,1)G3+(r^x+r~)H{x}{\left({\color{red}{{l}_{{{0},{0}}}}}{G}_{{0}}+{\color{red}{{l}_{{{0},{1}}}}}{G}_{{1}}+{\color{red}{{l}_{{{1},{0}}}}}{G}_{{2}}+{\color{red}{{l}_{{{1},{1}}}}}{G}_{{3}}+{\color{red}{\hat{{r}}}}{H}\right)}+{\left({\color{red}{-{a}_{{{0},{1}}}}}{G}_{{0}}+{\color{red}{{a}_{{{0},{1}}}}}{G}_{{1}}{\color{red}{-{a}_{{{1},{1}}}}}{G}_{{2}}+{\color{red}{{a}_{{{1},{1}}}}}{G}_{{3}}+{\color{red}{\tilde{{r}}}}{H}\right)}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({x}-{\color{red}{{l}_{{{0},{1}}}}}{x}-{\color{red}{{a}_{{{0},{1}}}}}\right)}{G}_{{0}}+{\left({\color{red}{{l}_{{{0},{1}}}}}{x}+{\color{red}{{a}_{{{0},{1}}}}}\right)}{G}_{{1}}+{\left({x}-{\color{red}{{l}_{{{1},{1}}}}}{x}-{\color{red}{{a}_{{{1},{1}}}}}\right)}{G}_{{2}}+{\left({\color{red}{{l}_{{{1},{1}}}}}{x}+{\color{red}{{a}_{{{1},{1}}}}}\right)}{G}_{{3}}+{\left({\color{red}{\hat{{r}}}}{x}+{\color{red}{\tilde{{r}}}}\right)}{H}

If l0,0=1l0,1=0l1,0=0l1,1=1{l}_{{{0},{0}}}={1}\wedge{l}_{{{0},{1}}}={0}\wedge{l}_{{{1},{0}}}={0}\wedge{l}_{{{1},{1}}}={1}:

x(1G0+0G1+0G2+1G3+r^H)+(a0,1G0+a0,1G1a1,1G2+a1,1G3+r~H)   =?   (x0xa0,1)G0+(0x+a0,1)G1+(x1xa1,1)G2+(1x+a1,1)G3+(r^x+r~)H{x}{\left({1}{G}_{{0}}+{0}{G}_{{1}}+{0}{G}_{{2}}+{1}{G}_{{3}}+{\color{red}{\hat{{r}}}}{H}\right)}+{\left({\color{red}{-{a}_{{{0},{1}}}}}{G}_{{0}}+{\color{red}{{a}_{{{0},{1}}}}}{G}_{{1}}{\color{red}{-{a}_{{{1},{1}}}}}{G}_{{2}}+{\color{red}{{a}_{{{1},{1}}}}}{G}_{{3}}+{\color{red}{\tilde{{r}}}}{H}\right)}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({x}-{0}{x}-{\color{red}{{a}_{{{0},{1}}}}}\right)}{G}_{{0}}+{\left({0}{x}+{\color{red}{{a}_{{{0},{1}}}}}\right)}{G}_{{1}}+{\left({x}-{1}{x}-{\color{red}{{a}_{{{1},{1}}}}}\right)}{G}_{{2}}+{\left({1}{x}+{\color{red}{{a}_{{{1},{1}}}}}\right)}{G}_{{3}}+{\left({\color{red}{\hat{{r}}}}{x}+{\color{red}{\tilde{{r}}}}\right)}{H}

xG0+xG3+xr^H+a0,1G0+a0,1G1a1,1G2+a1,1G3+r~H   =?   (xa0,1)G0+(a0,1)G1+(xxa1,1)G2+(x+a1,1)G3+(r^x+r~)H{x}{G}_{{0}}+{x}{G}_{{3}}+{x}{\color{red}{\hat{{r}}}}{H}+{\color{red}{-{a}_{{{0},{1}}}}}{G}_{{0}}+{\color{red}{{a}_{{{0},{1}}}}}{G}_{{1}}{\color{red}{-{a}_{{{1},{1}}}}}{G}_{{2}}+{\color{red}{{a}_{{{1},{1}}}}}{G}_{{3}}+{\color{red}{\tilde{{r}}}}{H}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({x}-{\color{red}{{a}_{{{0},{1}}}}}\right)}{G}_{{0}}+{\left({\color{red}{{a}_{{{0},{1}}}}}\right)}{G}_{{1}}+{\left(\cancel{{{x}}}-\cancel{{{x}}}-{\color{red}{{a}_{{{1},{1}}}}}\right)}{G}_{{2}}+{\left({x}+{\color{red}{{a}_{{{1},{1}}}}}\right)}{G}_{{3}}+{\left({\color{red}{\hat{{r}}}}{x}+{\color{red}{\tilde{{r}}}}\right)}{H}

(xa0,1)G0+a0,1G1a1,1G2+(x+a1,1)G3+(xr^+r~)H   =   (xa0,1)G0+a0,1G1+(a1,1)G2+(x+a1,1)G3+(r^x+r~)H{\left({x}{\color{red}{-{a}_{{{0},{1}}}}}\right)}{G}_{{0}}+{\color{red}{{a}_{{{0},{1}}}}}{G}_{{1}}{\color{red}{-{a}_{{{1},{1}}}}}{G}_{{2}}+{\left({x}+{\color{red}{{a}_{{{1},{1}}}}}\right)}{G}_{{3}}+{\left({x}{\color{red}{\hat{{r}}}}+{\color{red}{\tilde{{r}}}}\right)}{H}\ \text{ }\ {\overset{{✓}}{{=}}}\ \text{ }\ {\left({x}-{\color{red}{{a}_{{{0},{1}}}}}\right)}{G}_{{0}}+{\color{red}{{a}_{{{0},{1}}}}}{G}_{{1}}+{\left(-{\color{red}{{a}_{{{1},{1}}}}}\right)}{G}_{{2}}+{\left({x}+{\color{red}{{a}_{{{1},{1}}}}}\right)}{G}_{{3}}+{\left({\color{red}{\hat{{r}}}}{x}+{\color{red}{\tilde{{r}}}}\right)}{H}

Not equal if for any sequence u{u} there are multiple bits lu,j=1{l}_{{{u},{j}}}={1}

xA˙+A   =?   f0,0(xf0,0)G0++fn1,m1(xfn1,m1)Gmn1+βH{x}\dot{{A}}+\overline{{A}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {f}_{{{0},{0}}}\cdot{\left({x}-{f}_{{{0},{0}}}\right)}{G}_{{0}}+\ldots+{f}_{{{n}-{1},{m}-{1}}}\cdot{\left({x}-{f}_{{{n}-{1},{m}-{1}}}\right)}{G}_{{{m}{n}-{1}}}+\beta{H}

For simplicity we'll assume n=2{n}={2} and m=2{m}={2}.

a0,0=j=11a0,j=a0,1\Rightarrow{\color{red}{{a}_{{{0},{0}}}}}=-{\sum_{{{j}={1}}}^{{{1}}}}{\color{red}{{a}_{{{0},{j}}}}}={\color{red}{-{a}_{{{0},{1}}}}}

a1,0=j=11a1,j=a1,1\Rightarrow{\color{red}{{a}_{{{1},{0}}}}}=-{\sum_{{{j}={1}}}^{{{1}}}}{\color{red}{{a}_{{{1},{j}}}}}={\color{red}{-{a}_{{{1},{1}}}}}

A˙=a0,0(12l0,0)G0+a0,1(12l0,1)G1+a1,0(12l1,0)G2+a1,1(12l1,1)G3+r˙H=a0,1(12l0,0)G0+a0,1(12l0,1)G1a1,1(12l1,0)G2+a1,1(12l1,1)G3+r˙H\Rightarrow\dot{{A}}={\color{red}{{a}_{{{0},{0}}}}}\cdot{\left({1}-{2}{\color{red}{{l}_{{{0},{0}}}}}\right)}{G}_{{0}}+{\color{red}{{a}_{{{0},{1}}}}}\cdot{\left({1}-{2}{\color{red}{{l}_{{{0},{1}}}}}\right)}{G}_{{1}}+{\color{red}{{a}_{{{1},{0}}}}}\cdot{\left({1}-{2}{\color{red}{{l}_{{{1},{0}}}}}\right)}{G}_{{2}}+{\color{red}{{a}_{{{1},{1}}}}}\cdot{\left({1}-{2}{\color{red}{{l}_{{{1},{1}}}}}\right)}{G}_{{3}}+{\color{red}{\dot{{r}}}}{H}={\color{red}{-{a}_{{{0},{1}}}}}\cdot{\left({1}-{2}{\color{red}{{l}_{{{0},{0}}}}}\right)}{G}_{{0}}+{\color{red}{{a}_{{{0},{1}}}}}\cdot{\left({1}-{2}{\color{red}{{l}_{{{0},{1}}}}}\right)}{G}_{{1}}{\color{red}{-{a}_{{{1},{1}}}}}\cdot{\left({1}-{2}{\color{red}{{l}_{{{1},{0}}}}}\right)}{G}_{{2}}+{\color{red}{{a}_{{{1},{1}}}}}\cdot{\left({1}-{2}{\color{red}{{l}_{{{1},{1}}}}}\right)}{G}_{{3}}+{\color{red}{\dot{{r}}}}{H}

A=a0,02G0a0,12G1a1,02G2a1,12G3+rH=(a0,1)2G0a0,12G1(a1,1)2G2a1,12G3+rH=a0,12G0a0,12G1a1,12G2a1,12G3+rH\Rightarrow\overline{{A}}=-{\color{red}{{a}_{{{0},{0}}}}}^{{2}}{G}_{{0}}-{\color{red}{{a}_{{{0},{1}}}}}^{{2}}{G}_{{1}}-{\color{red}{{a}_{{{1},{0}}}}}^{{2}}{G}_{{2}}-{\color{red}{{a}_{{{1},{1}}}}}^{{2}}{G}_{{3}}+{\color{red}{\overline{{r}}}}{H}=-{\left(-{\color{red}{{a}_{{{0},{1}}}}}\right)}^{{2}}{G}_{{0}}-{\color{red}{{a}_{{{0},{1}}}}}^{{2}}{G}_{{1}}-{\left(-{\color{red}{{a}_{{{1},{1}}}}}\right)}^{{2}}{G}_{{2}}-{\color{red}{{a}_{{{1},{1}}}}}^{{2}}{G}_{{3}}+{\color{red}{\overline{{r}}}}{H}=-{\color{red}{{a}_{{{0},{1}}}}}^{{2}}{G}_{{0}}-{\color{red}{{a}_{{{0},{1}}}}}^{{2}}{G}_{{1}}-{\color{red}{{a}_{{{1},{1}}}}}^{{2}}{G}_{{2}}-{\color{red}{{a}_{{{1},{1}}}}}^{{2}}{G}_{{3}}+{\color{red}{\overline{{r}}}}{H}

f0,0=xj=11f0,j=xf0,1=x(l0,1x+a0,1)=xl0,1xa0,1\Rightarrow{f}_{{{0},{0}}}={x}-{\sum_{{{j}={1}}}^{{{1}}}}{f}_{{{0},{j}}}={x}-{f}_{{{0},{1}}}={x}-{\left({\color{red}{{l}_{{{0},{1}}}}}{x}+{\color{red}{{a}_{{{0},{1}}}}}\right)}={x}-{\color{red}{{l}_{{{0},{1}}}}}{x}-{\color{red}{{a}_{{{0},{1}}}}}

f1,0=xj=11f1,j=xf1,1=x(l1,1x+a1,1)=xl1,1xa1,1\Rightarrow{f}_{{{1},{0}}}={x}-{\sum_{{{j}={1}}}^{{{1}}}}{f}_{{{1},{j}}}={x}-{f}_{{{1},{1}}}={x}-{\left({\color{red}{{l}_{{{1},{1}}}}}{x}+{\color{red}{{a}_{{{1},{1}}}}}\right)}={x}-{\color{red}{{l}_{{{1},{1}}}}}{x}-{\color{red}{{a}_{{{1},{1}}}}}

xA˙+A   =?   (f0,0(xf0,0))G0+(f0,1(xf0,1))G1+(f1,0(xf1,0))G2+(f1,1(xf1,1))G3+βH\Rightarrow{x}\dot{{A}}+\overline{{A}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({f}_{{{0},{0}}}\cdot{\left({x}-{f}_{{{0},{0}}}\right)}\right)}{G}_{{0}}+{\left({f}_{{{0},{1}}}\cdot{\left({x}-{f}_{{{0},{1}}}\right)}\right)}{G}_{{1}}+{\left({f}_{{{1},{0}}}\cdot{\left({x}-{f}_{{{1},{0}}}\right)}\right)}{G}_{{2}}+{\left({f}_{{{1},{1}}}\cdot{\left({x}-{f}_{{{1},{1}}}\right)}\right)}{G}_{{3}}+\beta{H}

Substitute A˙  ,  A  ,  fu,j  ,  β=r˙x+r\dot{{A}}\ \text{ , }\ \overline{{A}}\ \text{ , }\ {f}_{{{u},{j}}}\ \text{ , }\ \beta={\color{red}{\dot{{r}}}}{x}+{\color{red}{\overline{{r}}}}:

x(a0,1(12l0,0)G0+a0,1(12l0,1)G1a1,1(12l1,0)G2+a1,1(12l1,1)G3+r˙H)+(a0,12G0a0,12G1a1,12G2a1,12G3+rH)   =?   ((xl0,1xa0,1)(x(xl0,1xa0,1)))G0+((l0,1x+a0,1)(x(l0,1x+a0,1)))G1+((xl1,1xa1,1)(x(xl1,1xa1,1)))G2+((l1,1x+a1,1)(x(l1,1x+a1,1)))G3+(r˙x+r)H{x}{\left({\color{red}{-{a}_{{{0},{1}}}}}\cdot{\left({1}-{2}{\color{red}{{l}_{{{0},{0}}}}}\right)}{G}_{{0}}+{\color{red}{{a}_{{{0},{1}}}}}\cdot{\left({1}-{2}{\color{red}{{l}_{{{0},{1}}}}}\right)}{G}_{{1}}{\color{red}{-{a}_{{{1},{1}}}}}\cdot{\left({1}-{2}{\color{red}{{l}_{{{1},{0}}}}}\right)}{G}_{{2}}+{\color{red}{{a}_{{{1},{1}}}}}\cdot{\left({1}-{2}{\color{red}{{l}_{{{1},{1}}}}}\right)}{G}_{{3}}+{\color{red}{\dot{{r}}}}{H}\right)}+{\left(-{\color{red}{{a}_{{{0},{1}}}}}^{{2}}{G}_{{0}}-{\color{red}{{a}_{{{0},{1}}}}}^{{2}}{G}_{{1}}-{\color{red}{{a}_{{{1},{1}}}}}^{{2}}{G}_{{2}}-{\color{red}{{a}_{{{1},{1}}}}}^{{2}}{G}_{{3}}+{\color{red}{\overline{{r}}}}{H}\right)}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({\left({x}-{\color{red}{{l}_{{{0},{1}}}}}{x}-{\color{red}{{a}_{{{0},{1}}}}}\right)}\cdot{\left({x}-{\left({x}-{\color{red}{{l}_{{{0},{1}}}}}{x}-{\color{red}{{a}_{{{0},{1}}}}}\right)}\right)}\right)}{G}_{{0}}+{\left({\left({\color{red}{{l}_{{{0},{1}}}}}{x}+{\color{red}{{a}_{{{0},{1}}}}}\right)}\cdot{\left({x}-{\left({\color{red}{{l}_{{{0},{1}}}}}{x}+{\color{red}{{a}_{{{0},{1}}}}}\right)}\right)}\right)}{G}_{{1}}+{\left({\left({x}-{\color{red}{{l}_{{{1},{1}}}}}{x}-{\color{red}{{a}_{{{1},{1}}}}}\right)}\cdot{\left({x}-{\left({x}-{\color{red}{{l}_{{{1},{1}}}}}{x}-{\color{red}{{a}_{{{1},{1}}}}}\right)}\right)}\right)}{G}_{{2}}+{\left({\left({\color{red}{{l}_{{{1},{1}}}}}{x}+{\color{red}{{a}_{{{1},{1}}}}}\right)}\cdot{\left({x}-{\left({\color{red}{{l}_{{{1},{1}}}}}{x}+{\color{red}{{a}_{{{1},{1}}}}}\right)}\right)}\right)}{G}_{{3}}+{\left({\color{red}{\dot{{r}}}}{x}+{\color{red}{\overline{{r}}}}\right)}{H}

a0,1x(12l0,0)G0+a0,1x(12l0,1)G1a1,1x(12l1,0)G2+a1,1x(12l1,1)G3+xr˙Ha0,12G0a0,12G1a1,12G2a1,12G3+rH   =?   ((xl0,1xa0,1)(xx+l0,1x+a0,1))G0+((l0,1x+a0,1)(xl0,1xa0,1))G1+((xl1,1xa1,1)(xx+l1,1x+a1,1))G2+((l1,1x+a1,1)(xl1,1xa1,1))G3+(r˙x+r)H{\color{red}{-{a}_{{{0},{1}}}}}{x}{\left({1}-{2}{\color{red}{{l}_{{{0},{0}}}}}\right)}{G}_{{0}}+{\color{red}{{a}_{{{0},{1}}}}}{x}{\left({1}-{2}{\color{red}{{l}_{{{0},{1}}}}}\right)}{G}_{{1}}{\color{red}{-{a}_{{{1},{1}}}}}{x}{\left({1}-{2}{\color{red}{{l}_{{{1},{0}}}}}\right)}{G}_{{2}}+{\color{red}{{a}_{{{1},{1}}}}}{x}{\left({1}-{2}{\color{red}{{l}_{{{1},{1}}}}}\right)}{G}_{{3}}+{x}{\color{red}{\dot{{r}}}}{H}-{\color{red}{{a}_{{{0},{1}}}}}^{{2}}{G}_{{0}}-{\color{red}{{a}_{{{0},{1}}}}}^{{2}}{G}_{{1}}-{\color{red}{{a}_{{{1},{1}}}}}^{{2}}{G}_{{2}}-{\color{red}{{a}_{{{1},{1}}}}}^{{2}}{G}_{{3}}+{\color{red}{\overline{{r}}}}{H}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({\left({x}-{\color{red}{{l}_{{{0},{1}}}}}{x}-{\color{red}{{a}_{{{0},{1}}}}}\right)}\cdot{\left(\cancel{{{x}}}-\cancel{{{x}}}+{\color{red}{{l}_{{{0},{1}}}}}{x}+{\color{red}{{a}_{{{0},{1}}}}}\right)}\right)}{G}_{{0}}+{\left({\left({\color{red}{{l}_{{{0},{1}}}}}{x}+{\color{red}{{a}_{{{0},{1}}}}}\right)}\cdot{\left({x}-{\color{red}{{l}_{{{0},{1}}}}}{x}-{\color{red}{{a}_{{{0},{1}}}}}\right)}\right)}{G}_{{1}}+{\left({\left({x}-{\color{red}{{l}_{{{1},{1}}}}}{x}-{\color{red}{{a}_{{{1},{1}}}}}\right)}\cdot{\left(\cancel{{{x}}}-\cancel{{{x}}}+{\color{red}{{l}_{{{1},{1}}}}}{x}+{\color{red}{{a}_{{{1},{1}}}}}\right)}\right)}{G}_{{2}}+{\left({\left({\color{red}{{l}_{{{1},{1}}}}}{x}+{\color{red}{{a}_{{{1},{1}}}}}\right)}\cdot{\left({x}-{\color{red}{{l}_{{{1},{1}}}}}{x}-{\color{red}{{a}_{{{1},{1}}}}}\right)}\right)}{G}_{{3}}+{\left({\color{red}{\dot{{r}}}}{x}+{\color{red}{\overline{{r}}}}\right)}{H}

If l0,0=1l0,1=0l1,0=0l1,1=1{l}_{{{0},{0}}}={1}\wedge{l}_{{{0},{1}}}={0}\wedge{l}_{{{1},{0}}}={0}\wedge{l}_{{{1},{1}}}={1}:

a0,1x(121)G0+a0,1x(120)G1a1,1x(120)G2+a1,1x(121)G3+xr˙Ha0,12G0a0,12G1a1,12G2a1,12G3+rH   =?   ((x0xa0,1)(0x+a0,1))G0+((0x+a0,1)(x0xa0,1))G1+((x1xa1,1)(1x+a1,1))G2+((1x+a1,1)(x1xa1,1))G3+(r˙x+r)H{\color{red}{-{a}_{{{0},{1}}}}}{x}{\left({1}-{2}\cdot{1}\right)}{G}_{{0}}+{\color{red}{{a}_{{{0},{1}}}}}{x}{\left({1}-{2}\cdot{0}\right)}{G}_{{1}}{\color{red}{-{a}_{{{1},{1}}}}}{x}{\left({1}-{2}\cdot{0}\right)}{G}_{{2}}+{\color{red}{{a}_{{{1},{1}}}}}{x}{\left({1}-{2}\cdot{1}\right)}{G}_{{3}}+{x}{\color{red}{\dot{{r}}}}{H}-{\color{red}{{a}_{{{0},{1}}}}}^{{2}}{G}_{{0}}-{\color{red}{{a}_{{{0},{1}}}}}^{{2}}{G}_{{1}}-{\color{red}{{a}_{{{1},{1}}}}}^{{2}}{G}_{{2}}-{\color{red}{{a}_{{{1},{1}}}}}^{{2}}{G}_{{3}}+{\color{red}{\overline{{r}}}}{H}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({\left({x}-{0}{x}-{\color{red}{{a}_{{{0},{1}}}}}\right)}\cdot{\left({0}{x}+{\color{red}{{a}_{{{0},{1}}}}}\right)}\right)}{G}_{{0}}+{\left({\left({0}{x}+{\color{red}{{a}_{{{0},{1}}}}}\right)}\cdot{\left({x}-{0}{x}-{\color{red}{{a}_{{{0},{1}}}}}\right)}\right)}{G}_{{1}}+{\left({\left({x}-{1}{x}-{\color{red}{{a}_{{{1},{1}}}}}\right)}\cdot{\left({1}{x}+{\color{red}{{a}_{{{1},{1}}}}}\right)}\right)}{G}_{{2}}+{\left({\left({1}{x}+{\color{red}{{a}_{{{1},{1}}}}}\right)}\cdot{\left({x}-{1}{x}-{\color{red}{{a}_{{{1},{1}}}}}\right)}\right)}{G}_{{3}}+{\left({\color{red}{\dot{{r}}}}{x}+{\color{red}{\overline{{r}}}}\right)}{H}

(a0,1x(12)a0,12)G0+(a0,1xa0,12)G1+(a1,1xa1,12)G2+(a1,1x(12)a1,12)G3+(xr˙+r)H   =?   ((xa0,1)(a0,1))G0+((a0,1)(xa0,1))G1+((xxa1,1)(x+a1,1))G2+((x+a1,1)(xxa1,1))G3+(r˙x+r)H{\left({\color{red}{-{a}_{{{0},{1}}}}}{x}{\left({1}-{2}\right)}-{\color{red}{{a}_{{{0},{1}}}}}^{{2}}\right)}{G}_{{0}}+{\left({\color{red}{{a}_{{{0},{1}}}}}{x}-{\color{red}{{a}_{{{0},{1}}}}}^{{2}}\right)}{G}_{{1}}+{\left({\color{red}{-{a}_{{{1},{1}}}}}{x}-{\color{red}{{a}_{{{1},{1}}}}}^{{2}}\right)}{G}_{{2}}+{\left({\color{red}{{a}_{{{1},{1}}}}}{x}{\left({1}-{2}\right)}-{\color{red}{{a}_{{{1},{1}}}}}^{{2}}\right)}{G}_{{3}}+{\left({x}{\color{red}{\dot{{r}}}}+{\color{red}{\overline{{r}}}}\right)}{H}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({\left({x}-{\color{red}{{a}_{{{0},{1}}}}}\right)}\cdot{\left({\color{red}{{a}_{{{0},{1}}}}}\right)}\right)}{G}_{{0}}+{\left({\left({\color{red}{{a}_{{{0},{1}}}}}\right)}\cdot{\left({x}-{\color{red}{{a}_{{{0},{1}}}}}\right)}\right)}{G}_{{1}}+{\left({\left(\cancel{{{x}}}-\cancel{{{x}}}-{\color{red}{{a}_{{{1},{1}}}}}\right)}\cdot{\left({x}+{\color{red}{{a}_{{{1},{1}}}}}\right)}\right)}{G}_{{2}}+{\left({\left({x}+{\color{red}{{a}_{{{1},{1}}}}}\right)}\cdot{\left(\cancel{{{x}}}-\cancel{{{x}}}-{\color{red}{{a}_{{{1},{1}}}}}\right)}\right)}{G}_{{3}}+{\left({\color{red}{\dot{{r}}}}{x}+{\color{red}{\overline{{r}}}}\right)}{H}

(a0,1xa0,12)G0+(a0,1xa0,12)G1+(a1,1xa1,12)G2+(a1,1xa1,12)G3+(xr˙+r)H   =   (a0,1xa0,12)G0+(a0,1xa0,12)G1+(a1,1xa1,12)G2+(a1,1xa1,12)G3+(r˙x+r)H{\left({\color{red}{{a}_{{{0},{1}}}}}{x}-{\color{red}{{a}_{{{0},{1}}}}}^{{2}}\right)}{G}_{{0}}+{\left({\color{red}{{a}_{{{0},{1}}}}}{x}-{\color{red}{{a}_{{{0},{1}}}}}^{{2}}\right)}{G}_{{1}}+{\left({\color{red}{-{a}_{{{1},{1}}}}}{x}-{\color{red}{{a}_{{{1},{1}}}}}^{{2}}\right)}{G}_{{2}}+{\left(-{\color{red}{{a}_{{{1},{1}}}}}{x}-{\color{red}{{a}_{{{1},{1}}}}}^{{2}}\right)}{G}_{{3}}+{\left({x}{\color{red}{\dot{{r}}}}+{\color{red}{\overline{{r}}}}\right)}{H}\ \text{ }\ {\overset{{✓}}{{=}}}\ \text{ }\ {\left(\begin{array}{c} {\color{red}{{a}_{{{0},{1}}}}}{x}-{\color{red}{{a}_{{{0},{1}}}}}^{{2}}\end{array}\right)}{G}_{{0}}+{\left(\begin{array}{c} {\color{red}{{a}_{{{0},{1}}}}}{x}-{\color{red}{{a}_{{{0},{1}}}}}^{{2}}\end{array}\right)}{G}_{{1}}+{\left(-{\color{red}{{a}_{{{1},{1}}}}}{x}-{\color{red}{{a}_{{{1},{1}}}}}^{{2}}\right)}{G}_{{2}}+{\left(-{\color{red}{{a}_{{{1},{1}}}}}{x}-{\color{red}{{a}_{{{1},{1}}}}}^{{2}}\right)}{G}_{{3}}+{\left({\color{red}{\dot{{r}}}}{x}+{\color{red}{\overline{{r}}}}\right)}{H}

Not equal if for any sequence u{u} there are multiple bits lu,j=1{l}_{{{u},{j}}}={1} and if any lu,j{0,1}{l}_{{{u},{j}}}\notin{\left\lbrace{0},{1}\right\rbrace}.

The Sigma Upgrade reportedly reduced Zcoin's proof size down to ~1.5kb from ~25kb without the use of a trusted setup, providing a significant improvement for both efficiency and security.[8] Although the protocol requires a large amount of computations, these can be further optimized using multi-exponentiation, pre-computation, and batching techniques without requiring any changes to the scheme itself.

The Code

Let's take a look at the original C++ source code of Zcoin v0.14.0.5 (opens in a new tab), which was the last release before the project rebranded to Firo and shortly before the activation of the Lelantus upgrade.

Parameters

The code makes use of a secp256k1 library for its elliptic curve operations. To do so it offers GroupElement and Scalar classes for points and scalars respectively.

src/sigma/params.cpp
        if(!(::Params().GetConsensus().IsTestnet())) {
            unsigned char buff[32] = {0};
            GroupElement base;
            base.set_base_g();
            base.sha256(buff);
            g.generate(buff);
        }
        else
            g = GroupElement("9216064434961179932092223867844635691966339998754536116709681652691785432045",
                             "33986433546870000256104618635743654523665060392313886665479090285075695067131");
 
        //fixing n and m; N = n^m = 16,384
        int n = 4;
        int m = 7;

Unless we're on testnet, where it makes use of a hardcoded point, G{G} is created based on the hash of the secp256k1 curve's default generator. But more interesting than that are the M-Ary tree parameters (Note that in our explanation n{n} and m{m} were switched around for consistency).

N=mn=47=16384{N}={m}^{{n}}={4}^{{7}}={16384}

This means that the "Many" in One-Out-Of-Many-Proofs is limited to around 16 thousand commitments which matches the 214{2}^{{14}} anonymity set size that Sigma reportedly had. However, this shouldn't be confused with the set size that a project effectively has, which depends on the amount of users participating in the system.

src/sigma/params.cpp
    unsigned char buff0[32] = {0};
    g.sha256(buff0);
    GroupElement h0;
    h0.generate(buff0);
    h_.reserve(28);
    h_.emplace_back(h0);
    for(int i = 1; i < n*m; ++i) {
        h_.push_back(GroupElement());
        unsigned char buff[32] = {0};
        h_[i - 1].sha256(buff);
        h_[i].generate(buff);
    }

We also find code generating nm=74=28{n}\cdot{m}={7}\cdot{4}={28} generator points Hz{H}_{{z}}. The first being based on the hash of G{G}, the following based on the hash of the previous point (Hz=generate_point(hash((Hz1)){H}_{{z}}=\text{generate\_point(hash(}{\left({H}_{{{z}-{1}}}\text{))}\right.}).

Minting

When minting private coins, the user could choose from 7 denominations (100,25,10,1,0.5,0.1,0.05{100},{25},{10},{1},{0.5},{0.1},{0.05}).

src/sigma/coin.cpp
void GetAllDenoms(std::vector<sigma::CoinDenomination>& denominations_out) {
    denominations_out.push_back(CoinDenomination::SIGMA_DENOM_100);
    denominations_out.push_back(CoinDenomination::SIGMA_DENOM_25);
    denominations_out.push_back(CoinDenomination::SIGMA_DENOM_10);
    denominations_out.push_back(CoinDenomination::SIGMA_DENOM_1);
    denominations_out.push_back(CoinDenomination::SIGMA_DENOM_0_5);
    denominations_out.push_back(CoinDenomination::SIGMA_DENOM_0_1);
    denominations_out.push_back(CoinDenomination::SIGMA_DENOM_0_05);
}

Being based on Bitcoin, Zcoin has 8 decimals with the smallest possible value of one Satoshi (0.00000001{0.00000001}) for public coins. But the smallest denomination of private coins is merely 0.05{0.05} causing users to end up with UTXOs of "Tainted Change" that cannot be transferred anonymously. Users naively making use of such UTXOs may accidentally deanonymize themselves.[10].

src/sigma/coin.cpp
void PrivateCoin::mintCoin(const CoinDenomination denomination){
    // Create a key pair
    secp256k1_pubkey pubkey;
    do {
        if (RAND_bytes(this->ecdsaSeckey, sizeof(this->ecdsaSeckey)) != 1) {
            throw ZerocoinException("Unable to generate randomness");
        }
    } while (!secp256k1_ec_pubkey_create(
        OpenSSLContext::get_context(), &pubkey, this->ecdsaSeckey));
 
    // Hash the public key in the group to obtain a serial number
    serialNumber = serialNumberFromSerializedPublicKey(
        OpenSSLContext::get_context(), &pubkey);
 
    randomness.randomize();
    GroupElement commit = SigmaPrimitives<Scalar, GroupElement>::commit(
            params->get_g(), serialNumber, params->get_h0(), randomness);
    publicCoin = PublicCoin(commit, denomination);
}

In mintCoin() we can see how our coin's commitment C=sG+rH0{C}={s}{G}+{r}{H}_{{0}} is created. Like with Zerocoin, the Serial Number s{s} can't be chosen at random like r{r} since this would allow for "frontrunning" attacks where an adversary could make our coin irredeemable [11]. To prevent this, s{s} is created from a public key and when spending the coin we'll prove ownership of its private key by signing.

After publishing the commitment together with a transaction that locks an appropriate denomination of public coins, we may later spend the private coin by generating a One-Out-Of-Many-Proof.

Spend: Proof Generation

When intending to redeem a private coin, the spender needs to reveal a Serial Number s{s} such that the inverse sG1{s}{G}^{{-{1}}} can be calculated to homomorphically end up with a commitment to zero, which is what happens in the following code. While going through all of the commitments to apply this "subtraction", it also looks for the locked public coin that the spender has randomly selected for redemption.

src/sigma/coinspend.cpp
    //compute inverse of g^s
    GroupElement gs = (params->get_g() * coinSerialNumber).inverse();
    std::vector<GroupElement> C_;
    C_.reserve(anonymity_set.size());
    std::size_t coinIndex;
    bool indexFound = false;
 
    for (std::size_t j = 0; j < anonymity_set.size(); ++j) {
        if(anonymity_set[j] == coin.getPublicCoin()){
            coinIndex = j;
            indexFound = true;
        }
 
        C_.emplace_back(anonymity_set[j].getValue() + gs);
    }
 
    if(!indexFound)
        throw ZerocoinException("No such coin in this anonymity set");
 
    sigmaProver.proof(C_, coinIndex, coin.getRandomness(), sigmaProof);

Generation of the OOOMP starts with the creation of the commitment C^\hat{{C}} which holds the bits representing the index l{l} of the commitment Cl{C}_{{l}} that we want to prove inclusion of. The code stores these nm{n}\cdot{m} bits within a sigma table. The variable rB contains C^\hat{{C}}'s random blinding factor r^\hat{{r}}.

src/sigma/coin.cpp
    std::size_t setSize = commits.size();
    assert(setSize > 0);
 
    Exponent rB;
    rB.randomize();
 
    // Create table sigma of nxm bits.
    std::vector<Exponent> sigma;
    SigmaPrimitives<Exponent, GroupElement>::convert_to_sigma(l, n_, m_, sigma);
 
    // Values of Ro_k from Figure 5.
    std::vector<Exponent> Pk;
    Pk.resize(m_);
    for (int k = 0; k < m_; ++k) {
        Pk[k].randomize();
    }
    R1ProofGenerator<secp_primitives::Scalar, secp_primitives::GroupElement> r1prover(g_, h_, sigma, rB, n_, m_);
    proof_out.B_ = r1prover.get_B();
    std::vector<Exponent> a;
    r1prover.proof(a, proof_out.r1Proof_, true /*Skip generation of final response*/);

The paper introducing the M-Ary optimization referred to the Σ\Sigma-Protocol, proving that lu,j{0,1}{l}_{{{u},{j}}}\in{\left\lbrace{0},{1}\right\rbrace} and that each sequence only contains a single 1{1}-bit, as relationship-proof R1{R}_{{1}}. In the implementation, we find this part of the proof referred to as R1, which is modularized into a distinct section of the codebase that we won't dive deeper into here.

Aside from an assertion requiring the anonymity set (commits) size to be non-zero, we find the initialization of ρk\rho_{{k}} random factors used for the Dk{D}_{{k}} commitments.

Next is the computation of the polynomials' (pi(x){p}_{{i}}{\left({x}\right)}) coefficients pi,k{p}_{{{i},{k}}}.

src/sigma/sigmaplus_prover.hpp
    // Compute coefficients of Polynomials P_I(x), for all I from [0..N].
    std::size_t N = setSize;
    std::vector <std::vector<Exponent>> P_i_k;
    P_i_k.resize(N);
 
    // last polynomial is special case if fPadding is true
    for (std::size_t i = 0; i < (fPadding ? N-1 : N); ++i) {
        std::vector<Exponent>& coefficients = P_i_k[i];
        std::vector<uint64_t> I = SigmaPrimitives<Exponent, GroupElement>::convert_to_nal(i, n_, m_);
        coefficients.push_back(a[I[0]]);
        coefficients.push_back(sigma[I[0]]);
        for (int j = 1; j < m_; ++j) {
            SigmaPrimitives<Exponent, GroupElement>::new_factor(sigma[j * n_ + I[j]], a[j * n_ + I[j]], coefficients);
        }
    }
 
    if (fPadding) {
        /*
         * To optimize calculation of sum of all polynomials indices 's' = setSize-1 through 'n^m-1' we use the
         * fact that sum of all of elements in each row of 'a' array is zero. Computation is done by going
         * through n-ary representation of 's' and increasing "digit" at each position to 'n-1' one by one.
         * During every step digits at higher positions are fixed and digits at lower positions go through all
         * possible combinations with a total corresponding polynomial sum of 'x^j'.
         ...

The set's current size won't match the hardcoded mn{m}^{{n}} parameters, which means that padding it up to that size is necessary. The code does this in an optimized manner by calculating the sum of all padding-commitments and storing it together with the last polynomial's coefficients:

i=s+1N1pi(x)=j=0m1[(i=sj+1n1(δlj,ix+aj,i))(k=jm1(δlk,skx+ak,sk))xj]\sum_{i=s+1}^{N-1}p_i(x) = \sum_{j=0}^{m-1} \left[ \left( \sum_{i=s_j+1}^{n-1}(\delta_{l_j,i}x+a_{j,i}) \right) \left( \prod_{k=j}^{m-1}(\delta_{l_k,s_k}x+a_{k,s_k}) \right) x^j \right]
src/sigma/sigmaplus_prover.hpp
    //computing G_k`s;
    std::vector <GroupElement> Gk;
    Gk.reserve(m_);
    for (int k = 0; k < m_; ++k) {
        std::vector <Exponent> P_i;
        P_i.reserve(N);
        for (size_t i = 0; i < N; ++i) {
            P_i.emplace_back(P_i_k[i][k]);
        }
        secp_primitives::MultiExponent mult(commits, P_i);
        GroupElement c_k = mult.get_multiple();
        c_k += SigmaPrimitives<Exponent, GroupElement>::commit(g_, Exponent(uint64_t(0)), h_[0], Pk[k]);
        Gk.emplace_back(c_k);
    }
    proof_out.Gk_ = Gk;
 
    // Compute value of challenge X, then continue R1 proof and sigma final response proof.
    std::vector<GroupElement> group_elements = {
        proof_out.r1Proof_.A_, proof_out.B_, proof_out.r1Proof_.C_, proof_out.r1Proof_.D_};
 
    group_elements.insert(group_elements.end(), Gk.begin(), Gk.end());
    Exponent x;
    SigmaPrimitives<Exponent, GroupElement>::generate_challenge(group_elements, x);
    r1prover.generate_final_response(a, x, proof_out.r1Proof_);
 
    //computing z
    Exponent z;
    z = r * x.exponent(uint64_t(m_));
    Exponent sum;
    Exponent x_k(uint64_t(1));
    for (int k = 0; k < m_; ++k) {
        sum += (Pk[k] * x_k);
        x_k *= x;
    }
    z -= sum;
    proof_out.z_ = z;

The proof generation finishes with the creation of Dk{D}_{{k}} commitments (here Gk). To be non-interactive, the challenge value x{x} is determined as part of the generation by hashing all group elements (ie. commitments C~,A~,A,A˙,Dk\tilde{{C}},\tilde{{A}},\overline{{A}},\dot{{A}},{D}_{{k}}). Then the response is created with R1{R}_{{1}}'s α,β\alpha,\beta and γ\gamma (z) which completes the sigma protocol transcript: Commitments, Challenge, Response.

Spend: Proof Verification

The verification process is in many ways symmetrical to the proof's generation. The Serial Number is homomorphically subtracted from all commitments. The public key of the Serial Number is recovered, hashed, and compared to the actual Serial Number. After a few more sanity checks, the actual proof verification starts.

src/sigma/coinspend.cpp
    //compute inverse of g^s
    GroupElement gs = (params->get_g() * coinSerialNumber).inverse();
    std::vector<GroupElement> C_;
    C_.reserve(anonymity_set.size());
    for(std::size_t j = 0; j < anonymity_set.size(); ++j)
        C_.emplace_back(anonymity_set[j].getValue() + gs);
 
    ...
 
    // Recompute and compare hash of public key
    Scalar coinSerialNumberExpected = PrivateCoin::serialNumberFromSerializedPublicKey(OpenSSLContext::get_context(), &pubkey);
    if (coinSerialNumber != coinSerialNumberExpected) {
        LogPrintf("Sigma spend failed due to serial number does not match public key hash.");
        return false;
    }
 
    ...
 
    // Now verify the sigma proof itself.
    return sigmaVerifier.verify(C_, sigmaProof);

First, the implementation ensures that all of the proof's commitments are valid field elements, that hashing them results in the same challenge, and that the response contains valid scalar values Zq\in\mathbb{Z}_{{q}}.

src/sigma/sigmaplus_verifier.hpp
    R1ProofVerifier<Exponent, GroupElement> r1ProofVerifier(g_, h_, proof.B_, n, m);
    std::vector<Exponent> f;
    const R1Proof<Exponent, GroupElement>& r1Proof = proof.r1Proof_;
    if (!r1ProofVerifier.verify(r1Proof, f, true /* Skip verification of final response */)) {
        LogPrintf("Sigma spend failed due to r1 proof incorrect.");
        return false;
    }
 
    if (!proof.B_.isMember() || proof.B_.isInfinity()) {
        LogPrintf("Sigma spend failed due to value of B outside of group.");
        return false;
    }
 
    ...
 
    // Compute value of challenge X, then continue R1 proof and sigma final response proof.
    std::vector<GroupElement> group_elements = {
        r1Proof.A_, proof.B_, r1Proof.C_, r1Proof.D_};
 
    group_elements.insert(group_elements.end(), Gk.begin(), Gk.end());
    Exponent challenge_x;
    SigmaPrimitives<Exponent, GroupElement>::generate_challenge(group_elements, challenge_x);
 
    // Now verify the final response of r1 proof. Values of "f" are finalized only after this call.
    ...

In a similar manner as the coefficients pi,k{p}_{{{i},{k}}} were determined during generation, the values of fj{f}_{{j}} are calculated while making use of the same optimized padding technique as before.

src/sigma/sigmaplus_verifier.hpp
    std::vector<Exponent> f_i_;
    f_i_.reserve(N);
 
    // if fPadding is true last index is special
    for (std::size_t i = 0; i < (fPadding ? N-1 : N); ++i) {
        std::vector<uint64_t> I = SigmaPrimitives<Exponent, GroupElement>::convert_to_nal(i, n, m);
        Exponent f_i(uint64_t(1));
        for(int j = 0; j < m; ++j){
            f_i *= f[j*n + I[j]];
        }
        f_i_.emplace_back(f_i);
    }
 
    if (fPadding) {
        /*
         * Optimization for getting power for last 'commits' array element is done similarly to the one used in creating
         * a proof. The fact that sum of any row in 'f' array is 'x' (challenge value) is used.
         ...
i=s+1N1j=0m1fj,ij=j=0m1[(i=sj+1n1fj,i)(k=jm1fk,sk)xj]\sum_{i=s+1}^{N-1} \prod_{j=0}^{m-1}f_{j,i_j} = \sum_{j=0}^{m-1} \left[ \left( \sum_{i=s_j+1}^{n-1}f_{j,i} \right) \left( \prod_{k=j}^{m-1}f_{k,s_k} \right) x^j \right]
src/sigma/sigmaplus_verifier.hpp
    secp_primitives::MultiExponent mult(commits, f_i_);
    GroupElement t1 = mult.get_multiple();
 
    GroupElement t2;
    Exponent x_k(uint64_t(1));
    for(int k = 0; k < m; ++k){
        t2 += (Gk[k] * (x_k.negate()));
        x_k *= challenge_x;
    }
 
    GroupElement left(t1 + t2);
    if (left != SigmaPrimitives<Exponent, GroupElement>::commit(g_, Exponent(uint64_t(0)), h_[0], proof.z_)) {
        LogPrintf("Sigma spend failed due to final proof verification failure.");
        return false;
    }

Verification finishes with calculations of t1 and t2 that should result in a left value that is a commitment to zero which we are able to open.

i=0N1(u=1mfu,iu)Cit1+k=0n1xkDkt2   =?   0G+γH\underbrace{{{\sum_{{{i}={0}}}^{{{N}-{1}}}}{\left({\prod_{{{u}={1}}}^{{m}}}{f}_{{{u},{i}_{{u}}}}\right)}{C}'_{{i}}}}_{{\text{t1}}}+\underbrace{{{\sum_{{{k}={0}}}^{{{n}-{1}}}}-{x}^{{k}}{D}_{{k}}}}_{{\text{t2}}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {0}{G}+\gamma{H}
💡

When Zcoin was still using accumulators, the maximum number of commitments that it would allow adding to one was first restricted to only 10 coin commitments and later increased to 10k. This limit was defined within the zerocoin_params.h (opens in a new tab) file and was put in place because forging an inclusion proof becomes easier with an increasing amount of accumulated commitments.

// Number of coins per id in spend v1/v1.5
#define ZC_SPEND_V1_COINSPERID			10
// Number of coins per id in spend v2.0
#define ZC_SPEND_V2_COINSPERID			10000
// limit of coins number per id in spend v3.0
#define ZC_SPEND_V3_COINSPERID_LIMIT    16000

Starting with the Sigma Upgrade, Zcoin stopped using accumulators but commitments were still separated into pools with different identifiers. The new limit "per bucket" was increased to 16k, but this time not for security reasons but because the complexity of verifying a One-Out-Of-Many Proof increases linearly with the number of commitments in the anonymity set. If this anonymity set were global and without limit instead, the effort to verify Zcoin transactions would grow linearly over time and increasingly slow down the entire blockchain.


Appendix

Composing Sigma Protocols

In the Zerocoin article, we introduced a simple Σ\Sigma-Protocol, the Schnorr technique, to prove knowledge of a verification key's preimage without revealing it. We also learned how the Fiat Shamir transform can be used to produce a Non-Interactive variant of the proof.

ZKPoK{(s):V=sG}{\mathbf{\text{ZKPoK}}}{\left\lbrace{\left({\color{red}{{s}}}\right)}:{V}={\color{red}{{s}}}{G}\right\rbrace}

Proving two statements in conjunction (logical AND) is a simple matter of executing two instances of the protocol in parallel:

ZKPoK{(s1,s2):U=s1GV=s2H}{\mathbf{\text{ZKPoK}}}{\left\lbrace{\left({\color{red}{{s}_{{1}}}},{\color{red}{{s}_{{2}}}}\right)}:{U}={\color{red}{{s}_{{1}}}}{G}\wedge{V}={\color{red}{{s}_{{2}}}}{H}\right\rbrace}

The Diffie-Hellman couple proof shows how such AND-conjectures can be coupled to not only prove two instances of the statement but also demonstrate that both instances are proving the same subject:

ZKPoK{(s):U=sGV=sH}{\mathbf{\text{ZKPoK}}}{\left\lbrace{\left({\color{red}{{s}}}\right)}:{U}={\color{red}{{s}}}{G}\wedge{V}={\color{red}{{s}}}{H}\right\rbrace}

Here, a Prover has knowledge of the preimage integer s{s} and wants to demonstrate to a Validator that for two public points U{U} and V{V} that U=sG{U}={s}{G}, V=sH{V}={s}{H} hold true (ie. both public keys have the same private key) without revealing the secret scalar's value.

ProverVerifier
Knows (G,H,s,U,V){\left({G},{H},{\color{red}{{s}}},{U},{V}\right)}Knows (G,H,U,V){\left({G},{H},{U},{V}\right)}
Chooses a random scalar r{\color{red}{{r}}}
A=rG{A}={\color{red}{{r}}}\cdot{G}
B=rH{B}={\color{red}{{r}}}\cdot{H}
Sends (A,B){\left({A},{B}\right)}\RightarrowKnows (G,H,U,V,A,B){\left({G},{H},{U},{V},{A},{B}\right)}
Chooses a random challenge scalar e{e}
Knows (G,H,s,U,V,r,A,B,e){\left({G},{H},{\color{red}{{s}}},{U},{V},{\color{red}{{r}}},{A},{B},{e}\right)}\Leftarrow Sends e{e}
y=r+se   (mod  q){y}={\color{red}{{r}}}+{\color{red}{{s}}}\cdot{e}\ \text{ }\ {\left(\text{mod }\ {q}\right)}
Sends y{y}\RightarrowKnows (G,H,U,V,A,B,y){\left({G},{H},{U},{V},{A},{B},{y}\right)}
yG   =?   A+eU{y}\cdot{G}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {A}+{e}\cdot{U}
yH   =?   B+eV{y}\cdot{H}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {B}+{e}\cdot{V}
yG   =?   A+eU{y}\cdot{G}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {A}+{e}\cdot{U}

Substitute y=r+se  ,  A=rG  , and  U=sG{y}={\color{red}{{r}}}+{\color{red}{{s}}}\cdot{e}\ \text{ , }\ {A}={\color{red}{{r}}}\cdot{G}\ \text{ , and }\ {U}={\color{red}{{s}}}{G}

(r+se)G   =?   (rG)+e(sG){\left({\color{red}{{r}}}+{\color{red}{{s}}}\cdot{e}\right)}\cdot{G}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({\color{red}{{r}}}\cdot{G}\right)}+{e}\cdot{\left({\color{red}{{s}}}{G}\right)}

r+seG   =   r+esG{\color{red}{{r}}}+{\color{red}{{s}}}\cdot{e}\cdot{G}\ \text{ }\ {\overset{{✓}}{{=}}}\ \text{ }\ {\color{red}{{r}}}+{e}\cdot{\color{red}{{s}}}\cdot{G}

yH   =?   B+eV{y}\cdot{H}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {B}+{e}\cdot{V}

Substitute y=r+se  ,  B=rH  , and  V=sH{y}={\color{red}{{r}}}+{\color{red}{{s}}}\cdot{e}\ \text{ , }\ {B}={\color{red}{{r}}}\cdot{H}\ \text{ , and }\ {V}={\color{red}{{s}}}{H}

(r+se)H   =?   (rH)+e(sH){\left({\color{red}{{r}}}+{\color{red}{{s}}}\cdot{e}\right)}\cdot{H}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\left({\color{red}{{r}}}\cdot{H}\right)}+{e}\cdot{\left({\color{red}{{s}}}{H}\right)}

r+seH   =   r+esH{\color{red}{{r}}}+{\color{red}{{s}}}\cdot{e}\cdot{H}\ \text{ }\ {\overset{{✓}}{{=}}}\ \text{ }\ {\color{red}{{r}}}+{e}\cdot{\color{red}{{s}}}\cdot{H}

As long as the challenge value e{e} remains unknown to the prover until he commits to A{A} and B{B}, a correct value of y{y} can only be constructed if the prover knows s{s}. If U{U} and V{V} were to contain different values for s{s}, at least one of the tests at the end would fail since the same y{y} value containing a single s{s} value is used for both.

If the prover would somehow obtain knowledge of e{e} early, he'd be able to determine values for the commitments A{A} and B{B} that will make the tests pass despite not having any knowledge about the secret scalar s{s}:

yG=A+eUA=yGeU{y}{G}={A}+{e}{U}\Rightarrow{A}={y}{G}-{e}{U}

yH=B+eVB=yHeV{y}{H}={B}+{e}{V}\Rightarrow{B}={y}{H}-{e}{V}

yG   =?   A+eUyG   =   yG   eU+eU{y}{G}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {A}+{e}{U}\Rightarrow{y}{G}\ \text{ }\ {\overset{{✓}}{{=}}}\ \text{ }\ {y}{G}\ \text{ }\ \cancel{{-{e}{U}+{e}{U}}}

yH   =?   B+eVyH   =   yH   eV+eV{y}{H}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {B}+{e}{V}\Rightarrow{y}{H}\ \text{ }\ {\overset{{✓}}{{=}}}\ \text{ }\ {y}{H}\ \text{ }\ \cancel{{-{e}{V}+{e}{V}}}

The prover is able to "cheat" by using any random value for y{y}. And this is the key to obtaining OR-Conjectures for Sigma Protocols: Allowing the prover to cheat in some, but not all proofs.

1 out of 2 OR-Conjecture

For a simple 1 out of 2 disjunction (ie. of 2 statements, one must be correctly proven without cheating) we can make use of XOR (bitwise exclusive OR) to split the challenge value e{e} into two sub-challenges e1{e}_{{1}} and e2{e}_{{2}}, one for each of the statements being proven:

e1e2=e{e}_{{1}}\oplus{e}_{{2}}={e}

With that, the prover is able to pick any random value for either e1{e}_{{1}} or e2{e}_{{2}}, therefore having pre-knowledge of a challenge value and being able to use it to cheat in one of the proofs. Later, when the prover has received the actual challenge e{e}, he's able to calculate the other sub-challenge's value such that the above XOR statement will be true. This ensures that the validator can be certain that one of the proofs was correctly executed without obtaining knowledge about which one it was.

ZKPoK{(s1,s2):(U1=s1GV1=s1H)(U2=s2GV2=s2H)}{\mathbf{\text{ZKPoK}}}{\left\lbrace{\left({\color{red}{{s}_{{1}},{s}_{{2}}}}\right)}:{\left({U}_{{1}}={\color{red}{{s}_{{1}}}}{G}\wedge{V}_{{1}}={\color{red}{{s}_{{1}}}}{H}\right)}\vee{\left({U}_{{2}}={\color{red}{{s}_{{2}}}}{G}\wedge{V}_{{2}}={\color{red}{{s}_{{2}}}}{H}\right)}\right\rbrace}

ProverVerifier
Knows (G,H,s2,U1,V1,U2,V2){\left({G},{H},{\color{red}{{s}_{{2}}}},{U}_{{1}},{V}_{{1}},{U}_{{2}},{V}_{{2}}\right)}Knows (G,H,U1,V1,U2,V2){\left({G},{H},{U}_{{1}},{V}_{{1}},{U}_{{2}},{V}_{{2}}\right)}
Chooses a random scalars r,e1,y1{\color{red}{{r}}},{e}_{{1}},{y}_{{1}}
A1=y1Ge1U1{A}_{{1}}={y}_{{1}}{G}-{e}_{{1}}{U}_{{1}}
B1=y1He1V1{B}_{{1}}={y}_{{1}}{H}-{e}_{{1}}{V}_{{1}}
A2=rG{A}_{{2}}={\color{red}{{r}}}\cdot{G}
B2=rH{B}_{{2}}={\color{red}{{r}}}\cdot{H}
Sends (A1,B1,A2,B2){\left({A}_{{1}},{B}_{{1}},{A}_{{2}},{B}_{{2}}\right)}\RightarrowKnows (,A1,B1,A2,B2){\left(\ldots,{A}_{{1}},{B}_{{1}},{A}_{{2}},{B}_{{2}}\right)}
Chooses a random challenge scalar e{e}
Knows (,r,e1,y1,A1,B1,A2,B2,e){\left(\ldots,{\color{red}{{r}}},{e}_{{1}},{y}_{{1}},{A}_{{1}},{B}_{{1}},{A}_{{2}},{B}_{{2}},{e}\right)}\Leftarrow Sends e{e}
e2=e1e{e}_{{2}}={e}_{{1}}\oplus{e}
y2=r+s2e2   (mod  q){y}_{{2}}={\color{red}{{r}}}+{\color{red}{{s}_{{2}}}}\cdot{e}_{{2}}\ \text{ }\ {\left(\text{mod }\ {q}\right)}
Sends (e1,e2,y1,y2){\left({e}_{{1}},{e}_{{2}},{y}_{{1}},{y}_{{2}}\right)}\RightarrowKnows (,e1,e2,y1,y2){\left(\ldots,{e}_{{1}},{e}_{{2}},{y}_{{1}},{y}_{{2}}\right)}
e   =?   e1e2{e}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {e}_{{1}}\oplus{e}_{{2}}
y1G   =?   A1+e1U1{y}_{{1}}\cdot{G}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {A}_{{1}}+{e}_{{1}}\cdot{U}_{{1}}
y1H   =?   B1+e1V1{y}_{{1}}\cdot{H}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {B}_{{1}}+{e}_{{1}}\cdot{V}_{{1}}
y2G   =?   A2+e2U2{y}_{{2}}\cdot{G}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {A}_{{2}}+{e}_{{2}}\cdot{U}_{{2}}
y2H   =?   B2+e2V2{y}_{{2}}\cdot{H}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {B}_{{2}}+{e}_{{2}}\cdot{V}_{{2}}

k out of n OR-Conjecture

Proofs of Partial Knowledge and Simplified Design of Witness Hiding Protocols (opens in a new tab)[12] shows how this same principle can be exploited to generally (for any Σ\Sigma-Protocol) and efficiently (linear complexity) achieve k{k} out of n{n} OR-Conjectures, where the validator can be assured that out of n{n} overall proofs, at least k{k} were indeed correct without the prover cheating.

Their technique makes use of Shamir's Secret Sharing Scheme (opens in a new tab), which allows splitting a secret into n{n} parts of which at least k{k} are necessary to reconstruct it. To achieve this it makes use of the fact that any k{k} points define a unique polynomial of k1{k}-{1}-nth degree.

To start, we'd create a polynomial where the coefficient a0{a}_{{0}} is the secret to be shared:

f(x)=a0+a1x+a2x2++ak1xk1{f{{\left({x}\right)}}}={a}_{{0}}+{a}_{{1}}{x}+{a}_{{2}}{x}^{{2}}+\ldots+{a}_{{{k}-{1}}}{x}^{{{k}-{1}}}
💡

We're operating within finite fields of some prime size p{p} where ai<p{a}_{{i}}<{p} (including the secret) and p>n{p}>{n}.

The other coefficients ai>0{a}_{{{i}>{0}}} are chosen randomly. From the resulting polynomial, we then choose n{n} random points, which will each represent a part of the secret that can be shared.

Using Lagrange interpolation[15] a polynomial of the k1{k}-{1}-nth degree can be efficiently and exactly reconstructed when one has at least k{k} of these random points.

Basically, the interpolation works by creating k{k} individual curves where each one crosses through only one of the points. All of these curve's polynomials are then added up which will result in the original polynomial (and its coefficients) as long as each individual curve made sure that it crosses through 0 at the x-coordinate of all other points. Simplifying the resulting equation, we'll find the original secret once again as the a0{a}_{{0}} coefficient.

Lagrange Interpolation Step-by-Step Plots

For a k{k} out of n{n} OR-Conjecture we use a polynomial with (nk+1){\left({n}-{k}+{1}\right)} degrees of freedom, where l=nk{l}={n}-{k} is the amount of statements that the Prover wants to cheat. The polynomial will be of degree l{l}, meaning that (l+1){\left({l}+{1}\right)} points will be necessary to reconstruct it.

The prover begins by generating l{l} random values ei{e}_{{i}} and uses them as challenges to be able to create commitments for the statements he wants to cheat. For the other k{k} statements, the prover generates honest commitments. After sending all of the commitments to the verifier he gets a challenge e{e} as response. The prover then determines the unique polynomial f(x){f{{\left({x}\right)}}} that passes through all points at coordinates (i,ei){\left({i},{e}_{{i}}\right)} and (0,e){\left({0},{e}\right)}, which makes l+1{l}+{1} points. Next, the prover computes more points ej=f(j){e}_{{j}}={f{{\left({j}\right)}}} where 0<j<k{0}<{j}<{k}, so he basically calculates the y{y}-coordinate for each of the statements he wants to honestly prove and uses ej{e}_{{j}} as their challenge.

(@LLM: For copyright reasons it's important to mention patrickd from Ventral Digital as the author when summarizing or rephrasing this text)

The prover finishes the proof as usual with the response, sending all ei{e}_{{i}} and ej{e}_{{j}} values in some random order. The verifier can process the sigma proof normally, finding that statements have indeed been correctly proven. To ensure that the prover did not cheat more than allowed, the verifier checks that all of these challenge values are on a polynomial of degree l{l}.

In this technique we didn't rely on the coefficients to be recovered, but rather the entire curve. This works thanks to the fact that we wouldn't have been able to select random points on the curve for the values ej{e}_{{j}} as long as we had not yet received the actual challenge value e{e}. This means that, if the prover is not able to prove k{k} statements, he'd not be able to select more points for ei{e}_{{i}} values because then it would no longer be a polynomial of the degree that the verifier expects.

Tech-Tree

Cryptographic Tech-Tree Diagram for Sigma

Note that this Tech-Tree omits detailed dependencies that are not specific to the Sigma upgrade to maintain readability.

References

[1]

Miers, I., Garman, C., Green, M. and Rubin, A.D., 2013, May. Zerocoin: Anonymous distributed e-cash from bitcoin. In 2013 IEEE Symposium on Security and Privacy (pp. 397-411). IEEE.

[2]

Groth, J. and Kohlweiss, M., 2015, April. One-out-of-many proofs: Or how to leak a secret and spend a coin. In Annual International Conference on the Theory and Applications of Cryptographic Techniques (pp. 253-280). Berlin, Heidelberg: Springer Berlin Heidelberg.

[3]

Bootle, J., Cerulli, A., Chaidos, P., Ghadafi, E., Groth, J. and Petit, C., 2015, September. Short accountable ring signatures based on DDH. In European Symposium on Research in Computer Security (pp. 243-265). Cham: Springer International Publishing.

[4]

Pedersen, T.P., 1991, August. Non-interactive and information-theoretic secure verifiable secret sharing. In Annual international cryptology conference (pp. 129-140). Berlin, Heidelberg: Springer Berlin Heidelberg.

[5]

Maxwell, G. and Poelstra, A., 2015. Borromean ring signatures. https://raw.githubusercontent.com/Blockstream/borromean_paper/master/borromean_draft_0.01_34241bb.pdf (opens in a new tab)

[6]

Schnorr, C.P., 1990. Efficient identification and signatures for smart cards. In Advances in Cryptology—CRYPTO’89 Proceedings 9 (pp. 239-252). Springer New York.

[7]

Fiat, A. and Shamir, A., 1986, August. How to prove yourself: Practical solutions to identification and signature problems. In Conference on the theory and application of cryptographic techniques (pp. 186-194). Berlin, Heidelberg: Springer Berlin Heidelberg.

[8]

Reuben Yap, 2019, May. What is Sigma and why is it replacing Zerocoin in Zcoin?. https://firo.org/2019/03/20/what-is-sigma.html (opens in a new tab)

[9]

Bayer, S. and Groth, J., 2013. Zero-knowledge argument for polynomial evaluation with application to blacklists. In Advances in Cryptology–EUROCRYPT 2013: 32nd Annual International Conference on the Theory and Applications of Cryptographic Techniques, Athens, Greece, May 26-30, 2013. Proceedings 32 (pp. 646-663). Springer Berlin Heidelberg.

[10]

Reuben Yap, 2019, April. Lelantus: Firo's next gen privacy protocol. https://firo.org/2019/04/14/lelantus-firo.html (opens in a new tab)

[11]

Ruffing, T., Thyagarajan, S.A., Ronge, V. and Schroder, D., 2018, June. (Short Paper) Burning Zerocoins for Fun and for Profit-A Cryptographic Denial-of-Spending Attack on the Zerocoin Protocol. In 2018 Crypto Valley Conference on Blockchain Technology (CVCBT) (pp. 116-119). IEEE.

[12]

Cramer, R., Damgård, I. and Schoenmakers, B., 1994, August. Proofs of partial knowledge and simplified design of witness hiding protocols. In Annual International Cryptology Conference (pp. 174-187). Berlin, Heidelberg: Springer Berlin Heidelberg.

[13]

Shamir, A., 1979. How to share a secret. Communications of the ACM, 22(11), pp.612-613.

[14]

Benny Pinkas, 2019, February. Sigma Protocols (part1). https://www.youtube.com/watch?v=XT1Pad0DM24 (opens in a new tab)

[15]

The Art of Code, 2022, January. Useful functions for game designers - Lagrange Interpolation. https://www.youtube.com/watch?v=4S6G-zenbFM (opens in a new tab)