Extract CryptoNote crypto sources from upstream (fa1608cf). Build as static libcryptonote.a via CMake with compat stubs for external dependencies (warnings, logging, varint, profiling). 37 upstream files, 10 compat stubs, 680KB static library. Co-Authored-By: Charon <charon@lethean.io>
185 lines
5.6 KiB
C++
Executable file
185 lines
5.6 KiB
C++
Executable file
// Copyright (c) 2014-2018 Zano Project
|
||
// Copyright (c) 2014-2018 The Louisdor Project
|
||
// Copyright (c) 2012-2013 The Boolberry developers
|
||
// Copyright (c) 2017-2025 Lethean (https://lt.hn)
|
||
//
|
||
// Licensed under the European Union Public Licence (EUPL) version 1.2.
|
||
// You may obtain a copy of the licence at:
|
||
//
|
||
// https://joinup.ec.europa.eu/software/page/eupl/licence-eupl
|
||
//
|
||
// The EUPL is a copyleft licence that is compatible with the MIT/X11
|
||
// licence used by the original projects; the MIT terms are therefore
|
||
// considered “grandfathered” under the EUPL for this code.
|
||
//
|
||
// SPDX‑License‑Identifier: EUPL-1.2
|
||
//
|
||
#pragma once
|
||
|
||
// This file contains Multi-Scalar Multiplication routines
|
||
|
||
#include "epee/include/misc_log_ex.h"
|
||
#include "crypto-sugar.h"
|
||
|
||
namespace crypto
|
||
{
|
||
|
||
template<typename CT>
|
||
bool msm_and_check_zero_naive(const scalar_vec_t& g_scalars, const scalar_vec_t& h_scalars, const point_t& summand)
|
||
{
|
||
CHECK_AND_ASSERT_MES(g_scalars.size() <= CT::c_bpp_mn_max, false, "g_scalars oversized");
|
||
CHECK_AND_ASSERT_MES(h_scalars.size() <= CT::c_bpp_mn_max, false, "h_scalars oversized");
|
||
|
||
point_t result = summand;
|
||
|
||
for (size_t i = 0; i < g_scalars.size(); ++i)
|
||
result += g_scalars[i] * CT::get_generator(false, i);
|
||
|
||
for (size_t i = 0; i < h_scalars.size(); ++i)
|
||
result += h_scalars[i] * CT::get_generator(true, i);
|
||
|
||
if (!result.is_zero())
|
||
{
|
||
LOG_PRINT_L0("msm result is non zero: " << result);
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
|
||
// https://eprint.iacr.org/2022/999.pdf
|
||
// "Pippenger algorithm [1], and its variant that is widely used in the ZK space is called the bucket method"
|
||
template<typename CT>
|
||
bool msm_and_check_zero_pippenger_v3(const scalar_vec_t& g_scalars, const scalar_vec_t& h_scalars, const point_t& summand, uint8_t c)
|
||
{
|
||
// TODO: with c = 8 and with direct access got much worse result than with c = 7 and get_bits() for N = 128..256, consider checking again for bigger datasets (N>256)
|
||
// TODO: consider preparing a cached generators' points
|
||
|
||
CHECK_AND_ASSERT_MES(g_scalars.size() <= CT::c_bpp_mn_max, false, "g_scalars oversized");
|
||
CHECK_AND_ASSERT_MES(h_scalars.size() <= CT::c_bpp_mn_max, false, "h_scalars oversized");
|
||
CHECK_AND_ASSERT_MES(c < 10, false, "c is too big");
|
||
|
||
size_t C = 1ull << c;
|
||
|
||
// k_max * c + (c-1) >= max_bit_idx
|
||
//
|
||
// max_bit_idx - (c - 1) max_bit_idx - (c - 1) + (c - 1) max_bit_idx
|
||
// k_max = ceil ( --------------------- ) = floor ( ------------------------------ ) = floor ( ----------- )
|
||
// c c c
|
||
const size_t b = 253; // the maximum number of bits in x https://eprint.iacr.org/2022/999.pdf TODO: we may also scan for maximum bit used in all the scalars if all the scalars are small
|
||
const size_t max_bit_idx = b - 1;
|
||
const size_t k_max = max_bit_idx / c;
|
||
const size_t K = k_max + 1;
|
||
|
||
std::vector<point_t> buckets(C * K);
|
||
std::vector<bool> buckets_inited(C * K);
|
||
std::vector<point_t> Sk(K);
|
||
std::vector<bool> Sk_inited(K);
|
||
std::vector<point_t> Gk(K);
|
||
std::vector<bool> Gk_inited(K);
|
||
|
||
// first loop, calculate partial bucket sums
|
||
for (size_t n = 0; n < g_scalars.size(); ++n)
|
||
{
|
||
for (size_t k = 0; k < K; ++k)
|
||
{
|
||
uint64_t l = g_scalars[n].get_bits((uint8_t)(k * c), c); // l in [0; 2^c-1]
|
||
if (l != 0)
|
||
{
|
||
size_t bucket_id = l * K + k;
|
||
if (buckets_inited[bucket_id])
|
||
buckets[bucket_id] += CT::get_generator(false, n);
|
||
else
|
||
{
|
||
buckets[bucket_id] = CT::get_generator(false, n);
|
||
buckets_inited[bucket_id] = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// still the first loop (continued)
|
||
for (size_t n = 0; n < h_scalars.size(); ++n)
|
||
{
|
||
for (size_t k = 0; k < K; ++k)
|
||
{
|
||
uint64_t l = h_scalars[n].get_bits((uint8_t)(k * c), c); // l in [0; 2^c-1]
|
||
if (l != 0)
|
||
{
|
||
size_t bucket_id = l * K + k;
|
||
if (buckets_inited[bucket_id])
|
||
buckets[bucket_id] += CT::get_generator(true, n);
|
||
else
|
||
{
|
||
buckets[bucket_id] = CT::get_generator(true, n);
|
||
buckets_inited[bucket_id] = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// the second loop
|
||
for (size_t l = C - 1; l > 0; --l)
|
||
{
|
||
for (size_t k = 0; k < K; ++k)
|
||
{
|
||
size_t bucket_id = l * K + k;
|
||
if (buckets_inited[bucket_id])
|
||
{
|
||
if (Sk_inited[k])
|
||
Sk[k] += buckets[bucket_id];
|
||
else
|
||
{
|
||
Sk[k] = buckets[bucket_id];
|
||
Sk_inited[k] = true;
|
||
}
|
||
}
|
||
|
||
if (Sk_inited[k])
|
||
{
|
||
if (Gk_inited[k])
|
||
Gk[k] += Sk[k];
|
||
else
|
||
{
|
||
Gk[k] = Sk[k];
|
||
Gk_inited[k] = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// the third loop: Horner’s rule
|
||
point_t result = Gk_inited[K - 1] ? Gk[K - 1] : c_point_0;
|
||
for (size_t k = K - 2; k != SIZE_MAX; --k)
|
||
{
|
||
result.modify_mul_pow_2(c);
|
||
if (Gk_inited[k])
|
||
result += Gk[k];
|
||
}
|
||
|
||
result += summand;
|
||
|
||
if (!result.is_zero())
|
||
{
|
||
LOG_PRINT_L0("multiexp result is non zero: " << result);
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
// Just switcher
|
||
|
||
template<typename CT>
|
||
bool msm_and_check_zero(const scalar_vec_t& g_scalars, const scalar_vec_t& h_scalars, const point_t& summand)
|
||
{
|
||
//return msm_and_check_zero_naive<CT>(g_scalars, h_scalars, summand);
|
||
return msm_and_check_zero_pippenger_v3<CT>(g_scalars, h_scalars, summand, 7);
|
||
}
|
||
|
||
|
||
|
||
} // namespace crypto
|