aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cryptonote_core/blockchain.cpp2
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp2
-rw-r--r--src/ringct/bulletproofs.cc394
-rw-r--r--src/ringct/bulletproofs.h2
-rw-r--r--src/ringct/rctSigs.cpp163
-rw-r--r--src/ringct/rctSigs.h6
-rw-r--r--tests/performance_tests/bulletproof.h38
-rw-r--r--tests/performance_tests/main.cpp11
-rw-r--r--tests/performance_tests/performance_tests.h2
-rw-r--r--tests/unit_tests/bulletproofs.cpp19
-rw-r--r--tests/unit_tests/ringct.cpp17
11 files changed, 425 insertions, 231 deletions
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index c815b507e..0800409b5 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -2988,7 +2988,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
}
}
- if (!rct::verRctSimple(rv, false))
+ if (!rct::verRctNonSemanticsSimple(rv))
{
MERROR_VER("Failed to check ringct signatures!");
return false;
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index d0db38799..4928bb528 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -896,7 +896,7 @@ namespace cryptonote
return false;
case rct::RCTTypeSimple:
case rct::RCTTypeSimpleBulletproof:
- if (!rct::verRctSimple(rv, true))
+ if (!rct::verRctSemanticsSimple(rv))
{
MERROR_VER("rct signature semantics check failed");
return false;
diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc
index 6ba984b03..e2540fb22 100644
--- a/src/ringct/bulletproofs.cc
+++ b/src/ringct/bulletproofs.cc
@@ -889,219 +889,248 @@ Bulletproof bulletproof_PROVE(const std::vector<uint64_t> &v, const rct::keyV &g
}
/* 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();
- 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() > 0, false, "Empty proof");
-
- const size_t logN = 6;
- const size_t N = 1 << logN;
- rct::key tmp, tmp2;
-
- size_t M, 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");
- const size_t MN = M*N;
-
- // Reconstruct the challenges
PERF_TIMER_START_BP(VERIFY);
- PERF_TIMER_START_BP(VERIFY_start);
- rct::key hash_cache = rct::hash_to_scalar(proof.V);
- rct::key y = hash_cache_mash(hash_cache, proof.A, proof.S);
- rct::key z = hash_cache = rct::hash_to_scalar(y);
- 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);
- PERF_TIMER_STOP(VERIFY_line_60);
- PERF_TIMER_START_BP(VERIFY_line_61);
- // PAPER LINE 61
- rct::key L61Left, L61Right;
- rct::addKeys2(L61Left, proof.taux, proof.t, rct::H);
-
- const rct::keyV zpow = vector_powers(z, M+3);
-
- rct::key k;
- const rct::key ip1y = vector_power_sum(y, MN);
- sc_mulsub(k.bytes, zpow[2].bytes, ip1y.bytes, rct::zero().bytes);
- for (size_t j = 1; j <= M; ++j)
+ // sanity and figure out which proof is longest
+ size_t max_length = 0;
+ for (const Bulletproof *p: proofs)
{
- CHECK_AND_ASSERT_MES(j+2 < zpow.size(), false, "invalid zpow index");
- sc_mulsub(k.bytes, zpow[j+2].bytes, ip12.bytes, k.bytes);
+ 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.L.size() == proof.R.size(), false, "Mismatched L and R sizes");
+ CHECK_AND_ASSERT_MES(proof.L.size() > 0, false, "Empty proof");
+
+ max_length = std::max(max_length, proof.L.size());
}
- PERF_TIMER_STOP(VERIFY_line_61);
+ CHECK_AND_ASSERT_MES(max_length < 32, false, "At least one proof is too large");
+ size_t maxMN = 1u << max_length;
- // bos coster is slower for small numbers of calcs, straus seems not
- if (1)
+ const size_t logN = 6;
+ const size_t N = 1 << logN;
+ 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)
{
- PERF_TIMER_START_BP(VERIFY_line_61rl_new);
- sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes);
- std::vector<MultiexpData> multiexp_data;
- multiexp_data.reserve(3+proof.V.size());
- multiexp_data.emplace_back(tmp, rct::H);
- for (size_t j = 0; j < proof.V.size(); j++)
+ const Bulletproof &proof = *p;
+
+ size_t M, 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");
+ const size_t MN = M*N;
+ rct::key weight = rct::skGen();
+
+ // Reconstruct the challenges
+ PERF_TIMER_START_BP(VERIFY_start);
+ rct::key hash_cache = rct::hash_to_scalar(proof.V);
+ rct::key y = hash_cache_mash(hash_cache, proof.A, proof.S);
+ 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_ip = hash_cache_mash(hash_cache, x, proof.taux, proof.mu, proof.t);
+ PERF_TIMER_STOP(VERIFY_start);
+
+ PERF_TIMER_START_BP(VERIFY_line_61);
+ // PAPER LINE 61
+ rct::key L61Left, L61Right;
+ rct::addKeys2(L61Left, proof.taux, proof.t, rct::H);
+
+ const rct::keyV zpow = vector_powers(z, M+3);
+
+ rct::key k;
+ const rct::key ip1y = vector_power_sum(y, MN);
+ sc_mulsub(k.bytes, zpow[2].bytes, ip1y.bytes, rct::zero().bytes);
+ for (size_t j = 1; j <= M; ++j)
{
- multiexp_data.emplace_back(zpow[j+2], proof.V[j]);
+ CHECK_AND_ASSERT_MES(j+2 < zpow.size(), false, "invalid zpow index");
+ sc_mulsub(k.bytes, zpow[j+2].bytes, ip12.bytes, k.bytes);
}
- multiexp_data.emplace_back(x, proof.T1);
- rct::key xsq;
- sc_mul(xsq.bytes, x.bytes, x.bytes);
- multiexp_data.emplace_back(xsq, proof.T2);
- L61Right = multiexp(multiexp_data, false);
- PERF_TIMER_STOP(VERIFY_line_61rl_new);
- }
- else
- {
- PERF_TIMER_START_BP(VERIFY_line_61rl_old);
- sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes);
- L61Right = rct::scalarmultKey(rct::H, tmp);
- ge_p3 L61Right_p3;
- CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&L61Right_p3, L61Right.bytes) == 0, "ge_frombytes_vartime failed");
- for (size_t j = 0; j+1 < proof.V.size(); j += 2)
+ PERF_TIMER_STOP(VERIFY_line_61);
+
+ // bos coster is slower for small numbers of calcs, straus seems not
+ if (1)
{
- CHECK_AND_ASSERT_MES(j+2+1 < zpow.size(), false, "invalid zpow index");
- ge_dsmp precomp0, precomp1;
- rct::precomp(precomp0, j < proof.V.size() ? proof.V[j] : rct::identity());
- rct::precomp(precomp1, j+1 < proof.V.size() ? proof.V[j+1] : rct::identity());
- rct::addKeys3acc_p3(&L61Right_p3, zpow[j+2], precomp0, zpow[j+2+1], precomp1);
+ PERF_TIMER_START_BP(VERIFY_line_61rl_new);
+ sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes);
+ std::vector<MultiexpData> multiexp_data;
+ multiexp_data.reserve(3+proof.V.size());
+ multiexp_data.emplace_back(tmp, rct::H);
+ for (size_t j = 0; j < proof.V.size(); j++)
+ {
+ multiexp_data.emplace_back(zpow[j+2], proof.V[j]);
+ }
+ multiexp_data.emplace_back(x, proof.T1);
+ rct::key xsq;
+ sc_mul(xsq.bytes, x.bytes, x.bytes);
+ multiexp_data.emplace_back(xsq, proof.T2);
+ L61Right = multiexp(multiexp_data, false);
+ PERF_TIMER_STOP(VERIFY_line_61rl_new);
}
- for (size_t j = proof.V.size() & 0xfffffffe; j < M; j++)
+ else
{
- CHECK_AND_ASSERT_MES(j+2 < zpow.size(), false, "invalid zpow index");
- // faster equivalent to:
- // tmp = rct::scalarmultKey(j < proof.V.size() ? proof.V[j] : rct::identity(), zpow[j+2]);
- // rct::addKeys(L61Right, L61Right, tmp);
- if (j < proof.V.size())
- addKeys_acc_p3(&L61Right_p3, zpow[j+2], proof.V[j]);
- }
-
- addKeys_acc_p3(&L61Right_p3, x, proof.T1);
-
- rct::key xsq;
- sc_mul(xsq.bytes, x.bytes, x.bytes);
- addKeys_acc_p3(&L61Right_p3, xsq, proof.T2);
- ge_p3_tobytes(L61Right.bytes, &L61Right_p3);
- PERF_TIMER_STOP(VERIFY_line_61rl_old);
- }
-
- if (!(L61Right == L61Left))
- {
- MERROR("Verification failure at step 1");
- return false;
- }
-
- PERF_TIMER_START_BP(VERIFY_line_62);
- // PAPER LINE 62
- rct::key P = rct::addKeys(proof.A, rct::scalarmultKey(proof.S, x));
- PERF_TIMER_STOP(VERIFY_line_62);
+ PERF_TIMER_START_BP(VERIFY_line_61rl_old);
+ sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes);
+ L61Right = rct::scalarmultKey(rct::H, tmp);
+ ge_p3 L61Right_p3;
+ CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&L61Right_p3, L61Right.bytes) == 0, "ge_frombytes_vartime failed");
+ for (size_t j = 0; j+1 < proof.V.size(); j += 2)
+ {
+ CHECK_AND_ASSERT_MES(j+2+1 < zpow.size(), false, "invalid zpow index");
+ ge_dsmp precomp0, precomp1;
+ rct::precomp(precomp0, j < proof.V.size() ? proof.V[j] : rct::identity());
+ rct::precomp(precomp1, j+1 < proof.V.size() ? proof.V[j+1] : rct::identity());
+ rct::addKeys3acc_p3(&L61Right_p3, zpow[j+2], precomp0, zpow[j+2+1], precomp1);
+ }
+ for (size_t j = proof.V.size() & 0xfffffffe; j < M; j++)
+ {
+ CHECK_AND_ASSERT_MES(j+2 < zpow.size(), false, "invalid zpow index");
+ // faster equivalent to:
+ // tmp = rct::scalarmultKey(j < proof.V.size() ? proof.V[j] : rct::identity(), zpow[j+2]);
+ // rct::addKeys(L61Right, L61Right, tmp);
+ if (j < proof.V.size())
+ addKeys_acc_p3(&L61Right_p3, zpow[j+2], proof.V[j]);
+ }
- // Compute the number of rounds for the inner product
- const size_t rounds = logM+logN;
- CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds");
+ addKeys_acc_p3(&L61Right_p3, x, proof.T1);
- PERF_TIMER_START_BP(VERIFY_line_21_22);
- // PAPER LINES 21-22
- // The inner product challenges are computed per round
- rct::keyV w(rounds);
- for (size_t i = 0; i < rounds; ++i)
- {
- w[i] = hash_cache_mash(hash_cache, proof.L[i], proof.R[i]);
- }
- PERF_TIMER_STOP(VERIFY_line_21_22);
+ rct::key xsq;
+ sc_mul(xsq.bytes, x.bytes, x.bytes);
+ addKeys_acc_p3(&L61Right_p3, xsq, proof.T2);
+ ge_p3_tobytes(L61Right.bytes, &L61Right_p3);
+ PERF_TIMER_STOP(VERIFY_line_61rl_old);
+ }
- PERF_TIMER_START_BP(VERIFY_line_24_25);
- // Basically PAPER LINES 24-25
- // Compute the curvepoints from G[i] and H[i]
- rct::key yinvpow = rct::identity();
- rct::key ypow = rct::identity();
+ if (!(L61Right == L61Left))
+ {
+ MERROR("Verification failure at step 1");
+ return false;
+ }
- PERF_TIMER_START_BP(VERIFY_line_24_25_invert);
- const rct::key yinv = invert(y);
- rct::keyV winv(rounds);
- for (size_t i = 0; i < rounds; ++i)
- winv[i] = invert(w[i]);
- PERF_TIMER_STOP(VERIFY_line_24_25_invert);
+ PERF_TIMER_START_BP(VERIFY_line_62);
+ // PAPER LINE 62
+ rct::addKeys(Z0, Z0, rct::scalarmultKey(rct::addKeys(proof.A, rct::scalarmultKey(proof.S, x)), weight));
+ PERF_TIMER_STOP(VERIFY_line_62);
- std::vector<MultiexpData> multiexp_data;
- multiexp_data.clear();
- multiexp_data.reserve(MN*2);
- for (size_t i = 0; i < MN; ++i)
- {
- // Convert the index to binary IN REVERSE and construct the scalar exponent
- rct::key g_scalar = proof.a;
- rct::key h_scalar;
- sc_mul(h_scalar.bytes, proof.b.bytes, yinvpow.bytes);
+ // Compute the number of rounds for the inner product
+ const size_t rounds = logM+logN;
+ CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds");
- for (size_t j = rounds; j-- > 0; )
+ PERF_TIMER_START_BP(VERIFY_line_21_22);
+ // PAPER LINES 21-22
+ // The inner product challenges are computed per round
+ rct::keyV w(rounds);
+ for (size_t i = 0; i < rounds; ++i)
+ {
+ w[i] = hash_cache_mash(hash_cache, proof.L[i], proof.R[i]);
+ }
+ PERF_TIMER_STOP(VERIFY_line_21_22);
+
+ PERF_TIMER_START_BP(VERIFY_line_24_25);
+ // Basically PAPER LINES 24-25
+ // Compute the curvepoints from G[i] and H[i]
+ rct::key yinvpow = rct::identity();
+ rct::key ypow = rct::identity();
+
+ PERF_TIMER_START_BP(VERIFY_line_24_25_invert);
+ const rct::key yinv = invert(y);
+ rct::keyV winv(rounds);
+ for (size_t i = 0; i < rounds; ++i)
+ winv[i] = invert(w[i]);
+ PERF_TIMER_STOP(VERIFY_line_24_25_invert);
+
+ for (size_t i = 0; i < MN; ++i)
{
- size_t J = w.size() - j - 1;
+ // Convert the index to binary IN REVERSE and construct the scalar exponent
+ rct::key g_scalar = proof.a;
+ rct::key h_scalar;
+ sc_mul(h_scalar.bytes, proof.b.bytes, yinvpow.bytes);
- if ((i & (((size_t)1)<<j)) == 0)
+ for (size_t j = rounds; j-- > 0; )
{
- sc_mul(g_scalar.bytes, g_scalar.bytes, winv[J].bytes);
- sc_mul(h_scalar.bytes, h_scalar.bytes, w[J].bytes);
+ size_t J = w.size() - j - 1;
+
+ if ((i & (((size_t)1)<<j)) == 0)
+ {
+ sc_mul(g_scalar.bytes, g_scalar.bytes, winv[J].bytes);
+ sc_mul(h_scalar.bytes, h_scalar.bytes, w[J].bytes);
+ }
+ else
+ {
+ sc_mul(g_scalar.bytes, g_scalar.bytes, w[J].bytes);
+ sc_mul(h_scalar.bytes, h_scalar.bytes, winv[J].bytes);
+ }
}
- else
+
+ // Adjust the scalars using the exponents from PAPER LINE 62
+ sc_add(g_scalar.bytes, g_scalar.bytes, z.bytes);
+ CHECK_AND_ASSERT_MES(2+i/N < zpow.size(), false, "invalid zpow index");
+ CHECK_AND_ASSERT_MES(i%N < twoN.size(), false, "invalid twoN index");
+ sc_mul(tmp.bytes, zpow[2+i/N].bytes, twoN[i%N].bytes);
+ sc_muladd(tmp.bytes, z.bytes, ypow.bytes, tmp.bytes);
+ sc_mulsub(h_scalar.bytes, tmp.bytes, yinvpow.bytes, h_scalar.bytes);
+
+ sc_muladd(z4[i].bytes, g_scalar.bytes, weight.bytes, z4[i].bytes);
+ sc_muladd(z5[i].bytes, h_scalar.bytes, weight.bytes, z5[i].bytes);
+
+ if (i != MN-1)
{
- sc_mul(g_scalar.bytes, g_scalar.bytes, w[J].bytes);
- sc_mul(h_scalar.bytes, h_scalar.bytes, winv[J].bytes);
+ sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes);
+ sc_mul(ypow.bytes, ypow.bytes, y.bytes);
}
}
- // Adjust the scalars using the exponents from PAPER LINE 62
- sc_add(g_scalar.bytes, g_scalar.bytes, z.bytes);
- CHECK_AND_ASSERT_MES(2+i/N < zpow.size(), false, "invalid zpow index");
- CHECK_AND_ASSERT_MES(i%N < twoN.size(), false, "invalid twoN index");
- sc_mul(tmp.bytes, zpow[2+i/N].bytes, twoN[i%N].bytes);
- sc_muladd(tmp.bytes, z.bytes, ypow.bytes, tmp.bytes);
- sc_mulsub(h_scalar.bytes, tmp.bytes, yinvpow.bytes, h_scalar.bytes);
+ PERF_TIMER_STOP(VERIFY_line_24_25);
- multiexp_data.emplace_back(g_scalar, Gi_p3[i]);
- multiexp_data.emplace_back(h_scalar, Hi_p3[i]);
+ // PAPER LINE 26
+ PERF_TIMER_START_BP(VERIFY_line_26_new);
+ std::vector<MultiexpData> multiexp_data;
+ multiexp_data.reserve(2*rounds);
- if (i != MN-1)
+ sc_muladd(z1.bytes, proof.mu.bytes, weight.bytes, z1.bytes);
+ for (size_t i = 0; i < rounds; ++i)
{
- sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes);
- sc_mul(ypow.bytes, ypow.bytes, y.bytes);
+ sc_mul(tmp.bytes, w[i].bytes, w[i].bytes);
+ multiexp_data.emplace_back(tmp, proof.L[i]);
+ sc_mul(tmp.bytes, winv[i].bytes, winv[i].bytes);
+ multiexp_data.emplace_back(tmp, proof.R[i]);
}
+ rct::key acc = multiexp(multiexp_data, false);
+ rct::addKeys(Z2, Z2, rct::scalarmultKey(acc, weight));
+ 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);
}
- rct::key inner_prod = multiexp(multiexp_data, true);
- PERF_TIMER_STOP(VERIFY_line_24_25);
-
- // PAPER LINE 26
- rct::key pprime;
- PERF_TIMER_START_BP(VERIFY_line_26_new);
- multiexp_data.clear();
- multiexp_data.reserve(1+2*rounds);
+ // now check all proofs at once
+ PERF_TIMER_START_BP(VERIFY_step2_check);
+ rct::key Y = Z0;
+ sc_sub(tmp.bytes, rct::zero().bytes, z1.bytes);
+ rct::addKeys(Y, Y, rct::scalarmultBase(tmp));
+ rct::addKeys(Y, Y, Z2);
+ rct::addKeys(Y, Y, rct::scalarmultKey(rct::H, z3));
- sc_sub(tmp.bytes, rct::zero().bytes, proof.mu.bytes);
- rct::addKeys(pprime, P, rct::scalarmultBase(tmp));
- for (size_t i = 0; i < rounds; ++i)
+ std::vector<MultiexpData> multiexp_data;
+ multiexp_data.reserve(2 * maxMN);
+ for (size_t i = 0; i < maxMN; ++i)
{
- 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(tmp2, proof.R[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]);
}
- sc_mul(tmp.bytes, proof.t.bytes, x_ip.bytes);
- multiexp_data.emplace_back(tmp, rct::H);
- addKeys(pprime, pprime, multiexp(multiexp_data, false));
- PERF_TIMER_STOP(VERIFY_line_26_new);
-
- PERF_TIMER_START_BP(VERIFY_step2_check);
- sc_mul(tmp.bytes, proof.a.bytes, proof.b.bytes);
- sc_mul(tmp.bytes, tmp.bytes, x_ip.bytes);
- tmp = rct::scalarmultKey(rct::H, tmp);
- rct::addKeys(tmp, tmp, inner_prod);
+ rct::addKeys(Y, Y, multiexp(multiexp_data, true));
PERF_TIMER_STOP(VERIFY_step2_check);
- if (!(pprime == tmp))
+
+ if (!(Y == rct::identity()))
{
MERROR("Verification failure at step 2");
return false;
@@ -1111,4 +1140,19 @@ bool bulletproof_VERIFY(const Bulletproof &proof)
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);
+}
+
}
diff --git a/src/ringct/bulletproofs.h b/src/ringct/bulletproofs.h
index 3dfa38b12..b86202ccc 100644
--- a/src/ringct/bulletproofs.h
+++ b/src/ringct/bulletproofs.h
@@ -43,6 +43,8 @@ Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma);
Bulletproof bulletproof_PROVE(const rct::keyV &v, const rct::keyV &gamma);
Bulletproof bulletproof_PROVE(const std::vector<uint64_t> &v, const rct::keyV &gamma);
bool bulletproof_VERIFY(const Bulletproof &proof);
+bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs);
+bool bulletproof_VERIFY(const std::vector<Bulletproof> &proofs);
}
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index 6dcb203bf..2e2b07fcc 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -70,6 +70,13 @@ namespace rct {
catch (...) { return false; }
}
+ bool verBulletproof(const std::vector<const Bulletproof*> &proofs)
+ {
+ try { return bulletproof_VERIFY(proofs); }
+ // we can get deep throws from ge_frombytes_vartime if input isn't valid
+ catch (...) { return false; }
+ }
+
//Borromean (c.f. gmax/andytoshi's paper)
boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) {
key64 L[2], alpha;
@@ -918,15 +925,23 @@ namespace rct {
//ver RingCT simple
//assumes only post-rct style inputs (at least for max anonymity)
- bool verRctSimple(const rctSig & rv, bool semantics) {
+ bool verRctSemanticsSimple(const std::vector<const rctSig*> & rvv) {
try
{
- PERF_TIMER(verRctSimple);
+ PERF_TIMER(verRctSemanticsSimple);
- CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctSimple called on non simple rctSig");
- const bool bulletproof = is_rct_bulletproof(rv.type);
- if (semantics)
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
+ std::deque<bool> results;
+ std::vector<const Bulletproof*> proofs;
+ size_t max_non_bp_proofs = 0, offset = 0;
+
+ for (const rctSig *rvp: rvv)
{
+ CHECK_AND_ASSERT_MES(rvp, false, "rctSig pointer is NULL");
+ const rctSig &rv = *rvp;
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctSemanticsSimple called on non simple rctSig");
+ const bool bulletproof = is_rct_bulletproof(rv.type);
if (bulletproof)
{
CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs");
@@ -940,28 +955,22 @@ namespace rct {
CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.empty(), false, "rv.p.pseudoOuts is not empty");
}
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
- }
- else
- {
- // semantics check is early, and mixRing/MGs aren't resolved yet
- if (bulletproof)
- CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.p.pseudoOuts and mixRing");
- else
- CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing");
- }
- const size_t threads = std::max(rv.outPk.size(), rv.mixRing.size());
+ if (!bulletproof)
+ max_non_bp_proofs += rv.p.rangeSigs.size();
+ }
- std::deque<bool> results(threads);
- tools::threadpool& tpool = tools::threadpool::getInstance();
- tools::threadpool::waiter waiter;
+ results.resize(max_non_bp_proofs);
+ for (const rctSig *rvp: rvv)
+ {
+ const rctSig &rv = *rvp;
- const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
+ const bool bulletproof = is_rct_bulletproof(rv.type);
+ const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
- if (semantics) {
key sumOutpks = identity();
for (size_t i = 0; i < rv.outPk.size(); i++) {
- addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask);
+ addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask);
}
DP(sumOutpks);
key txnFeeKey = scalarmultH(d2h(rv.txnFee));
@@ -969,50 +978,100 @@ namespace rct {
key sumPseudoOuts = identity();
for (size_t i = 0 ; i < pseudoOuts.size() ; i++) {
- addKeys(sumPseudoOuts, sumPseudoOuts, pseudoOuts[i]);
+ addKeys(sumPseudoOuts, sumPseudoOuts, pseudoOuts[i]);
}
DP(sumPseudoOuts);
//check pseudoOuts vs Outs..
if (!equalKeys(sumPseudoOuts, sumOutpks)) {
- LOG_PRINT_L1("Sum check failed");
- return false;
+ LOG_PRINT_L1("Sum check failed");
+ return false;
}
- results.clear();
- results.resize(bulletproof ? rv.p.bulletproofs.size() : rv.outPk.size());
if (bulletproof)
+ {
for (size_t i = 0; i < rv.p.bulletproofs.size(); i++)
- tpool.submit(&waiter, [&, i] { results[i] = verBulletproof(rv.p.bulletproofs[i]); });
+ proofs.push_back(&rv.p.bulletproofs[i]);
+ }
else
+ {
for (size_t i = 0; i < rv.p.rangeSigs.size(); i++)
- tpool.submit(&waiter, [&, i] { results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); });
- waiter.wait(&tpool);
-
- for (size_t i = 0; i < results.size(); ++i) {
- if (!results[i]) {
- LOG_PRINT_L1("Range proof verified failed for proof " << i);
- return false;
- }
+ tpool.submit(&waiter, [&, i, offset] { results[i+offset] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); });
+ offset += rv.p.rangeSigs.size();
}
}
- else {
- const key message = get_pre_mlsag_hash(rv, hw::get_device("default"));
-
- results.clear();
- results.resize(rv.mixRing.size());
- for (size_t i = 0 ; i < rv.mixRing.size() ; i++) {
- tpool.submit(&waiter, [&, i] {
- results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]);
- }, true);
+ if (!proofs.empty() && !verBulletproof(proofs))
+ {
+ LOG_PRINT_L1("Aggregate range proof verified failed");
+ return false;
+ }
+
+ waiter.wait(&tpool);
+ for (size_t i = 0; i < results.size(); ++i) {
+ if (!results[i]) {
+ LOG_PRINT_L1("Range proof verified failed for proof " << i);
+ return false;
}
- waiter.wait(&tpool);
+ }
- for (size_t i = 0; i < results.size(); ++i) {
- if (!results[i]) {
- LOG_PRINT_L1("verRctMGSimple failed for input " << i);
- return false;
- }
+ return true;
+ }
+ // we can get deep throws from ge_frombytes_vartime if input isn't valid
+ catch (const std::exception &e)
+ {
+ LOG_PRINT_L1("Error in verRctSemanticsSimple: " << e.what());
+ return false;
+ }
+ catch (...)
+ {
+ LOG_PRINT_L1("Error in verRctSemanticsSimple, but not an actual exception");
+ return false;
+ }
+ }
+
+ bool verRctSemanticsSimple(const rctSig & rv)
+ {
+ return verRctSemanticsSimple(std::vector<const rctSig*>(1, &rv));
+ }
+
+ //ver RingCT simple
+ //assumes only post-rct style inputs (at least for max anonymity)
+ bool verRctNonSemanticsSimple(const rctSig & rv) {
+ try
+ {
+ PERF_TIMER(verRctNonSemanticsSimple);
+
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctNonSemanticsSimple called on non simple rctSig");
+ const bool bulletproof = is_rct_bulletproof(rv.type);
+ // semantics check is early, and mixRing/MGs aren't resolved yet
+ if (bulletproof)
+ CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.p.pseudoOuts and mixRing");
+ else
+ CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing");
+
+ const size_t threads = std::max(rv.outPk.size(), rv.mixRing.size());
+
+ std::deque<bool> results(threads);
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
+
+ const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
+
+ const key message = get_pre_mlsag_hash(rv, hw::get_device("default"));
+
+ results.clear();
+ results.resize(rv.mixRing.size());
+ for (size_t i = 0 ; i < rv.mixRing.size() ; i++) {
+ tpool.submit(&waiter, [&, i] {
+ results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]);
+ });
+ }
+ waiter.wait(&tpool);
+
+ for (size_t i = 0; i < results.size(); ++i) {
+ if (!results[i]) {
+ LOG_PRINT_L1("verRctMGSimple failed for input " << i);
+ return false;
}
}
@@ -1021,12 +1080,12 @@ namespace rct {
// we can get deep throws from ge_frombytes_vartime if input isn't valid
catch (const std::exception &e)
{
- LOG_PRINT_L1("Error in verRct: " << e.what());
+ LOG_PRINT_L1("Error in verRctNonSemanticsSimple: " << e.what());
return false;
}
catch (...)
{
- LOG_PRINT_L1("Error in verRct, but not an actual exception");
+ LOG_PRINT_L1("Error in verRctNonSemanticsSimple, but not an actual exception");
return false;
}
}
diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h
index 9a66310c7..d1090ca77 100644
--- a/src/ringct/rctSigs.h
+++ b/src/ringct/rctSigs.h
@@ -125,8 +125,10 @@ namespace rct {
rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, const std::vector<unsigned int> & index, ctkeyV &outSk, RangeProofType range_proof_type, hw::device &hwdev);
bool verRct(const rctSig & rv, bool semantics);
static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); }
- bool verRctSimple(const rctSig & rv, bool semantics);
- static inline bool verRctSimple(const rctSig & rv) { return verRctSimple(rv, true) && verRctSimple(rv, false); }
+ bool verRctSemanticsSimple(const rctSig & rv);
+ bool verRctSemanticsSimple(const std::vector<const rctSig*> & rv);
+ bool verRctNonSemanticsSimple(const rctSig & rv);
+ static inline bool verRctSimple(const rctSig & rv) { return verRctSemanticsSimple(rv) && verRctNonSemanticsSimple(rv); }
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev);
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev);
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev);
diff --git a/tests/performance_tests/bulletproof.h b/tests/performance_tests/bulletproof.h
index b11c9fe97..7bb702c3d 100644
--- a/tests/performance_tests/bulletproof.h
+++ b/tests/performance_tests/bulletproof.h
@@ -60,3 +60,41 @@ public:
private:
rct::Bulletproof proof;
};
+
+template<bool batch, size_t start, size_t repeat, size_t mul, size_t add, size_t N>
+class test_aggregated_bulletproof
+{
+public:
+ static const size_t loop_count = 500 / (N * repeat);
+
+ bool init()
+ {
+ size_t o = start;
+ for (size_t n = 0; n < N; ++n)
+ {
+ //printf("adding %zu times %zu\n", repeat, o);
+ for (size_t i = 0; i < repeat; ++i)
+ proofs.push_back(rct::bulletproof_PROVE(std::vector<uint64_t>(o, 749327532984), rct::skvGen(o)));
+ o = o * mul + add;
+ }
+ return true;
+ }
+
+ bool test()
+ {
+ if (batch)
+ {
+ return rct::bulletproof_VERIFY(proofs);
+ }
+ else
+ {
+ for (const rct::Bulletproof &proof: proofs)
+ if (!rct::bulletproof_VERIFY(proof))
+ return false;
+ return true;
+ }
+ }
+
+private:
+ std::vector<rct::Bulletproof> proofs;
+};
diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp
index a00f05ce7..c125f9042 100644
--- a/tests/performance_tests/main.cpp
+++ b/tests/performance_tests/main.cpp
@@ -183,6 +183,17 @@ int main(int argc, char** argv)
TEST_PERFORMANCE2(filter, verbose, test_bulletproof, true, 15);
TEST_PERFORMANCE2(filter, verbose, test_bulletproof, false, 15);
+ TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 2, 1, 1, 0, 4);
+ TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 2, 1, 1, 0, 4);
+ TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 8, 1, 1, 0, 4);
+ TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 8, 1, 1, 0, 4);
+ TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 1, 1, 2, 0, 4);
+ TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 1, 1, 2, 0, 4);
+ TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 1, 8, 1, 1, 4);
+ TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 1, 8, 1, 1, 4);
+ TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 2, 1, 1, 0, 64);
+ TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 2, 1, 1, 0, 64);
+
TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 3, false);
TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 5, false);
TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 10, false);
diff --git a/tests/performance_tests/performance_tests.h b/tests/performance_tests/performance_tests.h
index 6f613e0a5..ce9baf61f 100644
--- a/tests/performance_tests/performance_tests.h
+++ b/tests/performance_tests/performance_tests.h
@@ -169,3 +169,5 @@ void run_test(const std::string &filter, bool verbose, const char* test_name)
#define TEST_PERFORMANCE2(filter, verbose, test_class, a0, a1) run_test< test_class<a0, a1> >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ">")
#define TEST_PERFORMANCE3(filter, verbose, test_class, a0, a1, a2) run_test< test_class<a0, a1, a2> >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ">")
#define TEST_PERFORMANCE4(filter, verbose, test_class, a0, a1, a2, a3) run_test< test_class<a0, a1, a2, a3> >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ", " QUOTEME(a3) ">")
+#define TEST_PERFORMANCE5(filter, verbose, test_class, a0, a1, a2, a3, a4) run_test< test_class<a0, a1, a2, a3, a4> >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ", " QUOTEME(a3) ", " QUOTEME(a4) ">")
+#define TEST_PERFORMANCE6(filter, verbose, test_class, a0, a1, a2, a3, a4, a5) run_test< test_class<a0, a1, a2, a3, a4, a5> >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ", " QUOTEME(a3) ", " QUOTEME(a4) ", " QUOTEME(a5) ">")
diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp
index 183bb5167..db14c050a 100644
--- a/tests/unit_tests/bulletproofs.cpp
+++ b/tests/unit_tests/bulletproofs.cpp
@@ -135,6 +135,25 @@ TEST(bulletproofs, multi_splitting)
}
}
+TEST(bulletproofs, valid_aggregated)
+{
+ static const size_t N_PROOFS = 8;
+ std::vector<rct::Bulletproof> proofs(N_PROOFS);
+ for (size_t n = 0; n < N_PROOFS; ++n)
+ {
+ size_t outputs = 2 + n;
+ std::vector<uint64_t> amounts;
+ rct::keyV gamma;
+ for (size_t i = 0; i < outputs; ++i)
+ {
+ amounts.push_back(crypto::rand<uint64_t>());
+ gamma.push_back(rct::skGen());
+ }
+ proofs[n] = bulletproof_PROVE(amounts, gamma);
+ }
+ ASSERT_TRUE(rct::bulletproof_VERIFY(proofs));
+}
+
TEST(bulletproofs, invalid_8)
{
diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp
index 6e3958f8a..d4e942176 100644
--- a/tests/unit_tests/ringct.cpp
+++ b/tests/unit_tests/ringct.cpp
@@ -1085,3 +1085,20 @@ TEST(ringct, zeroCommmit)
const rct::key manual = rct::addKeys(a, b);
ASSERT_EQ(z, manual);
}
+
+TEST(ringct, aggregated)
+{
+ static const size_t N_PROOFS = 16;
+ std::vector<rctSig> s(N_PROOFS);
+ std::vector<const rctSig*> sp(N_PROOFS);
+
+ for (size_t n = 0; n < N_PROOFS; ++n)
+ {
+ static const uint64_t inputs[] = {1000, 1000};
+ static const uint64_t outputs[] = {500, 1500};
+ s[n] = make_sample_simple_rct_sig(NELTS(inputs), inputs, NELTS(outputs), outputs, 0);
+ sp[n] = &s[n];
+ }
+
+ ASSERT_TRUE(verRctSemanticsSimple(sp));
+}