Cryptocurrency Privacy Technologies: Lelantus Protocol

25. March, 2024 by patrickd

Meme: Confidential Transactions an the Zerocoin Scheme being fused resulting in Lelantus

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.

Previously we discussed how Zcoin's Sigma Protocol upgrade re-implemented the Zerocoin Scheme with One-Out-Of-Many Proofs. This resulted in an efficient and trustless cryptocurrency that enhances privacy by breaking the transaction graph. Its biggest drawback however was that it did not allow hiding the amounts being transacted. On the other hand, we had Confidential Transactions that became efficiently viable thanks to range proofs implemented with Bulletproofs. While the Confidential Transactions scheme hides coin amounts, its transactions remain traceable. Fusing both of these technologies, we obtain the Lelantus Protocol that achieves both confidentiality and untraceability.

The Concept

Understanding the core concept behind the Lelantus Protocol only requires some basic knowledge on Bitcoin transactions and Elliptic Curve Cryptography.

Zerocoin Scheme in Review

At a high level, the Zerocoin Scheme basically adds a native mixer to a standard transparent ledger blockchain: A fixed denomination of public coins is "burned" in exchange for "minting" a private coin. Or more accurately, some fixed amount of public coins are locked into a pool together with all other coins of the same denomination and for each time a user does this they obtain a secret voucher that they can later redeem in order to re-obtain that same amount of public coins. When the voucher is revealed for redemption, the fact that there's no way to tell which of the public coins were originally burned in exchange for the voucher is how this scheme achieves untraceability.

High Level Zerocoin Scheme

The Sigma Protocol implements this scheme by using Pedersen Commitments for vouchers, where s{s} is a serial number and r{r} is a random blinding factor.

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

When a user mints a voucher, they publish such a commitment C{C} as part of the transaction's output and burn the appropriate amount of public coin from some UTXO. Later, the user can redeem those vouchers by generating a Zero-Knowledge One-Out-Of-Many Proof that demonstrates that they have the knowledge necessary (s{s}, r{r}) to open a commitment within the set of all minted vouchers. The verifier requires the serial number s{s} to be revealed as part of this process to prevent users from redeeming the same voucher multiple times. The blinding factor r{r} though is kept secret and without it, it's impossible to determine the commitment C{C} that belongs to the revealed serial number.

Confidential Transactions in Review

The Confidential Transactions scheme works by hiding transaction amounts within homomorphic commitments. Homomorphism allows the verifier to sum the hidden amounts of both transaction inputs and outputs and compare these sums to ensure that no new coins are being created from nothing. These commitments V{V} are Pedersen as well, with the difference that they're hiding the public coin amounts v{v} instead of a serial number s{s}.

High Level Confidential Transactions Scheme

Unfortunately, checking vinputs   =?   voutputs\sum{v}_{{\text{inputs}}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ \sum{v}_{{\text{outputs}}} on its own is not sufficient since outputs could contain "negative amounts" that would once again allow the creation of new coins. To prevent this, the verifier requires each output to have a range proof (opens in a new tab) demonstrating that all values voutputs{v}_{{{o}{u}{t}{p}{u}{t}{s}}} are "positive".

Lelantus' Core Idea

As shown, the cryptographic primitive that both the Zerocoin Scheme and Confidential Transactions have in common are Pedersen Commitments:

C=sG+rH{C}={s}{G}+{r}{H} V=vG+rH{V}={v}{G}+{r}{H}

Lelantus combines both of these schemes through this commonality, resulting in a protocol that inherits both of their the privacy-enhancing aspects.

C=sG1+vG2+rH{C}={s}{G}_{{1}}+{v}{G}_{{2}}+{r}{H}

Coin commitment C{C} now hides both the coin's serial number s{s} and the coin amount v{v} it is carrying. To maintain the binding properties of this generalized Pedersen commitment (or "double-blinded" commitment), we assume that the logarithmic relationship between all generator points (G1,G2,H{G}_{{1}},{G}_{{2}},{H}) is unknown.

Minting

For a coin commitment to be of value, it has to be "minted" as done in the Zerocoin scheme. To mint, the transaction signer chooses the public coin UTXOs they want to "burn" in exchange. The sum of the input amounts becomes the committed value v{v} which is blinded by both the serial s{s} and the blinding factor r{r}. The resulting coin commitment C{C} is published by the signer as part of the transaction outputs. Before adding the commitment to the set of valid vouchers, the validators have to ensure that the committed value v{v} within C{C} is actually equal to vinputs\sum{v}_{{\text{inputs}}}\text{}.

Lelantus Minting Diagram

Despite knowledge of what v{v} should be, the verifier can only open the commitment C{C} with additional knowledge of s{s} and r{r}. However the serial number should only be revealed as part of the redemption process, and the random blinding factor must never be revealed as this would introduce traceability by reconstructing the commitment during redemption.

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

Instead of revealing information to allow the verifier to open the commitment and check the committed coin amount, a generalized Schnorr Proof is used to convince the verifier of the signer's knowledge of s{s} and r{r} such that the commitment opens for the correct v{v}.

SplitJoin

The mint operation is purposefully kept simple with a single private output, which is sufficient to move arbitrary amounts of coins into the "mixer". But if the spending logic would be equally simple (ie. redeeming only a single voucher per transaction), the protocol's privacy would actually be worse than the Zerocoin scheme on its own. That is because before we basically had multiple anonymity sets, one for each fixed denomination. But if redemptions reveal the exact same amount of coins used during minting, each specific public coin amount would become its own anonymity set - and it's unlikely that an amount such as 1.50284{1.50284} would be minted by multiple people.

This is why, instead of a simple spend operation, we have "SplitJoin" that allows merging multiple coin commitments in a transaction's input and splitting them apart into new commitments as output. Most interestingly, it lets us execute partial spends: If you had minted a single coin commitment with a large sum, you'll be able to anonymously redeem that commitment in exchange for a new commitment and a public coin output that contains a partial amount of the original sum.

Diagram of Lelantus' spend (SplitJoin)

Implementing the JoinSplit operation comes with several challenges:

  • With the coin commitments now being double-blinded, changes to the One-Out-Of-Many Proof protocol are necessary to demonstrate the ability to open a commitment within the anonymity set with the specified serial number.
  • Similar to how it was part of the Confidential Transactions scheme, it's necessary to ensure that the sum of input coin amounts is equal to the sum of output amounts. But since we can't reveal the input commitments without losing untraceability, this needs to be done as part of the OOOMP somehow.
  • When a transaction has more than a single output (which is always the case when considering fees as a transparent output), each of the output coin commitments must come with a range proof to prohibit commitments with negative coin amounts being used to create new value out of thin air.

The Math

Generalized Schnorr Proof

We previously discussed the construction of simple Schnorr Proofs in the introduction of the Zerocoin scheme. In short, it allowed us to prove knowledge of a secret witness s{s} belonging to a public key V{V} such that V=sG{V}={s}{G}.

A generalized Schnorr Proof allows the prover to demonstrate knowledge of multiple such witnesses sn{s}_{{n}} belonging to a single commitment C{C} without revealing them.

C=s1G1+s2G2++snGn{C}={s}_{{1}}{G}_{{1}}+{s}_{{2}}{G}_{{2}}+\ldots+{s}_{{n}}{G}_{{n}}

With all Gn{G}_{{n}} being logarithmically independent generators, the following protocol is executed between Prover and Verifier:

ProverVerifier
Knows (G1,G2,,Gn,C,s1,s2,,sn){\left({G}_{{1}},{G}_{{2}},\ldots,{G}_{{n}},{C},{\color{red}{{s}_{{1}},{s}_{{2}},\ldots,{s}_{{n}}}}\right)}Knows (G1,G2,,Gn,C){\left({G}_{{1}},{G}_{{2}},\ldots,{G}_{{n}},{C}\right)}
Chooses random scalars (r1,r2,,r3){\left({\color{red}{{r}_{{1}},{r}_{{2}},\ldots,{r}_{{3}}}}\right)}
R=r1G1+r2G2++rnGn{R}={\color{red}{{r}_{{1}}}}{G}_{{1}}+{\color{red}{{r}_{{2}}}}{G}_{{2}}+\ldots+{\color{red}{{r}_{{n}}}}{G}_{{n}}
Sends R{R}\RightarrowKnows (G1,G2,,Gn,C,R){\left({G}_{{1}},{G}_{{2}},\ldots,{G}_{{n}},{C},{R}\right)}
Chooses random challenge x{x}
Knows (,r1,r2,,r3,R,x){\left(\ldots,{\color{red}{{r}_{{1}},{r}_{{2}},\ldots,{r}_{{3}}}},{R},{x}\right)}\Leftarrow Sends x{x}
Computes
t1=r1xs1{t}_{{1}}={\color{red}{{r}_{{1}}}}-{x}{\color{red}{{s}_{{1}}}}
t2=r2xs2{t}_{{2}}={\color{red}{{r}_{{2}}}}-{x}{\color{red}{{s}_{{2}}}}
\vdots
tn=rnxsn{t}_{{n}}={\color{red}{{r}_{{n}}}}-{x}{\color{red}{{s}_{{n}}}}
Sends (t1,t2,,tn){\left({t}_{{1}},{t}_{{2}},\ldots,{t}_{{n}}\right)}\RightarrowKnows (,t1,t2,,tn){\left(\ldots,{t}_{{1}},{t}_{{2}},\ldots,{t}_{{n}}\right)}
R   =?   xC+t1G1+t2G2++tnGn{R}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {x}{C}+{t}_{{1}}{G}_{{1}}+{t}_{{2}}{G}_{{2}}+\ldots+{t}_{{n}}{G}_{{n}}
R   =?   xC+t1G1+t2G2++tnGn{R}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {x}{C}+{t}_{{1}}{G}_{{1}}+{t}_{{2}}{G}_{{2}}+\ldots+{t}_{{n}}{G}_{{n}}

R   =?   x(s1G1+s2G2++snGn)C+(r1xs1)t1G1+(r2xs2)t2G2++(rnxsn)tnGn{R}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {x}\overbrace{{{\left({\color{red}{{s}_{{1}}}}{G}_{{1}}+{\color{red}{{s}_{{2}}}}{G}_{{2}}+\ldots+{\color{red}{{s}_{{n}}}}{G}_{{n}}\right)}}}^{{{C}}}+\overbrace{{{\left({\color{red}{{r}_{{1}}}}-{x}{\color{red}{{s}_{{1}}}}\right)}}}^{{{t}_{{1}}}}{G}_{{1}}+\overbrace{{{\left({\color{red}{{r}_{{2}}}}-{x}{\color{red}{{s}_{{2}}}}\right)}}}^{{{t}_{{2}}}}{G}_{{2}}+\ldots+\overbrace{{{\left({\color{red}{{r}_{{n}}}}-{x}{\color{red}{{s}_{{n}}}}\right)}}}^{{{t}_{{n}}}}{G}_{{n}}

R   =?   xs1G1+xs2G2++xsnGn+r1G1xs1G1+r2G2xs2G2++rnGnxsnGn{R}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {x}{\color{red}{{s}_{{1}}}}{G}_{{1}}+{x}{\color{red}{{s}_{{2}}}}{G}_{{2}}+\ldots+{x}{\color{red}{{s}_{{n}}}}{G}_{{n}}+{\color{red}{{r}_{{1}}}}{G}_{{1}}-{x}{\color{red}{{s}_{{1}}}}{G}_{{1}}+{\color{red}{{r}_{{2}}}}{G}_{{2}}-{x}{\color{red}{{s}_{{2}}}}{G}_{{2}}+\ldots+{\color{red}{{r}_{{n}}}}{G}_{{n}}-{x}{\color{red}{{s}_{{n}}}}{G}_{{n}}

R   =?   xs1G1+xs2G2++xsnGn+r1G1xs1G1+r2G2xs2G2++rnGnxsnGn{R}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ \cancel{{{x}{\color{red}{{s}_{{1}}}}{G}_{{1}}}}+\cancel{{{x}{\color{red}{{s}_{{2}}}}{G}_{{2}}}}+\ldots+\cancel{{{x}{\color{red}{{s}_{{n}}}}{G}_{{n}}}}+{\color{red}{{r}_{{1}}}}{G}_{{1}}\cancel{{-{x}{\color{red}{{s}_{{1}}}}{G}_{{1}}}}+{\color{red}{{r}_{{2}}}}{G}_{{2}}\cancel{{-{x}{\color{red}{{s}_{{2}}}}{G}_{{2}}}}+\ldots+{\color{red}{{r}_{{n}}}}{G}_{{n}}\cancel{{-{x}{\color{red}{{s}_{{n}}}}{G}_{{n}}}}

R   =   r1G1+r2G2++rnGn{R}\ \text{ }\ {\overset{{✓}}{{=}}}\ \text{ }\ {\color{red}{{r}_{{1}}}}{G}_{{1}}+{\color{red}{{r}_{{2}}}}{G}_{{2}}+\ldots+{\color{red}{{r}_{{n}}}}{G}_{{n}}

During the Lelantus minting process, the verifier already has knowledge of the coin amount v{v} from the transparent UTXO inputs and is therefore able to compute vG2{v}{G}_{{2}}. After subtracting this from the coin commitment specified as the transaction output (C=CvG2{C}'={C}-{v}{G}_{{2}}), the above Schnorr protocol demonstrates that the signer is able to open C=sG1+rH{C}'={s}{G}_{{1}}+{r}{H} without revealing its secret scalars. If this succeeds, the verifier can be sure that the original C{C} committed to the expected amount v{v} since C=C+vG2{C}={C}'+{v}{G}_{{2}}.

OOOMP in Review

To briefly recap how One-Out-Of-Many Proofs originally worked, we assume that there are N{N} commitments Ci{C}_{{i}} each representing a minted private coin. We say that a Prover has knowledge of serial number s{s} and a blinding factor r{r} by which one of these commitments at index l{l} can be opened, proving ownership and therefore the ability to spend the private coin.

Cl=sG+rH{C}_{{l}}={s}{G}+{r}{H}

To exchange the private coin for its denomination of locked public coins, the Prover reveals the serial number s{s} which the Verifier will remember to prevent the Prover from spending the same commitment more than once. The serial number is "homomorphically subtracted" from all commitments Ci{C}_{{i}} resulting in some other Ci{C}'_{{i}} for all but the commitment at l{l} which now commits to 0:

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

The proof now demonstrates that the Prover is able to open one of the commitments Ci{C}'_{{i}} with his knowledge of r{r} without revealing that it was Cl{C}'_{{l}}. To do so, it begins with the execution of a 3-move-type proof committing to a valid index l{l} in zero-knowledge, during which it responds to the challenge x{x} with fj{f}_{{j}}:

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

Here, the value of l{l} is represented by n{n} bits lj{0,1}{l}_{{j}}\in{\left\lbrace{0},{1}\right\rbrace}, while aj{a}_{{j}} is a random value committed to by the Prover in the first move of the protocol. This is generalized, such that it can be applied to all indices i{i} represented by bits ij{i}_{{j}}:

fj,ij={fj=δlj1x+ajif ij=1,xfj=δlj0xajif ij=0.f_{j,i_{j}} = \begin{cases} f_{j} &=& \delta_{l_{j}1}x+a_{j} &\text{if } i_{j} = 1, \\ x-f_{j} &=& \delta_{l_{j}0}x-a_{j} &\text{if } i_{j} = 0. \end{cases}

Creating the product j=1nfj,ij{\prod_{{{j}={1}}}^{{n}}}{f}_{{{j},{i}_{{j}}}} for any index i{i} results in a polynomial (δl1i1x±a1)(δl2i2x±a2)(δlninx±an){\left(\delta_{{{l}_{{1}}{i}_{{1}}}}{x}\pm{a}_{{1}}\right)}\cdot{\left(\delta_{{{l}_{{2}}{i}_{{2}}}}{x}\pm{a}_{{2}}\right)}\cdot\ldots\cdot{\left(\delta_{{{l}_{{n}}{i}_{{n}}}}{x}\pm{a}_{{n}}\right)} that can be expanded to the standard form:

δlixn+pi,n1xn1++pi,1x1+pi,0x0\delta_{{{l}{i}}}{x}^{{n}}+{p}_{{{i},{n}-{1}}}{x}^{{{n}-{1}}}+\ldots+{p}_{{{i},{1}}}{x}^{{{1}}}+{p}_{{{i},{0}}}{x}^{{{0}}}

The Kronecker Delta δljij\delta_{l_{j}i_{j}} will only be 1{1} for when the bits of both l{l} and i{i} at position j{j} are equal. This means that all x{x} of the product are only multiplied by 1{1} when the current index i=l{i}={l}, resulting in a polynomial of n{n}-th order. For all other indices i{i} some x{x} will be multiplied by 0{0} resulting in lower-order coefficients.

f polynomials diagram

Even without knowing x{x} yet, the Prover is able to determine the polynomial's coefficients which are based on his own randomness ±aj\pm{a}_{{j}}. The lower-order coefficients pi,k{p}_{{{i},{k}}} are used to extend the zero-knowledge protocol with additional first-move commitments:

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

D commitments diagram

The idea is that the Dk{D}_{{k}} commitments represent the polynomials fi(x){{f}_{{i}}{\left({x}\right)}} in a state where they have not been evaluated for a specific x{x} yet, with the difference that the commitments lack the highest order coefficient for xn{x}^{{n}}. On the other hand, the products based on the challenge-response values fj,ij{f}_{{{j},{i}_{{j}}}} represent the polynomials fi(x){{f}_{{i}}{\left({x}\right)}} evaluated for challenge x{x} with all their coefficients. A subtraction between these two will therefore result in all lower-order coefficients canceling out, leaving xnCl{x}^{{n}}{C}'_{{l}}.

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

f polynomial - (subtraction) - D commitments diagram

To prevent revealing the actual Cl{C}'_{{l}}, the commitments Dk{D}_{{k}} would additionally have blinding factors rk{r}'_{{k}} known only to the Prover.

Cl+rH=0G+(r+r)H  with  r=k=0n1rk{C}'_{{l}}+{r}'{H}={0}{G}+{\left({r}+{r}'\right)}{H}\ \text{ with }\ {r}'={\sum_{{{k}={0}}}^{{{n}-{1}}}}{r}'_{{k}}

With that, the Prover is able to demonstrate knowledge of blinding factors r{r} and r{r}' without revealing them, and therefore proving his ability to open the commitment Cl{C}'_{{l}}, and ultimately Cl{C}_{{l}}.

Generalized OOOMP

As can be seen above, the original One-Out-Of-Many Proof scheme assumed that the commitment, that is being proven to be part of the anonymity set, is required to commit to zero with a single blinding factor:

0G+rH{0}{G}+{r}{H}

But this does not apply in Lelantus anymore, where we basically have a commitment that is double-blinded by both the coin amount and a random factor:

0G1+vG2+rH{0}{G}_{{1}}+{v}{G}_{{2}}+{r}{H}

The Lelantus paper presents a modification to the OOOMP system that works with double-blinded Pedersen commitments:

ProverVerifier
Knows (G1,G2,H,(C0,,CN1),l,v,r){\left({G}_{{1}},{\color{blue}{{G}_{{2}}}},{H},{\left({C}'_{{0}},\ldots,{C}'_{{{N}-{1}}}\right)},{\color{red}{{l},{v},{r}}}\right)}Knows (G1,G2,H,(C0,,CN1)){\left({G}_{{1}},{\color{blue}{{G}_{{2}}}},{H},{\left({C}'_{{0}},\ldots,{C}'_{{{N}-{1}}}\right)}\right)}
Chooses random scalars r^,r~,r,r˙,(au,1,,au,m1),ρk,ρ^k,ρ˙k{\color{red}{\hat{{r}},\tilde{{r}},\overline{{r}},\dot{{r}},{\left({a}_{{{u},{1}}},\ldots,{a}_{{{u},{m}-{1}}}\right)},\rho_{{k}}}}{\color{blue}{,\hat{\rho}_{{k}},\dot{\rho}_{{k}}}}
C^=l0,0G0++ln1,m1Gmn1+r^H\hat{{C}}={\color{red}{{l}_{{{0},{0}}}}}{\mathbf{{{G}}}}_{{0}}+\ldots+{\color{red}{{l}_{{{n}-{1},{m}-{1}}}}}{\mathbf{{{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}}}}}{\mathbf{{{G}}}}_{{0}}+\ldots+{\color{red}{{a}_{{{n}-{1},{m}-{1}}}}}{\mathbf{{{G}}}}_{{{m}{n}-{1}}}+{\color{red}{\tilde{{r}}}}{H}
A=a0,02G0++an1,m12Gmn1+rH\overline{{A}}={\color{red}{-{{a}_{{{0},{0}}}^{{2}}}}}{\mathbf{{{G}}}}_{{0}}+\ldots+{\color{red}{-{{a}_{{{n}-{1},{m}-{1}}}^{{2}}}}}{\mathbf{{{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)}{\mathbf{{{G}}}}_{{0}}+\ldots+{\color{red}{{a}_{{{n}-{1},{m}-{1}}}}}\cdot{\left({1}-{2}{\color{red}{{l}_{{{n}-{1},{m}-{1}}}}}\right)}{\mathbf{{{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{blue}{-}}{\color{red}{\rho_{{k}}}}{H}
Ek=ρ^kG2+ρ˙kH+ρkH{\color{blue}{{E}_{{k}}=\hat{\rho}_{{k}}{G}_{{2}}+\dot{\rho}_{{k}}{H}+\rho_{{k}}{H}}}
Sends (A~,A,A˙,C^,Dk,Ek){\left(\tilde{{A}},\overline{{A}},\dot{{A}},\hat{{C}},{D}_{{k}}{\color{blue}{,{E}_{{k}}}}\right)}\RightarrowKnows (,A~,A,A˙,C^,Dk,Ek){\left(\ldots,\tilde{{A}},\overline{{A}},\dot{{A}},\hat{{C}},{D}_{{k}}{\color{blue}{,{E}_{{k}}}}\right)}
Chooses a random challenge scalar x{x}
Knows (,A~,A,A˙,C^,Dk,Ek,x){\left(\ldots,\tilde{{A}},\overline{{A}},\dot{{A}},\hat{{C}},{D}_{{k}}{\color{blue}{,{E}_{{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}}}}
ε=vxnk=0n1ρ^kxk{\color{blue}{\varepsilon={v}{x}^{{n}}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}\hat{\rho}_{{k}}{x}^{{k}}}}
γ=rxnk=0n1ρ˙kxk\gamma={\color{red}{{r}}}{x}^{{n}}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\color{blue}{\dot{\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{\color{blue}{,\varepsilon}},\gamma\right)}\RightarrowKnows (,x,fu,j,α,β,ε,γ){\left(\ldots,{x},{f}_{{{u},{j}}},\alpha,\beta,{\color{blue}{\varepsilon}},\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}}}{\mathbf{{{G}}}}_{{0}}+\ldots+{f}_{{{n}-{1},{m}-{1}}}{\mathbf{{{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)}{\mathbf{{{G}}}}_{{0}}+\ldots+{f}_{{{n}-{1},{m}-{1}}}\cdot{\left({x}-{f}_{{{n}-{1},{m}-{1}}}\right)}{\mathbf{{{G}}}}_{{{m}{n}-{1}}}+\beta{H}
i=0N1(u=1mfu,iu)Cik=0n1xk(Ek+Dk)   =?   εG2+γ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}}{\left({\color{blue}{{E}_{{k}}}}+{D}_{{k}}\right)}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\color{blue}{\varepsilon{G}_{{2}}}}+\gamma{H}

The above protocol for N=mn{N}={m}^{{n}} anonymity sets was taken from the previous article on the Sigma Protocol. For the purposes of this article, we'll only concentrate on the changes{\color{blue}{\text{changes}}}.

The final check originally looked like this

i=0N1(u=1mfu,iu)Ci(1)k=0n1xkDk(2)   =?   γH\underbrace{{{\sum_{{{i}={0}}}^{{{N}-{1}}}}{\left({\prod_{{{u}={1}}}^{{m}}}{f}_{{{u},{i}_{{u}}}}\right)}{C}'_{{i}}}}_{{\text{(1)}}}-\underbrace{{{\sum_{{{k}={0}}}^{{{n}-{1}}}}{x}^{{k}}{D}_{{k}}}}_{{\text{(2)}}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ \gamma{H}

with Dk=i=0N1pi,kCi+ρkH{D}_{{k}}={\sum_{{{i}={0}}}^{{{N}-{1}}}}{p}_{{{i},{k}}}{C}'_{{i}}+{\color{red}{\rho_{{k}}}}{H} and γ=rxnk=0n1ρkxk\gamma={\color{red}{{r}}}{x}^{{n}}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\color{red}{\rho_{{k}}}}{x}^{{k}}

As explained in the review, (1)\text{(1)} and (2)\text{(2)} mostly cancel each other out. Specifically, (1)\text{(1)} will leave behind the commitment Cl=0G+rH{\color{red}{{C}'_{{l}}}}={0}{G}+{\color{red}{{r}}}{H} that we were proving inclusion within the anonymity set of, while (2)\text{(2)} leaves the blinding factors ρkH{\color{red}{\rho_{{k}}}}{H}.

xnrH(1)k=0n1ρkH(2)   =   (rxnk=0n1ρkxk)γH\overbrace{{{x}^{{n}}\cdot{\color{red}{{r}}}{H}}}^{{\text{(1)}}}-\overbrace{{{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\color{red}{\rho_{{k}}}}{H}}}^{{\text{(2)}}}\ \text{ }\ {\overset{{✓}}{{=}}}\ \text{ }\ \overbrace{{{\left({\color{red}{{r}}}{x}^{{n}}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\color{red}{\rho_{{k}}}}{x}^{{k}}\right)}}}^{{\gamma}}{H}

The same principle still applies for the generalized protocol for double-blinded commitments where Cl=0G1+vG2+rH{\color{red}{{C}'_{{l}}}}={0}{G}_{{1}}+{\color{red}{{v}}}{G}_{{2}}+{\color{red}{{r}}}{H}:

i=0N1(u=1mfu,iu)Ci(1)k=0n1xk(Ek+Dk)(2)   =?   εG2+γH\underbrace{{{\sum_{{{i}={0}}}^{{{N}-{1}}}}{\left({\prod_{{{u}={1}}}^{{m}}}{f}_{{{u},{i}_{{u}}}}\right)}{C}'_{{i}}}}_{{\text{(1)}}}-\underbrace{{{\sum_{{{k}={0}}}^{{{n}-{1}}}}{x}^{{k}}{\left({\color{blue}{{E}_{{k}}}}+{D}_{{k}}\right)}}}_{{\text{(2)}}}\ \text{ }\ {\overset{{?}}{{=}}}\ \text{ }\ {\color{blue}{\varepsilon{G}_{{2}}}}+\gamma{H}

Looking at (2){\left({2}\right)} more closely, we can see that the addition of the new Ek{\color{blue}{{E}_{{k}}}} commitments results in something very similar to the old Dk{D}_{{k}}, with the difference that it will leave behind blinding factors for both G2{G}_{{2}} (for v{v}) and H{H} (for r{r}).

k=0n1xk(ρk^G2+ρk˙H+ρkHEk+i=0N1pi,kCiρkHDk){\sum_{{{k}={0}}}^{{{n}-{1}}}}{x}^{{k}}{\left(\overbrace{{\hat{{\color{red}{\rho_{{k}}}}}{G}_{{2}}+\dot{{\color{red}{\rho_{{k}}}}}{H}+\cancel{{{\color{red}{\rho_{{k}}}}{H}}}}}^{{{\color{blue}{{E}_{{k}}}}}}+\overbrace{{{\sum_{{{i}={0}}}^{{{N}-{1}}}}{p}_{{{i},{k}}}{C}'_{{i}}\cancel{{{\color{blue}{-}}{\color{red}{\rho_{{k}}}}{H}}}}}^{{{D}_{{k}}}}\right)}

Therefore, we can see that thanks to these changes the protocol indeed works for double-blinded commitments:

xn(vG2+rH)(1)k=0n1ρk^G2+ρk˙H(2)   =   (vxnk=0n1ρk^xk)εG2+(rxnk=0n1ρ˙kxk)γH\overbrace{{{x}^{{n}}\cdot{\left({\color{red}{{v}}}{G}_{{2}}+{\color{red}{{r}}}{H}\right)}}}^{{\text{(1)}}}-\overbrace{{{\sum_{{{k}={0}}}^{{{n}-{1}}}}\hat{{\color{red}{\rho_{{k}}}}}{G}_{{2}}+\dot{{\color{red}{\rho_{{k}}}}}{H}}}^{{\text{(2)}}}\ \text{ }\ {\overset{{✓}}{{=}}}\ \text{ }\ \overbrace{{{\left({\color{red}{{v}}}{x}^{{n}}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}\hat{{\color{red}{\rho_{{k}}}}}{x}^{{k}}\right)}}}^{{\varepsilon}}{G}_{{2}}+\overbrace{{{\left({\color{red}{{r}}}{x}^{{n}}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\color{red}{\dot{\rho}_{{k}}}}{x}^{{k}}\right)}}}^{{\gamma}}{H}

Balance Proof

💡

It should be noted that the formal proof of soundness for the following balance proof provided by the paper turned out to be incorrect. So far, nobody had been able to solve the issue with the proof, though nobody was able to find a way to actually exploit that either. This was also the reason why Firo went with the Spark segregated approach.

For Transparent Output

A yet unresolved problem is verifying the sum of coin amounts that each commitment being spent as transaction input is carrying - while not having the actual commitment but only an inclusion proof demonstrating that said commitment was indeed minted and added to the pool of valid vouchers.

Since the inclusion proof carries (blinded) information about the commitment value v{v}, we can make use of the OOOMP's transcript to extract a commitment Cl{C}{''}_{{l}}, which is basically Cl{C}'_{{l}} with a modified blinding factor:

εG2+γH+k=0n1xkEk{\color{blue}{\varepsilon{G}_{{2}}}}+\gamma{H}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}{x}^{{k}}{\color{blue}{{E}_{{k}}}} (vxnk=0n1ρ^kxk)εG2+(rxnk=0n1ρ˙kxk)γH+k=0n1xk(ρ^kG2+ρ˙kH+ρkH)Ek\overbrace{{{\left({\color{red}{{v}}}{x}^{{n}}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\color{red}{\hat{\rho}_{{k}}}}{x}^{{k}}\right)}}}^{{\varepsilon}}{G}_{{2}}+\overbrace{{{\left({\color{red}{{r}}}{x}^{{n}}-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\color{red}{\dot{\rho}_{{k}}}}{x}^{{k}}\right)}}}^{{\gamma}}{H}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}{x}^{{k}}\overbrace{{{\left({\color{red}{\hat{\rho}_{{k}}}}{G}_{{2}}+{\color{red}{\dot{\rho}_{{k}}}}{H}+{\color{red}{\rho_{{k}}}}{H}\right)}}}^{{{E}_{{k}}}} vxnG2k=0n1(ρ^kxkG2)+rxnHk=0n1(ρ˙kxkH)+k=0n1(xkρ^kG2+xkρ˙kH+xkρkH){\color{red}{{v}}}{x}^{{n}}{G}_{{2}}\cancel{{-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\left({\color{red}{\hat{\rho}_{{k}}}}{x}^{{k}}{G}_{{2}}\right)}}}+{\color{red}{{r}}}{x}^{{n}}{H}\cancel{{-{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\left({\color{red}{\dot{\rho}_{{k}}}}{x}^{{k}}{H}\right)}}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\left(\cancel{{{x}^{{k}}{\color{red}{\hat{\rho}_{{k}}}}{G}_{{2}}}}+\cancel{{{x}^{{k}}{\color{red}{\dot{\rho}_{{k}}}}{H}}}+{x}^{{k}}{\color{red}{\rho_{{k}}}}{H}\right)} vxnG2+rxnH+k=0n1(xkρkH){\color{red}{{v}}}{x}^{{n}}{G}_{{2}}+{\color{red}{{r}}}{x}^{{n}}{H}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}{\left({x}^{{k}}{\color{red}{\rho_{{k}}}}{H}\right)} vxnG2+(rxn+k=0n1xkρk)H{\color{red}{{v}}}{x}^{{n}}{G}_{{2}}+{\left({\color{red}{{r}}}{x}^{{n}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}{x}^{{k}}{\color{red}{\rho_{{k}}}}\right)}{H}

If we now want to verify whether the commitment being redeemed carries the expected coin amount v{v}, we can subtract vxnG2{v}{x}^{{n}}{G}_{{2}} from it and have the user prove that he is able to open the resulting commitment (rxn+k=0n1xkρk)H{\left({\color{red}{{r}}}{x}^{{n}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}{x}^{{k}}{\color{red}{\rho_{{k}}}}\right)}{H}. The way Lelantus handles this is by treating it as a public key and having the user sign the transaction with rxn+k=0n1xkρk{\color{red}{{r}}}{x}^{{n}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}{x}^{{k}}{\color{red}{\rho_{{k}}}} as the private key.

If we have multiple private coin transaction inputs, and therefore multiple OOOMPs for each, we can simply sum all of the commitments Cl{C}{''}_{{l}} and subtract the overall expected value from it to obtain the "public key":

(inputsCl)vxnG2{\left(\sum^{{\text{inputs}}}{C}{''}_{{l}}\right)}-{v}{x}^{{n}}{G}_{{2}}

Doing this requires all One-Out-Of-Many proofs to share a common challenge value x{x} (ie. executing them in parallel). Furthermore, in regards to this challenge being derived from committed values (ie. Fiat-Shamir), those values should not only include those of all inclusion proof commitments, but also all of the output coin commitments and their range proofs.

For Private Coin Output

So far we're able to prove that the sum of private inputs is equal to the sum of public outputs. But for a partial spend or split operation we need to check equality against coin commitments that were specified as transaction outputs.

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

The intuitive solution would be to simply sum the output commitments C{C} and subtract them from the sum of blinded input commitments Cl{C}{''}_{{l}} derived from the OOOMP transcript.

(inputsCl)Transaction Inputsxn(outputsC)vxnG2Transaction Outputs\overbrace{{{\left(\sum^{{\text{inputs}}}{C}{''}_{{l}}\right)}}}^{{\text{Transaction Inputs}}}-{x}^{{n}}\overbrace{{{\left(\sum^{{\text{outputs}}}{C}\right)}-{v}{x}^{{n}}{G}_{{2}}}}^{{\text{Transaction Outputs}}}

Let's look at the simplest case where we have a single coin commitment being spent as input and a single new coin commitment being minted as output:

vxnG2+(rinxn+k=0n1xkρk)Hinput:  Clxn(sG1+vG2+routH)output:  C\overbrace{{{\color{red}{{v}}}{x}^{{n}}{G}_{{2}}+{\left({\color{red}{{r}_{\text{in}}}}{x}^{{n}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}{x}^{{k}}{\color{red}{\rho_{{k}}}}\right)}{H}}}^{{\text{input: }\ {C}{''}_{{l}}}}-{x}^{{n}}\overbrace{{{\left({\color{red}{{s}}}{G}_{{1}}+{\color{red}{{v}}}{G}_{{2}}+{\color{red}{{r}_{\text{out}}}}{H}\right)}}}^{{\text{output: }\ {C}}} vxnG2+(rinxn+k=0n1xkρk)HxnsG1xnvG2xnroutH\cancel{{{\color{red}{{v}}}{x}^{{n}}{G}_{{2}}}}+{\left({\color{red}{{r}_{\text{in}}}}{x}^{{n}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}{x}^{{k}}{\color{red}{\rho_{{k}}}}\right)}{H}-{x}^{{n}}{\color{red}{{s}}}{G}_{{1}}\cancel{{-{x}^{{n}}{\color{red}{{v}}}{G}_{{2}}}}-{x}^{{n}}{\color{red}{{r}_{\text{out}}}}{H} xnsG1+(rinxn+k=0n1xkρkxnrout)H-{x}^{{n}}{\color{red}{{s}}}{G}_{{1}}+{\left({\color{red}{{r}_{\text{in}}}}{x}^{{n}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}{x}^{{k}}{\color{red}{\rho_{{k}}}}-{x}^{{n}}{\color{red}{{r}_{\text{out}}}}\right)}{H}

If the values v{v} of inputs and outputs are truly equal then they should cancel out leaving a commitment of the form aG1+bH{a}{G}_{{1}}+{b}{H}. The balance proof can therefore be a simple generalized Schnorr Proof showing that the user is able to open this commitment without the use of the G2{G}_{{2}} generator (ie. with v=0{v}={0}).

ABDK's Critical Bug

You might have been wondering why the transaction's private output coins should be committed to as part of the inclusion proof for input coins. The reason is a critical issue identified by ABDK Consulting (opens in a new tab) during an audit of the Lelantus Protocol's cryptography.

In the generalized OOOMP, commitments Ek{E}_{{k}} and Dk{D}_{{k}} are summed as part of the proof's verification. The core of the exploit stems from the fact that these commitments are simply assumed to be correctly formed, but they can be arbitrarily modified as long as their sum remains valid.

For example, we can instead commit to Ek{E}'_{{k}} and Dk{D}'_{{k}} which contain a coin amount v˙\dot{{v}}:

Ek=Ek+v˙G2{E}'_{{k}}={E}_{{k}}{\color{blue}{+\dot{{v}}{G}_{{2}}}} Dk=Dkv˙G2{D}'_{{k}}={D}_{{k}}{\color{blue}{-\dot{{v}}{G}_{{2}}}}

This is completely legal since Ek+Dk=Ek+Dk{E}'_{{k}}+{D}'_{{k}}={E}_{{k}}+{D}_{{k}} causes the inclusion proof to remain valid. The balance proof however only makes use of Ek{E}'_{{k}} where this modification can now be exploited: Based on the One-Out-Of-Many Proof's transcript the attacker constructs an output coin of the form C=sG1+(v+v˙xn)G2+rH{C}={s}{G}_{{1}}+{\left({v}{\color{blue}{+\dot{{v}}\cdot{x}^{{-{n}}}}}\right)}{G}_{{2}}+{r}{H} then the Balance Proof will look like

(vxn+v˙)G2+(rinxn+k=0n1xkρk)Hinput:  Clxn(sG1+(v+v˙xn)G2+routH)output:  C\overbrace{{{\left({\color{red}{{v}}}{x}^{{n}}{\color{blue}{+\dot{{v}}}}\right)}{G}_{{2}}+{\left({\color{red}{{r}_{\text{in}}}}{x}^{{n}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}{x}^{{k}}{\color{red}{\rho_{{k}}}}\right)}{H}}}^{{\text{input: }\ {C}{''}_{{l}}}}-{x}^{{n}}\overbrace{{{\left({\color{red}{{s}}}{G}_{{1}}+{\left({\color{red}{{v}}}{\color{blue}{+\dot{{v}}\cdot{x}^{{-{n}}}}}\right)}{G}_{{2}}+{\color{red}{{r}_{\text{out}}}}{H}\right)}}}^{{\text{output: }\ {C}}} (vxn+v˙)G2+(rinxn+k=0n1xkρk)HxnsG1xn(v+v˙xn)G2xnroutH{\left({\color{red}{{v}}}{x}^{{n}}{\color{blue}{+\dot{{v}}}}\right)}{G}_{{2}}+{\left({\color{red}{{r}_{\text{in}}}}{x}^{{n}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}{x}^{{k}}{\color{red}{\rho_{{k}}}}\right)}{H}-{x}^{{n}}{\color{red}{{s}}}{G}_{{1}}-{x}^{{n}}{\left({\color{red}{{v}}}{\color{blue}{+\dot{{v}}\cdot{x}^{{-{n}}}}}\right)}{G}_{{2}}-{x}^{{n}}{\color{red}{{r}_{\text{out}}}}{H} (vxn+v˙)G2+(rinxn+k=0n1xkρk)HxnsG1xnvG2xnv˙xnxnxn=1G2xnroutH{\left({\color{red}{{v}}}{x}^{{n}}{\color{blue}{+\dot{{v}}}}\right)}{G}_{{2}}+{\left({\color{red}{{r}_{\text{in}}}}{x}^{{n}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}{x}^{{k}}{\color{red}{\rho_{{k}}}}\right)}{H}-{x}^{{n}}{\color{red}{{s}}}{G}_{{1}}-{x}^{{n}}{\color{red}{{v}}}{G}_{{2}}-\underbrace{{{x}^{{n}}{\color{blue}{\dot{{v}}\cdot{x}^{{-{n}}}}}}}_{{{x}^{{n}}\cdot{x}^{{-{n}}}={1}}}{G}_{{2}}-{x}^{{n}}{\color{red}{{r}_{\text{out}}}}{H} (vxn+v˙G2)+(rinxn+k=0n1xkρk)HxnsG1xnvG2v˙G2xnroutH\cancel{{{\left({\color{red}{{v}}}{x}^{{n}}{\color{blue}{+\dot{{v}}}}{G}_{{2}}\right)}}}+{\left({\color{red}{{r}_{\text{in}}}}{x}^{{n}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}{x}^{{k}}{\color{red}{\rho_{{k}}}}\right)}{H}-{x}^{{n}}{\color{red}{{s}}}{G}_{{1}}\cancel{{-{x}^{{n}}{\color{red}{{v}}}{G}_{{2}}}}\cancel{{-{\color{blue}{\dot{{v}}}}{G}_{{2}}}}-{x}^{{n}}{\color{red}{{r}_{\text{out}}}}{H} xnsG1+(rinxn+k=0n1xkρkxnrout)H-{x}^{{n}}{\color{red}{{s}}}{G}_{{1}}+{\left({\color{red}{{r}_{\text{in}}}}{x}^{{n}}+{\sum_{{{k}={0}}}^{{{n}-{1}}}}{x}^{{k}}{\color{red}{\rho_{{k}}}}-{x}^{{n}}{\color{red}{{r}_{\text{out}}}}\right)}{H}

Thanks to knowledge of challenge x{x} we can compute the inverse xn{x}^{{-{n}}} for the output commitment C{C}'s construction. With that, our injected value v˙\dot{{v}} cancels out and the balance proof remains valid. This allows us to illegally obtain a new voucher worth v+v˙xn{v}{\color{blue}{+\dot{{v}}\cdot{x}^{{-{n}}}}} coins. But if we're required to commit to the output coins before we obtain x{x} we will no longer be able to exploit this.

Generalized Bulletproofs

In the article about Bulletproof Range Proofs, we saw how we can prove that a value v{v} within a Pedersen Commitment of the form vG+rH{v}{G}+{r}{H} is within a specified range [0;2n1]{\left[{0};{2}^{{\mathbf{{{n}}}}}-{1}\right]}. Deconstructing the scheme into multiple parts, we ended up with a (1) Zero-Knowledge Inner Product Proof, a (2) Recursive Inner Product Argument, and a (3) Range Constraint Circuit.

The changes{\color{blue}{\text{changes}}} required for generalizing Bulletproofs to work with double-blinded commitments only affect (1) the Zero-Knowledge Proof. For this, remember that we're trying to show that the inner product of two vectors <a,b><\vec{{\mathbf{{{a}}}}},\vec{{\mathbf{{{b}}}}}> results in a value v{v} hidden within a commitment C{C}.

C=sG1+vG2+rvH{C}={\color{blue}{{s}{G}_{{1}}+}}{v}{G}_{{2}}+{r}_{{v}}{H}

The vectors are separately committed to in

W=aG+bH+rcH{W}=\vec{{\mathbf{{{a}}}}}\vec{{\mathbf{{{G}}}}}+\vec{{\mathbf{{{b}}}}}\vec{{\mathbf{{{H}}}}}+{r}_{{c}}{H}

where G\vec{{\mathbf{{{G}}}}