|
|
@ -889,36 +889,54 @@ Bulletproof bulletproof_PROVE(const std::vector<uint64_t> &v, const rct::keyV &g
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Given a range proof, determine if it is valid */
|
|
|
|
/* Given a range proof, determine if it is valid */
|
|
|
|
bool bulletproof_VERIFY(const Bulletproof &proof)
|
|
|
|
bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
init_exponents();
|
|
|
|
init_exponents();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PERF_TIMER_START_BP(VERIFY);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// sanity and figure out which proof is longest
|
|
|
|
|
|
|
|
size_t max_length = 0;
|
|
|
|
|
|
|
|
for (const Bulletproof *p: proofs)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
const Bulletproof &proof = *p;
|
|
|
|
CHECK_AND_ASSERT_MES(proof.V.size() >= 1, false, "V does not have at least one element");
|
|
|
|
CHECK_AND_ASSERT_MES(proof.V.size() >= 1, false, "V does not have at least one element");
|
|
|
|
CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), false, "Mismatched L and R sizes");
|
|
|
|
CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), false, "Mismatched L and R sizes");
|
|
|
|
CHECK_AND_ASSERT_MES(proof.L.size() > 0, false, "Empty proof");
|
|
|
|
CHECK_AND_ASSERT_MES(proof.L.size() > 0, false, "Empty proof");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
max_length = std::max(max_length, proof.L.size());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
CHECK_AND_ASSERT_MES(max_length < 32, false, "At least one proof is too large");
|
|
|
|
|
|
|
|
size_t maxMN = 1u << max_length;
|
|
|
|
|
|
|
|
|
|
|
|
const size_t logN = 6;
|
|
|
|
const size_t logN = 6;
|
|
|
|
const size_t N = 1 << logN;
|
|
|
|
const size_t N = 1 << logN;
|
|
|
|
rct::key tmp, tmp2;
|
|
|
|
rct::key tmp;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// setup weighted aggregates
|
|
|
|
|
|
|
|
rct::key Z0 = rct::identity();
|
|
|
|
|
|
|
|
rct::key z1 = rct::zero();
|
|
|
|
|
|
|
|
rct::key Z2 = rct::identity();
|
|
|
|
|
|
|
|
rct::key z3 = rct::zero();
|
|
|
|
|
|
|
|
rct::keyV z4(maxMN, rct::zero()), z5(maxMN, rct::zero());
|
|
|
|
|
|
|
|
for (const Bulletproof *p: proofs)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
const Bulletproof &proof = *p;
|
|
|
|
|
|
|
|
|
|
|
|
size_t M, logM;
|
|
|
|
size_t M, logM;
|
|
|
|
for (logM = 0; (M = 1<<logM) <= maxM && M < proof.V.size(); ++logM);
|
|
|
|
for (logM = 0; (M = 1<<logM) <= maxM && M < proof.V.size(); ++logM);
|
|
|
|
CHECK_AND_ASSERT_MES(proof.L.size() == 6+logM, false, "Proof is not the expected size");
|
|
|
|
CHECK_AND_ASSERT_MES(proof.L.size() == 6+logM, false, "Proof is not the expected size");
|
|
|
|
const size_t MN = M*N;
|
|
|
|
const size_t MN = M*N;
|
|
|
|
|
|
|
|
rct::key weight = rct::skGen();
|
|
|
|
|
|
|
|
|
|
|
|
// Reconstruct the challenges
|
|
|
|
// Reconstruct the challenges
|
|
|
|
PERF_TIMER_START_BP(VERIFY);
|
|
|
|
|
|
|
|
PERF_TIMER_START_BP(VERIFY_start);
|
|
|
|
PERF_TIMER_START_BP(VERIFY_start);
|
|
|
|
rct::key hash_cache = rct::hash_to_scalar(proof.V);
|
|
|
|
rct::key hash_cache = rct::hash_to_scalar(proof.V);
|
|
|
|
rct::key y = hash_cache_mash(hash_cache, proof.A, proof.S);
|
|
|
|
rct::key y = hash_cache_mash(hash_cache, proof.A, proof.S);
|
|
|
|
rct::key z = hash_cache = rct::hash_to_scalar(y);
|
|
|
|
rct::key z = hash_cache = rct::hash_to_scalar(y);
|
|
|
|
rct::key x = hash_cache_mash(hash_cache, z, proof.T1, proof.T2);
|
|
|
|
rct::key x = hash_cache_mash(hash_cache, z, proof.T1, proof.T2);
|
|
|
|
PERF_TIMER_STOP(VERIFY_start);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PERF_TIMER_START_BP(VERIFY_line_60);
|
|
|
|
|
|
|
|
// Reconstruct the challenges
|
|
|
|
|
|
|
|
rct::key x_ip = hash_cache_mash(hash_cache, x, proof.taux, proof.mu, proof.t);
|
|
|
|
rct::key x_ip = hash_cache_mash(hash_cache, x, proof.taux, proof.mu, proof.t);
|
|
|
|
PERF_TIMER_STOP(VERIFY_line_60);
|
|
|
|
PERF_TIMER_STOP(VERIFY_start);
|
|
|
|
|
|
|
|
|
|
|
|
PERF_TIMER_START_BP(VERIFY_line_61);
|
|
|
|
PERF_TIMER_START_BP(VERIFY_line_61);
|
|
|
|
// PAPER LINE 61
|
|
|
|
// PAPER LINE 61
|
|
|
@ -998,7 +1016,7 @@ bool bulletproof_VERIFY(const Bulletproof &proof)
|
|
|
|
|
|
|
|
|
|
|
|
PERF_TIMER_START_BP(VERIFY_line_62);
|
|
|
|
PERF_TIMER_START_BP(VERIFY_line_62);
|
|
|
|
// PAPER LINE 62
|
|
|
|
// PAPER LINE 62
|
|
|
|
rct::key P = rct::addKeys(proof.A, rct::scalarmultKey(proof.S, x));
|
|
|
|
rct::addKeys(Z0, Z0, rct::scalarmultKey(rct::addKeys(proof.A, rct::scalarmultKey(proof.S, x)), weight));
|
|
|
|
PERF_TIMER_STOP(VERIFY_line_62);
|
|
|
|
PERF_TIMER_STOP(VERIFY_line_62);
|
|
|
|
|
|
|
|
|
|
|
|
// Compute the number of rounds for the inner product
|
|
|
|
// Compute the number of rounds for the inner product
|
|
|
@ -1028,9 +1046,6 @@ bool bulletproof_VERIFY(const Bulletproof &proof)
|
|
|
|
winv[i] = invert(w[i]);
|
|
|
|
winv[i] = invert(w[i]);
|
|
|
|
PERF_TIMER_STOP(VERIFY_line_24_25_invert);
|
|
|
|
PERF_TIMER_STOP(VERIFY_line_24_25_invert);
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<MultiexpData> multiexp_data;
|
|
|
|
|
|
|
|
multiexp_data.clear();
|
|
|
|
|
|
|
|
multiexp_data.reserve(MN*2);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < MN; ++i)
|
|
|
|
for (size_t i = 0; i < MN; ++i)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// Convert the index to binary IN REVERSE and construct the scalar exponent
|
|
|
|
// Convert the index to binary IN REVERSE and construct the scalar exponent
|
|
|
@ -1062,8 +1077,8 @@ bool bulletproof_VERIFY(const Bulletproof &proof)
|
|
|
|
sc_muladd(tmp.bytes, z.bytes, ypow.bytes, tmp.bytes);
|
|
|
|
sc_muladd(tmp.bytes, z.bytes, ypow.bytes, tmp.bytes);
|
|
|
|
sc_mulsub(h_scalar.bytes, tmp.bytes, yinvpow.bytes, h_scalar.bytes);
|
|
|
|
sc_mulsub(h_scalar.bytes, tmp.bytes, yinvpow.bytes, h_scalar.bytes);
|
|
|
|
|
|
|
|
|
|
|
|
multiexp_data.emplace_back(g_scalar, Gi_p3[i]);
|
|
|
|
sc_muladd(z4[i].bytes, g_scalar.bytes, weight.bytes, z4[i].bytes);
|
|
|
|
multiexp_data.emplace_back(h_scalar, Hi_p3[i]);
|
|
|
|
sc_muladd(z5[i].bytes, h_scalar.bytes, weight.bytes, z5[i].bytes);
|
|
|
|
|
|
|
|
|
|
|
|
if (i != MN-1)
|
|
|
|
if (i != MN-1)
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -1072,36 +1087,50 @@ bool bulletproof_VERIFY(const Bulletproof &proof)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rct::key inner_prod = multiexp(multiexp_data, true);
|
|
|
|
|
|
|
|
PERF_TIMER_STOP(VERIFY_line_24_25);
|
|
|
|
PERF_TIMER_STOP(VERIFY_line_24_25);
|
|
|
|
|
|
|
|
|
|
|
|
// PAPER LINE 26
|
|
|
|
// PAPER LINE 26
|
|
|
|
rct::key pprime;
|
|
|
|
|
|
|
|
PERF_TIMER_START_BP(VERIFY_line_26_new);
|
|
|
|
PERF_TIMER_START_BP(VERIFY_line_26_new);
|
|
|
|
multiexp_data.clear();
|
|
|
|
std::vector<MultiexpData> multiexp_data;
|
|
|
|
multiexp_data.reserve(1+2*rounds);
|
|
|
|
multiexp_data.reserve(2*rounds);
|
|
|
|
|
|
|
|
|
|
|
|
sc_sub(tmp.bytes, rct::zero().bytes, proof.mu.bytes);
|
|
|
|
sc_muladd(z1.bytes, proof.mu.bytes, weight.bytes, z1.bytes);
|
|
|
|
rct::addKeys(pprime, P, rct::scalarmultBase(tmp));
|
|
|
|
|
|
|
|
for (size_t i = 0; i < rounds; ++i)
|
|
|
|
for (size_t i = 0; i < rounds; ++i)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
sc_mul(tmp.bytes, w[i].bytes, w[i].bytes);
|
|
|
|
sc_mul(tmp.bytes, w[i].bytes, w[i].bytes);
|
|
|
|
sc_mul(tmp2.bytes, winv[i].bytes, winv[i].bytes);
|
|
|
|
|
|
|
|
multiexp_data.emplace_back(tmp, proof.L[i]);
|
|
|
|
multiexp_data.emplace_back(tmp, proof.L[i]);
|
|
|
|
multiexp_data.emplace_back(tmp2, proof.R[i]);
|
|
|
|
sc_mul(tmp.bytes, winv[i].bytes, winv[i].bytes);
|
|
|
|
|
|
|
|
multiexp_data.emplace_back(tmp, proof.R[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sc_mul(tmp.bytes, proof.t.bytes, x_ip.bytes);
|
|
|
|
rct::key acc = multiexp(multiexp_data, false);
|
|
|
|
multiexp_data.emplace_back(tmp, rct::H);
|
|
|
|
rct::addKeys(Z2, Z2, rct::scalarmultKey(acc, weight));
|
|
|
|
addKeys(pprime, pprime, multiexp(multiexp_data, false));
|
|
|
|
sc_mulsub(tmp.bytes, proof.a.bytes, proof.b.bytes, proof.t.bytes);
|
|
|
|
|
|
|
|
sc_mul(tmp.bytes, tmp.bytes, x_ip.bytes);
|
|
|
|
|
|
|
|
sc_muladd(z3.bytes, tmp.bytes, weight.bytes, z3.bytes);
|
|
|
|
PERF_TIMER_STOP(VERIFY_line_26_new);
|
|
|
|
PERF_TIMER_STOP(VERIFY_line_26_new);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// now check all proofs at once
|
|
|
|
PERF_TIMER_START_BP(VERIFY_step2_check);
|
|
|
|
PERF_TIMER_START_BP(VERIFY_step2_check);
|
|
|
|
sc_mul(tmp.bytes, proof.a.bytes, proof.b.bytes);
|
|
|
|
rct::key Y = Z0;
|
|
|
|
sc_mul(tmp.bytes, tmp.bytes, x_ip.bytes);
|
|
|
|
sc_sub(tmp.bytes, rct::zero().bytes, z1.bytes);
|
|
|
|
tmp = rct::scalarmultKey(rct::H, tmp);
|
|
|
|
rct::addKeys(Y, Y, rct::scalarmultBase(tmp));
|
|
|
|
rct::addKeys(tmp, tmp, inner_prod);
|
|
|
|
rct::addKeys(Y, Y, Z2);
|
|
|
|
|
|
|
|
rct::addKeys(Y, Y, rct::scalarmultKey(rct::H, z3));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<MultiexpData> multiexp_data;
|
|
|
|
|
|
|
|
multiexp_data.reserve(2 * maxMN);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < maxMN; ++i)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
sc_sub(tmp.bytes, rct::zero().bytes, z4[i].bytes);
|
|
|
|
|
|
|
|
multiexp_data.emplace_back(tmp, Gi_p3[i]);
|
|
|
|
|
|
|
|
sc_sub(tmp.bytes, rct::zero().bytes, z5[i].bytes);
|
|
|
|
|
|
|
|
multiexp_data.emplace_back(tmp, Hi_p3[i]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
rct::addKeys(Y, Y, multiexp(multiexp_data, true));
|
|
|
|
PERF_TIMER_STOP(VERIFY_step2_check);
|
|
|
|
PERF_TIMER_STOP(VERIFY_step2_check);
|
|
|
|
if (!(pprime == tmp))
|
|
|
|
|
|
|
|
|
|
|
|
if (!(Y == rct::identity()))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
MERROR("Verification failure at step 2");
|
|
|
|
MERROR("Verification failure at step 2");
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
@ -1111,4 +1140,19 @@ bool bulletproof_VERIFY(const Bulletproof &proof)
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool bulletproof_VERIFY(const std::vector<Bulletproof> &proofs)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
std::vector<const Bulletproof*> proof_pointers;
|
|
|
|
|
|
|
|
for (const Bulletproof &proof: proofs)
|
|
|
|
|
|
|
|
proof_pointers.push_back(&proof);
|
|
|
|
|
|
|
|
return bulletproof_VERIFY(proof_pointers);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool bulletproof_VERIFY(const Bulletproof &proof)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
std::vector<const Bulletproof*> proofs;
|
|
|
|
|
|
|
|
proofs.push_back(&proof);
|
|
|
|
|
|
|
|
return bulletproof_VERIFY(proofs);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|