mirror of
https://github.com/monero-project/monero.git
synced 2024-12-13 20:06:32 +02:00
bulletproofs: remove single value prover
It is now expressed in terms of the array prover
This commit is contained in:
parent
484155d043
commit
a281b950bf
@ -313,17 +313,6 @@ static rct::keyV vector_dup(const rct::key &x, size_t N)
|
||||
return rct::keyV(N, x);
|
||||
}
|
||||
|
||||
/* Get the sum of a vector's elements */
|
||||
static rct::key vector_sum(const rct::keyV &a)
|
||||
{
|
||||
rct::key res = rct::zero();
|
||||
for (size_t i = 0; i < a.size(); ++i)
|
||||
{
|
||||
sc_add(res.bytes, res.bytes, a[i].bytes);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static rct::key switch_endianness(rct::key k)
|
||||
{
|
||||
std::reverse(k.bytes, k.bytes + sizeof(k));
|
||||
@ -414,281 +403,12 @@ static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, con
|
||||
/* Given a value v (0..2^N-1) and a mask gamma, construct a range proof */
|
||||
Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma)
|
||||
{
|
||||
init_exponents();
|
||||
|
||||
PERF_TIMER_UNIT(PROVE, 1000000);
|
||||
|
||||
constexpr size_t logN = 6; // log2(64)
|
||||
constexpr size_t N = 1<<logN;
|
||||
|
||||
rct::key V;
|
||||
rct::keyV aL(N), aR(N);
|
||||
rct::keyV aL8(N), aR8(N);
|
||||
rct::key tmp, tmp2;
|
||||
|
||||
PERF_TIMER_START_BP(PROVE_v);
|
||||
rct::key gamma8, sv8;
|
||||
sc_mul(gamma8.bytes, gamma.bytes, INV_EIGHT.bytes);
|
||||
sc_mul(sv8.bytes, sv.bytes, INV_EIGHT.bytes);
|
||||
rct::addKeys2(V, gamma8, sv8, rct::H);
|
||||
PERF_TIMER_STOP(PROVE_v);
|
||||
|
||||
PERF_TIMER_START_BP(PROVE_aLaR);
|
||||
for (size_t i = N; i-- > 0; )
|
||||
{
|
||||
if (sv[i/8] & (((uint64_t)1)<<(i%8)))
|
||||
{
|
||||
aL[i] = rct::identity();
|
||||
aL8[i] = INV_EIGHT;
|
||||
aR[i] = aR8[i] = rct::zero();
|
||||
}
|
||||
else
|
||||
{
|
||||
aL[i] = aL8[i] = rct::zero();
|
||||
aR[i] = MINUS_ONE;
|
||||
aR8[i] = MINUS_INV_EIGHT;
|
||||
}
|
||||
}
|
||||
PERF_TIMER_STOP(PROVE_aLaR);
|
||||
|
||||
rct::key hash_cache = rct::hash_to_scalar(V);
|
||||
|
||||
// DEBUG: Test to ensure this recovers the value
|
||||
#ifdef DEBUG_BP
|
||||
uint64_t test_aL = 0, test_aR = 0;
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
{
|
||||
if (aL[i] == rct::identity())
|
||||
test_aL += ((uint64_t)1)<<i;
|
||||
if (aR[i] == rct::zero())
|
||||
test_aR += ((uint64_t)1)<<i;
|
||||
}
|
||||
uint64_t v_test = 0;
|
||||
for (int n = 0; n < 8; ++n) v_test |= (((uint64_t)sv[n]) << (8*n));
|
||||
CHECK_AND_ASSERT_THROW_MES(test_aL == v_test, "test_aL failed");
|
||||
CHECK_AND_ASSERT_THROW_MES(test_aR == v_test, "test_aR failed");
|
||||
#endif
|
||||
|
||||
try_again:
|
||||
PERF_TIMER_START_BP(PROVE_step1);
|
||||
// PAPER LINES 38-39
|
||||
rct::key alpha = rct::skGen();
|
||||
rct::key ve = vector_exponent(aL8, aR8);
|
||||
rct::key A;
|
||||
sc_mul(tmp.bytes, alpha.bytes, INV_EIGHT.bytes);
|
||||
rct::addKeys(A, ve, rct::scalarmultBase(tmp));
|
||||
|
||||
// PAPER LINES 40-42
|
||||
rct::keyV sL = rct::skvGen(N), sR = rct::skvGen(N);
|
||||
rct::key rho = rct::skGen();
|
||||
ve = vector_exponent(sL, sR);
|
||||
rct::key S;
|
||||
rct::addKeys(S, ve, rct::scalarmultBase(rho));
|
||||
S = rct::scalarmultKey(S, INV_EIGHT);
|
||||
|
||||
// PAPER LINES 43-45
|
||||
rct::key y = hash_cache_mash(hash_cache, A, S);
|
||||
if (y == rct::zero())
|
||||
{
|
||||
PERF_TIMER_STOP(PROVE_step1);
|
||||
MINFO("y is 0, trying again");
|
||||
goto try_again;
|
||||
}
|
||||
rct::key z = hash_cache = rct::hash_to_scalar(y);
|
||||
if (z == rct::zero())
|
||||
{
|
||||
PERF_TIMER_STOP(PROVE_step1);
|
||||
MINFO("z is 0, trying again");
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
// Polynomial construction before PAPER LINE 46
|
||||
rct::key t0 = rct::zero();
|
||||
rct::key t1 = rct::zero();
|
||||
rct::key t2 = rct::zero();
|
||||
|
||||
const auto yN = vector_powers(y, N);
|
||||
|
||||
rct::key ip1y = vector_sum(yN);
|
||||
sc_muladd(t0.bytes, z.bytes, ip1y.bytes, t0.bytes);
|
||||
|
||||
rct::key zsq;
|
||||
sc_mul(zsq.bytes, z.bytes, z.bytes);
|
||||
sc_muladd(t0.bytes, zsq.bytes, sv.bytes, t0.bytes);
|
||||
|
||||
rct::key k = rct::zero();
|
||||
sc_mulsub(k.bytes, zsq.bytes, ip1y.bytes, k.bytes);
|
||||
|
||||
rct::key zcu;
|
||||
sc_mul(zcu.bytes, zsq.bytes, z.bytes);
|
||||
sc_mulsub(k.bytes, zcu.bytes, ip12.bytes, k.bytes);
|
||||
sc_add(t0.bytes, t0.bytes, k.bytes);
|
||||
|
||||
// DEBUG: Test the value of t0 has the correct form
|
||||
#ifdef DEBUG_BP
|
||||
rct::key test_t0 = rct::zero();
|
||||
rct::key iph = inner_product(aL, hadamard(aR, yN));
|
||||
sc_add(test_t0.bytes, test_t0.bytes, iph.bytes);
|
||||
rct::key ips = inner_product(vector_subtract(aL, aR), yN);
|
||||
sc_muladd(test_t0.bytes, z.bytes, ips.bytes, test_t0.bytes);
|
||||
rct::key ipt = inner_product(twoN, aL);
|
||||
sc_muladd(test_t0.bytes, zsq.bytes, ipt.bytes, test_t0.bytes);
|
||||
sc_add(test_t0.bytes, test_t0.bytes, k.bytes);
|
||||
CHECK_AND_ASSERT_THROW_MES(t0 == test_t0, "t0 check failed");
|
||||
#endif
|
||||
PERF_TIMER_STOP(PROVE_step1);
|
||||
|
||||
PERF_TIMER_START_BP(PROVE_step2);
|
||||
const auto HyNsR = hadamard(yN, sR);
|
||||
const auto vpIz = vector_dup(z, N);
|
||||
const auto vp2zsq = vector_scalar(twoN, zsq);
|
||||
const auto aL_vpIz = vector_subtract(aL, vpIz);
|
||||
const auto aR_vpIz = vector_add(aR, vpIz);
|
||||
|
||||
rct::key ip1 = inner_product(aL_vpIz, HyNsR);
|
||||
sc_add(t1.bytes, t1.bytes, ip1.bytes);
|
||||
|
||||
rct::key ip2 = inner_product(sL, vector_add(hadamard(yN, aR_vpIz), vp2zsq));
|
||||
sc_add(t1.bytes, t1.bytes, ip2.bytes);
|
||||
|
||||
rct::key ip3 = inner_product(sL, HyNsR);
|
||||
sc_add(t2.bytes, t2.bytes, ip3.bytes);
|
||||
|
||||
// PAPER LINES 47-48
|
||||
rct::key tau1 = rct::skGen(), tau2 = rct::skGen();
|
||||
|
||||
rct::key T1, T2;
|
||||
ge_p3 p3;
|
||||
sc_mul(tmp.bytes, t1.bytes, INV_EIGHT.bytes);
|
||||
sc_mul(tmp2.bytes, tau1.bytes, INV_EIGHT.bytes);
|
||||
ge_double_scalarmult_base_vartime_p3(&p3, tmp.bytes, &ge_p3_H, tmp2.bytes);
|
||||
ge_p3_tobytes(T1.bytes, &p3);
|
||||
sc_mul(tmp.bytes, t2.bytes, INV_EIGHT.bytes);
|
||||
sc_mul(tmp2.bytes, tau2.bytes, INV_EIGHT.bytes);
|
||||
ge_double_scalarmult_base_vartime_p3(&p3, tmp.bytes, &ge_p3_H, tmp2.bytes);
|
||||
ge_p3_tobytes(T2.bytes, &p3);
|
||||
|
||||
// PAPER LINES 49-51
|
||||
rct::key x = hash_cache_mash(hash_cache, z, T1, T2);
|
||||
if (x == rct::zero())
|
||||
{
|
||||
PERF_TIMER_STOP(PROVE_step2);
|
||||
MINFO("x is 0, trying again");
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
// PAPER LINES 52-53
|
||||
rct::key taux = rct::zero();
|
||||
sc_mul(taux.bytes, tau1.bytes, x.bytes);
|
||||
rct::key xsq;
|
||||
sc_mul(xsq.bytes, x.bytes, x.bytes);
|
||||
sc_muladd(taux.bytes, tau2.bytes, xsq.bytes, taux.bytes);
|
||||
sc_muladd(taux.bytes, gamma.bytes, zsq.bytes, taux.bytes);
|
||||
rct::key mu;
|
||||
sc_muladd(mu.bytes, x.bytes, rho.bytes, alpha.bytes);
|
||||
|
||||
// PAPER LINES 54-57
|
||||
rct::keyV l = vector_add(aL_vpIz, vector_scalar(sL, x));
|
||||
rct::keyV r = vector_add(hadamard(yN, vector_add(aR_vpIz, vector_scalar(sR, x))), vp2zsq);
|
||||
PERF_TIMER_STOP(PROVE_step2);
|
||||
|
||||
PERF_TIMER_START_BP(PROVE_step3);
|
||||
rct::key t = inner_product(l, r);
|
||||
|
||||
// DEBUG: Test if the l and r vectors match the polynomial forms
|
||||
#ifdef DEBUG_BP
|
||||
rct::key test_t;
|
||||
sc_muladd(test_t.bytes, t1.bytes, x.bytes, t0.bytes);
|
||||
sc_muladd(test_t.bytes, t2.bytes, xsq.bytes, test_t.bytes);
|
||||
CHECK_AND_ASSERT_THROW_MES(test_t == t, "test_t check failed");
|
||||
#endif
|
||||
|
||||
// PAPER LINES 32-33
|
||||
rct::key x_ip = hash_cache_mash(hash_cache, x, taux, mu, t);
|
||||
|
||||
// These are used in the inner product rounds
|
||||
size_t nprime = N;
|
||||
std::vector<ge_p3> Gprime(N);
|
||||
std::vector<ge_p3> Hprime(N);
|
||||
rct::keyV aprime(N);
|
||||
rct::keyV bprime(N);
|
||||
const rct::key yinv = invert(y);
|
||||
rct::key yinvpow = rct::identity();
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
{
|
||||
Gprime[i] = Gi_p3[i];
|
||||
ge_scalarmult_p3(&Hprime[i], yinvpow.bytes, &Hi_p3[i]);
|
||||
sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes);
|
||||
aprime[i] = l[i];
|
||||
bprime[i] = r[i];
|
||||
}
|
||||
rct::keyV L(logN);
|
||||
rct::keyV R(logN);
|
||||
int round = 0;
|
||||
rct::keyV w(logN); // this is the challenge x in the inner product protocol
|
||||
PERF_TIMER_STOP(PROVE_step3);
|
||||
|
||||
PERF_TIMER_START_BP(PROVE_step4);
|
||||
// PAPER LINE 13
|
||||
while (nprime > 1)
|
||||
{
|
||||
// PAPER LINE 15
|
||||
nprime /= 2;
|
||||
|
||||
// PAPER LINES 16-17
|
||||
rct::key cL = inner_product(slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size()));
|
||||
rct::key cR = inner_product(slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime));
|
||||
|
||||
// PAPER LINES 18-19
|
||||
sc_mul(tmp.bytes, cL.bytes, x_ip.bytes);
|
||||
L[round] = cross_vector_exponent8(nprime, Gprime, nprime, Hprime, 0, aprime, 0, bprime, nprime, &ge_p3_H, &tmp);
|
||||
sc_mul(tmp.bytes, cR.bytes, x_ip.bytes);
|
||||
R[round] = cross_vector_exponent8(nprime, Gprime, 0, Hprime, nprime, aprime, nprime, bprime, 0, &ge_p3_H, &tmp);
|
||||
|
||||
// PAPER LINES 21-22
|
||||
w[round] = hash_cache_mash(hash_cache, L[round], R[round]);
|
||||
if (w[round] == rct::zero())
|
||||
{
|
||||
PERF_TIMER_STOP(PROVE_step4);
|
||||
MINFO("w[round] is 0, trying again");
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
// PAPER LINES 24-25
|
||||
const rct::key winv = invert(w[round]);
|
||||
if (nprime > 1)
|
||||
{
|
||||
hadamard_fold(Gprime, winv, w[round]);
|
||||
hadamard_fold(Hprime, w[round], winv);
|
||||
}
|
||||
|
||||
// PAPER LINES 28-29
|
||||
aprime = vector_add(vector_scalar(slice(aprime, 0, nprime), w[round]), vector_scalar(slice(aprime, nprime, aprime.size()), winv));
|
||||
bprime = vector_add(vector_scalar(slice(bprime, 0, nprime), winv), vector_scalar(slice(bprime, nprime, bprime.size()), w[round]));
|
||||
|
||||
++round;
|
||||
}
|
||||
PERF_TIMER_STOP(PROVE_step4);
|
||||
|
||||
// PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20)
|
||||
return Bulletproof(V, A, S, T1, T2, taux, mu, std::move(L), std::move(R), aprime[0], bprime[0], t);
|
||||
return bulletproof_PROVE(rct::keyV(1, sv), rct::keyV(1, gamma));
|
||||
}
|
||||
|
||||
Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma)
|
||||
{
|
||||
// vG + gammaH
|
||||
PERF_TIMER_START_BP(PROVE_v);
|
||||
rct::key sv = rct::zero();
|
||||
sv.bytes[0] = v & 255;
|
||||
sv.bytes[1] = (v >> 8) & 255;
|
||||
sv.bytes[2] = (v >> 16) & 255;
|
||||
sv.bytes[3] = (v >> 24) & 255;
|
||||
sv.bytes[4] = (v >> 32) & 255;
|
||||
sv.bytes[5] = (v >> 40) & 255;
|
||||
sv.bytes[6] = (v >> 48) & 255;
|
||||
sv.bytes[7] = (v >> 56) & 255;
|
||||
PERF_TIMER_STOP(PROVE_v);
|
||||
return bulletproof_PROVE(sv, gamma);
|
||||
return bulletproof_PROVE(std::vector<uint64_t>(1, v), rct::keyV(1, gamma));
|
||||
}
|
||||
|
||||
/* Given a set of values v (0..2^N-1) and masks gamma, construct a range proof */
|
||||
|
@ -45,15 +45,6 @@ using namespace std;
|
||||
#define CHECK_AND_ASSERT_MES_L1(expr, ret, message) {if(!(expr)) {MCERROR("verify", message); return ret;}}
|
||||
|
||||
namespace rct {
|
||||
Bulletproof proveRangeBulletproof(key &C, key &mask, uint64_t amount)
|
||||
{
|
||||
mask = rct::skGen();
|
||||
Bulletproof proof = bulletproof_PROVE(amount, mask);
|
||||
CHECK_AND_ASSERT_THROW_MES(proof.V.size() == 1, "V has not exactly one element");
|
||||
C = proof.V[0];
|
||||
return proof;
|
||||
}
|
||||
|
||||
Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector<uint64_t> &amounts)
|
||||
{
|
||||
masks = rct::skvGen(amounts.size());
|
||||
|
Loading…
Reference in New Issue
Block a user