bulletproofs: remove single value prover

It is now expressed in terms of the array prover
This commit is contained in:
moneromooo-monero 2018-08-08 18:39:31 +00:00
parent 484155d043
commit a281b950bf
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
2 changed files with 2 additions and 291 deletions

View file

@ -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 */

View file

@ -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());