mirror of
https://github.com/monero-project/monero.git
synced 2024-11-17 16:27:39 +00:00
bulletproofs: add aggregated verification
Ported from sarang's java code
This commit is contained in:
parent
e895c3def1
commit
bacf0a1e2f
11 changed files with 453 additions and 259 deletions
|
@ -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!");
|
MERROR_VER("Failed to check ringct signatures!");
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -896,7 +896,7 @@ namespace cryptonote
|
||||||
return false;
|
return false;
|
||||||
case rct::RCTTypeSimple:
|
case rct::RCTTypeSimple:
|
||||||
case rct::RCTTypeSimpleBulletproof:
|
case rct::RCTTypeSimpleBulletproof:
|
||||||
if (!rct::verRctSimple(rv, true))
|
if (!rct::verRctSemanticsSimple(rv))
|
||||||
{
|
{
|
||||||
MERROR_VER("rct signature semantics check failed");
|
MERROR_VER("rct signature semantics check failed");
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -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);
|
||||||
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);
|
sc_mul(tmp.bytes, tmp.bytes, x_ip.bytes);
|
||||||
tmp = rct::scalarmultKey(rct::H, tmp);
|
sc_muladd(z3.bytes, tmp.bytes, weight.bytes, z3.bytes);
|
||||||
rct::addKeys(tmp, tmp, inner_prod);
|
PERF_TIMER_STOP(VERIFY_line_26_new);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 rct::keyV &v, const rct::keyV &gamma);
|
||||||
Bulletproof bulletproof_PROVE(const std::vector<uint64_t> &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 Bulletproof &proof);
|
||||||
|
bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs);
|
||||||
|
bool bulletproof_VERIFY(const std::vector<Bulletproof> &proofs);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,13 @@ namespace rct {
|
||||||
catch (...) { return false; }
|
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)
|
//Borromean (c.f. gmax/andytoshi's paper)
|
||||||
boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) {
|
boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) {
|
||||||
key64 L[2], alpha;
|
key64 L[2], alpha;
|
||||||
|
@ -918,15 +925,23 @@ namespace rct {
|
||||||
|
|
||||||
//ver RingCT simple
|
//ver RingCT simple
|
||||||
//assumes only post-rct style inputs (at least for max anonymity)
|
//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
|
try
|
||||||
{
|
{
|
||||||
PERF_TIMER(verRctSimple);
|
PERF_TIMER(verRctSemanticsSimple);
|
||||||
|
|
||||||
CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctSimple called on non simple rctSig");
|
tools::threadpool& tpool = tools::threadpool::getInstance();
|
||||||
const bool bulletproof = is_rct_bulletproof(rv.type);
|
tools::threadpool::waiter waiter;
|
||||||
if (semantics)
|
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)
|
if (bulletproof)
|
||||||
{
|
{
|
||||||
CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs");
|
CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs");
|
||||||
|
@ -940,25 +955,19 @@ namespace rct {
|
||||||
CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.empty(), false, "rv.p.pseudoOuts is not empty");
|
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");
|
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
|
||||||
|
|
||||||
|
if (!bulletproof)
|
||||||
|
max_non_bp_proofs += rv.p.rangeSigs.size();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
results.resize(max_non_bp_proofs);
|
||||||
|
for (const rctSig *rvp: rvv)
|
||||||
{
|
{
|
||||||
// semantics check is early, and mixRing/MGs aren't resolved yet
|
const rctSig &rv = *rvp;
|
||||||
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 bool bulletproof = is_rct_bulletproof(rv.type);
|
||||||
const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
|
const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
|
||||||
|
|
||||||
if (semantics) {
|
|
||||||
key sumOutpks = identity();
|
key sumOutpks = identity();
|
||||||
for (size_t i = 0; i < rv.outPk.size(); i++) {
|
for (size_t i = 0; i < rv.outPk.size(); i++) {
|
||||||
addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask);
|
addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask);
|
||||||
|
@ -979,24 +988,75 @@ namespace rct {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
results.clear();
|
|
||||||
results.resize(bulletproof ? rv.p.bulletproofs.size() : rv.outPk.size());
|
|
||||||
if (bulletproof)
|
if (bulletproof)
|
||||||
|
{
|
||||||
for (size_t i = 0; i < rv.p.bulletproofs.size(); i++)
|
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
|
else
|
||||||
|
{
|
||||||
for (size_t i = 0; i < rv.p.rangeSigs.size(); i++)
|
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]); });
|
tpool.submit(&waiter, [&, i, offset] { results[i+offset] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); });
|
||||||
waiter.wait(&tpool);
|
offset += rv.p.rangeSigs.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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) {
|
for (size_t i = 0; i < results.size(); ++i) {
|
||||||
if (!results[i]) {
|
if (!results[i]) {
|
||||||
LOG_PRINT_L1("Range proof verified failed for proof " << i);
|
LOG_PRINT_L1("Range proof verified failed for proof " << i);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
// 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"));
|
const key message = get_pre_mlsag_hash(rv, hw::get_device("default"));
|
||||||
|
|
||||||
results.clear();
|
results.clear();
|
||||||
|
@ -1004,7 +1064,7 @@ namespace rct {
|
||||||
for (size_t i = 0 ; i < rv.mixRing.size() ; i++) {
|
for (size_t i = 0 ; i < rv.mixRing.size() ; i++) {
|
||||||
tpool.submit(&waiter, [&, i] {
|
tpool.submit(&waiter, [&, i] {
|
||||||
results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]);
|
results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]);
|
||||||
}, true);
|
});
|
||||||
}
|
}
|
||||||
waiter.wait(&tpool);
|
waiter.wait(&tpool);
|
||||||
|
|
||||||
|
@ -1014,19 +1074,18 @@ namespace rct {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// we can get deep throws from ge_frombytes_vartime if input isn't valid
|
// we can get deep throws from ge_frombytes_vartime if input isn't valid
|
||||||
catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
LOG_PRINT_L1("Error in verRct: " << e.what());
|
LOG_PRINT_L1("Error in verRctNonSemanticsSimple: " << e.what());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
catch (...)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
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);
|
bool verRct(const rctSig & rv, bool semantics);
|
||||||
static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); }
|
static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); }
|
||||||
bool verRctSimple(const rctSig & rv, bool semantics);
|
bool verRctSemanticsSimple(const rctSig & rv);
|
||||||
static inline bool verRctSimple(const rctSig & rv) { return verRctSimple(rv, true) && verRctSimple(rv, false); }
|
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, key & mask, hw::device &hwdev);
|
||||||
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, 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);
|
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev);
|
||||||
|
|
|
@ -60,3 +60,41 @@ public:
|
||||||
private:
|
private:
|
||||||
rct::Bulletproof proof;
|
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;
|
||||||
|
};
|
||||||
|
|
|
@ -183,6 +183,17 @@ int main(int argc, char** argv)
|
||||||
TEST_PERFORMANCE2(filter, verbose, test_bulletproof, true, 15);
|
TEST_PERFORMANCE2(filter, verbose, test_bulletproof, true, 15);
|
||||||
TEST_PERFORMANCE2(filter, verbose, test_bulletproof, false, 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, 3, false);
|
||||||
TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 5, false);
|
TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 5, false);
|
||||||
TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 10, false);
|
TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 10, false);
|
||||||
|
|
|
@ -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_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_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_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) ">")
|
||||||
|
|
|
@ -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)
|
TEST(bulletproofs, invalid_8)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1085,3 +1085,20 @@ TEST(ringct, zeroCommmit)
|
||||||
const rct::key manual = rct::addKeys(a, b);
|
const rct::key manual = rct::addKeys(a, b);
|
||||||
ASSERT_EQ(z, manual);
|
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));
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue