mirror of
https://github.com/monero-project/monero.git
synced 2026-07-28 14:47:15 -07:00
RingCT crypto: 6x faster zero commit
This commit is contained in:
@@ -233,7 +233,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti
|
||||
if (miner_tx && tx.version == 2)
|
||||
{
|
||||
cryptonote::tx_out vout = tx.vout[i];
|
||||
rct::key commitment = rct::zeroCommit(vout.amount);
|
||||
rct::key commitment = rct::zeroCommitVartime(vout.amount);
|
||||
vout.amount = 0;
|
||||
amount_output_indices[i] = add_output(tx_hash, vout, i, tx.unlock_time,
|
||||
&commitment);
|
||||
|
||||
@@ -3457,7 +3457,7 @@ output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint6
|
||||
const pre_rct_outkey *okp = (const pre_rct_outkey *)v.mv_data;
|
||||
memcpy(&ret, &okp->data, sizeof(pre_rct_output_data_t));
|
||||
if (include_commitmemt)
|
||||
ret.commitment = rct::zeroCommit(amount);
|
||||
ret.commitment = rct::zeroCommitVartime(amount);
|
||||
}
|
||||
TXN_POSTFIX_RDONLY();
|
||||
return ret;
|
||||
@@ -4203,7 +4203,7 @@ void BlockchainLMDB::get_output_key(const epee::span<const uint64_t> &amounts, c
|
||||
outputs.resize(outputs.size() + 1);
|
||||
output_data_t &data = outputs.back();
|
||||
memcpy(&data, &okp->data, sizeof(pre_rct_output_data_t));
|
||||
data.commitment = rct::zeroCommit(amount);
|
||||
data.commitment = rct::zeroCommitVartime(amount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ static void fe_sq(fe, const fe);
|
||||
static void ge_madd(ge_p1p1 *, const ge_p3 *, const ge_precomp *);
|
||||
static void ge_msub(ge_p1p1 *, const ge_p3 *, const ge_precomp *);
|
||||
static void ge_p2_0(ge_p2 *);
|
||||
static void ge_p3_dbl(ge_p1p1 *, const ge_p3 *);
|
||||
static void fe_divpowm1(fe, const fe, const fe);
|
||||
|
||||
/* Common functions */
|
||||
@@ -1529,7 +1528,7 @@ static void ge_p3_0(ge_p3 *h) {
|
||||
r = 2 * p
|
||||
*/
|
||||
|
||||
static void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p) {
|
||||
void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p) {
|
||||
ge_p2 q;
|
||||
ge_p3_to_p2(&q, p);
|
||||
ge_p2_dbl(r, &q);
|
||||
|
||||
@@ -102,6 +102,10 @@ void ge_p1p1_to_p3(ge_p3 *, const ge_p1p1 *);
|
||||
|
||||
void ge_p2_dbl(ge_p1p1 *, const ge_p2 *);
|
||||
|
||||
/* From ge_p3_dbl.c */
|
||||
|
||||
void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p);
|
||||
|
||||
/* From ge_p3_to_cached.c */
|
||||
|
||||
extern const fe fe_d2;
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace cryptonote
|
||||
rct::key mask; //ringct amount mask
|
||||
rct::multisig_kLRki multisig_kLRki; //multisig info
|
||||
|
||||
void push_output(uint64_t idx, const crypto::public_key &k, uint64_t amount) { outputs.push_back(std::make_pair(idx, rct::ctkey({rct::pk2rct(k), rct::zeroCommit(amount)}))); }
|
||||
void push_output(uint64_t idx, const crypto::public_key &k, uint64_t amount) { outputs.push_back(std::make_pair(idx, rct::ctkey({rct::pk2rct(k), rct::zeroCommitVartime(amount)}))); }
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(outputs)
|
||||
|
||||
+43
-4
@@ -29,6 +29,7 @@
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "crypto/generators.h"
|
||||
#include "misc_log_ex.h"
|
||||
#include "rctOps.h"
|
||||
using namespace crypto;
|
||||
@@ -216,6 +217,33 @@ static const zero_commitment zero_commitments[] = {
|
||||
{ (uint64_t)10000000000000000000ull, {{0x65, 0x8d, 0x1, 0x37, 0x6d, 0x18, 0x63, 0xe7, 0x7b, 0x9, 0x6f, 0x98, 0xe6, 0xe5, 0x13, 0xc2, 0x4, 0x10, 0xf5, 0xc7, 0xfb, 0x18, 0xa6, 0xe5, 0x9a, 0x52, 0x66, 0x84, 0x5c, 0xd9, 0xb1, 0xe3}} },
|
||||
};
|
||||
|
||||
static constexpr std::size_t H_TABLE_SIZE = 64;
|
||||
const std::vector<ge_cached>& H_TABLE()
|
||||
{
|
||||
struct static_h_table
|
||||
{
|
||||
std::vector<ge_cached> h_table;
|
||||
static_h_table()
|
||||
: h_table()
|
||||
{
|
||||
h_table.resize(H_TABLE_SIZE);
|
||||
|
||||
ge_p3_to_cached(&h_table.at(0), &ge_p3_H);
|
||||
ge_p3 H_bit_p3 = ge_p3_H;
|
||||
|
||||
for (std::size_t i = 1; i < H_TABLE_SIZE; ++i)
|
||||
{
|
||||
ge_p1p1 H_bit_p1p1;
|
||||
ge_p3_dbl(&H_bit_p1p1, &H_bit_p3);
|
||||
ge_p1p1_to_p3(&H_bit_p3, &H_bit_p1p1);
|
||||
ge_p3_to_cached(&h_table.at(i), &H_bit_p3);
|
||||
}
|
||||
}
|
||||
};
|
||||
static const static_h_table out;
|
||||
return out.h_table;
|
||||
}
|
||||
|
||||
namespace rct {
|
||||
|
||||
//Various key initialization functions
|
||||
@@ -318,7 +346,7 @@ namespace rct {
|
||||
return make_tuple(sk, pk);
|
||||
}
|
||||
|
||||
key zeroCommit(xmr_amount amount) {
|
||||
key zeroCommitVartime(xmr_amount amount) {
|
||||
const zero_commitment *begin = zero_commitments;
|
||||
const zero_commitment *end = zero_commitments + sizeof(zero_commitments) / sizeof(zero_commitments[0]);
|
||||
const zero_commitment value{amount, rct::zero()};
|
||||
@@ -327,9 +355,20 @@ namespace rct {
|
||||
{
|
||||
return it->commitment;
|
||||
}
|
||||
key am = d2h(amount);
|
||||
key bH = scalarmultH(am);
|
||||
return addKeys(G, bH);
|
||||
ge_p3 res_ge_p3 = get_G_p3();
|
||||
static_assert(sizeof(xmr_amount) * 8 == H_TABLE_SIZE, "unexpected size of h table");
|
||||
for (size_t i = 0; i < H_TABLE_SIZE; ++i)
|
||||
{
|
||||
if (amount & (xmr_amount(1) << i))
|
||||
{
|
||||
ge_p1p1 p1p1;
|
||||
ge_add(&p1p1, &res_ge_p3, &H_TABLE()[i]);
|
||||
ge_p1p1_to_p3(&res_ge_p3, &p1p1);
|
||||
}
|
||||
}
|
||||
rct::key res;
|
||||
ge_p3_tobytes(res.bytes, &res_ge_p3);
|
||||
return res;
|
||||
}
|
||||
|
||||
key commit(xmr_amount amount, const key &mask) {
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ namespace rct {
|
||||
// make a pedersen commitment with given key
|
||||
key commit(xmr_amount amount, const key &mask);
|
||||
// make a pedersen commitment with zero key
|
||||
key zeroCommit(xmr_amount amount);
|
||||
key zeroCommitVartime(xmr_amount amount);
|
||||
//generates a random uint long long
|
||||
xmr_amount randXmrAmount(xmr_amount upperlimit);
|
||||
|
||||
|
||||
@@ -9550,7 +9550,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
|
||||
size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0);
|
||||
outs.push_back(std::vector<get_outs_entry>());
|
||||
outs.back().reserve(fake_outputs_count + 1);
|
||||
const rct::key mask = td.is_rct() ? rct::commit(td.amount(), td.m_mask) : rct::zeroCommit(td.amount());
|
||||
const rct::key mask = td.is_rct() ? rct::commit(td.amount(), td.m_mask) : rct::zeroCommitVartime(td.amount());
|
||||
|
||||
uint64_t num_outs = 0;
|
||||
const uint64_t amount = td.is_rct() ? 0 : td.amount();
|
||||
@@ -9659,7 +9659,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
|
||||
{
|
||||
const transfer_details &td = m_transfers[idx];
|
||||
std::vector<get_outs_entry> v;
|
||||
const rct::key mask = td.is_rct() ? rct::commit(td.amount(), td.m_mask) : rct::zeroCommit(td.amount());
|
||||
const rct::key mask = td.is_rct() ? rct::commit(td.amount(), td.m_mask) : rct::zeroCommitVartime(td.amount());
|
||||
v.push_back(std::make_tuple(td.m_global_output_index, td.get_public_key(), mask));
|
||||
outs.push_back(v);
|
||||
}
|
||||
|
||||
@@ -594,7 +594,7 @@ bool fill_tx_sources(std::vector<tx_source_entry>& sources, const std::vector<te
|
||||
ts.rct = false;
|
||||
ts.mask = rct::identity(); // non-rct has identity mask by definition
|
||||
|
||||
rct::key comm = rct::zeroCommit(ts.amount);
|
||||
rct::key comm = rct::zeroCommitVartime(ts.amount);
|
||||
for(auto & ot : ts.outputs)
|
||||
ot.second.mask = comm;
|
||||
|
||||
|
||||
@@ -296,7 +296,7 @@ bool gen_rct_tx_pre_rct_bad_real_mask::generate(std::vector<test_event_entry>& e
|
||||
const int out_idx[] = {0, -1};
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, out_idx, mixin, amount_paid, false,
|
||||
[](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {sources[0].outputs[0].second.mask = rct::zeroCommit(99999);},
|
||||
[](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {sources[0].outputs[0].second.mask = rct::zeroCommitVartime(99999);},
|
||||
NULL);
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ bool gen_rct_tx_pre_rct_bad_fake_mask::generate(std::vector<test_event_entry>& e
|
||||
const int out_idx[] = {0, -1};
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, out_idx, mixin, amount_paid, false,
|
||||
[](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {sources[0].outputs[1].second.mask = rct::zeroCommit(99999);},
|
||||
[](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {sources[0].outputs[1].second.mask = rct::zeroCommitVartime(99999);},
|
||||
NULL);
|
||||
}
|
||||
|
||||
@@ -339,7 +339,7 @@ bool gen_rct_tx_rct_bad_real_mask::generate(std::vector<test_event_entry>& event
|
||||
const int out_idx[] = {1, -1};
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, out_idx, mixin, amount_paid, false,
|
||||
[](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {sources[0].outputs[0].second.mask = rct::zeroCommit(99999);},
|
||||
[](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {sources[0].outputs[0].second.mask = rct::zeroCommitVartime(99999);},
|
||||
NULL);
|
||||
}
|
||||
|
||||
@@ -359,7 +359,7 @@ bool gen_rct_tx_rct_bad_fake_mask::generate(std::vector<test_event_entry>& event
|
||||
const int out_idx[] = {1, -1};
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, out_idx, mixin, amount_paid, false,
|
||||
[](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {sources[0].outputs[1].second.mask = rct::zeroCommit(99999);},
|
||||
[](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {sources[0].outputs[1].second.mask = rct::zeroCommitVartime(99999);},
|
||||
NULL);
|
||||
}
|
||||
|
||||
@@ -369,7 +369,7 @@ bool gen_rct_tx_rct_spend_with_zero_commit::generate(std::vector<test_event_entr
|
||||
const int out_idx[] = {1, -1};
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, out_idx, mixin, amount_paid, false,
|
||||
[](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {sources[0].outputs[0].second.mask = rct::zeroCommit(sources[0].amount); sources[0].mask = rct::identity();},
|
||||
[](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {sources[0].outputs[0].second.mask = rct::zeroCommitVartime(sources[0].amount); sources[0].mask = rct::identity();},
|
||||
[](transaction &tx){boost::get<txin_to_key>(tx.vin[0]).amount = 0;});
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,8 @@ set(performance_tests_headers
|
||||
multi_tx_test_base.h
|
||||
performance_tests.h
|
||||
performance_utils.h
|
||||
single_tx_test_base.h)
|
||||
single_tx_test_base.h
|
||||
zero_commit.h)
|
||||
|
||||
monero_add_minimal_executable(performance_tests
|
||||
${performance_tests_sources}
|
||||
|
||||
@@ -128,8 +128,8 @@ public:
|
||||
case op_addKeys_aGbBcC: rct::addKeys_aGbBcC(key, scalar0, scalar1, precomp1, scalar2, precomp2); break;
|
||||
case op_addKeys_aAbBcC: rct::addKeys_aAbBcC(key, scalar0, precomp0, scalar1, precomp1, scalar2, precomp2); break;
|
||||
case op_isInMainSubgroup: rct::isInMainSubgroup(point0); break;
|
||||
case op_zeroCommitUncached: rct::zeroCommit(9001); break;
|
||||
case op_zeroCommitCached: rct::zeroCommit(9000); break;
|
||||
case op_zeroCommitUncached: rct::zeroCommitVartime(9001); break;
|
||||
case op_zeroCommitCached: rct::zeroCommitVartime(9000); break;
|
||||
default: return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
#include "multiexp.h"
|
||||
#include "sig_mlsag.h"
|
||||
#include "sig_clsag.h"
|
||||
#include "zero_commit.h"
|
||||
|
||||
namespace po = boost::program_options;
|
||||
|
||||
@@ -594,6 +595,9 @@ int main(int argc, char** argv)
|
||||
TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 9);
|
||||
#endif
|
||||
|
||||
TEST_PERFORMANCE1(filter, p, test_zero_commit, true); // fast
|
||||
TEST_PERFORMANCE1(filter, p, test_zero_commit, false);
|
||||
|
||||
std::cout << "Tests finished. Elapsed time: " << timer.elapsed_ms() / 1000 << " sec" << std::endl;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
return false;
|
||||
|
||||
txout_to_key tx_out = boost::get<txout_to_key>(m_miner_txs[i].vout[0].target);
|
||||
output_entries.push_back(std::make_pair(i, rct::ctkey({rct::pk2rct(tx_out.key), rct::zeroCommit(m_miner_txs[i].vout[0].amount)})));
|
||||
output_entries.push_back(std::make_pair(i, rct::ctkey({rct::pk2rct(tx_out.key), rct::zeroCommitVartime(m_miner_txs[i].vout[0].amount)})));
|
||||
m_public_keys[i] = tx_out.key;
|
||||
m_public_key_ptrs[i] = &m_public_keys[i];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) 2025, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "crypto/random.h"
|
||||
#include "ringct/rctOps.h"
|
||||
|
||||
template<bool fast>
|
||||
class test_zero_commit
|
||||
{
|
||||
public:
|
||||
static const size_t loop_count = 1000;
|
||||
|
||||
bool init()
|
||||
{
|
||||
m_amount = crypto::rand<uint64_t>();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test()
|
||||
{
|
||||
if constexpr (fast)
|
||||
{
|
||||
rct::zeroCommitVartime(m_amount);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Old way of doing it
|
||||
rct::key am = rct::d2h(m_amount);
|
||||
rct::key bH = rct::scalarmultH(am);
|
||||
rct::addKeys(rct::G, bH);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t m_amount;
|
||||
};
|
||||
+10
-10
@@ -1204,14 +1204,14 @@ TEST(ringct, key_ostream)
|
||||
TEST(ringct, zeroCommmit)
|
||||
{
|
||||
static const uint64_t amount = crypto::rand<uint64_t>();
|
||||
const rct::key z = rct::zeroCommit(amount);
|
||||
const rct::key z = rct::zeroCommitVartime(amount);
|
||||
const rct::key a = rct::scalarmultBase(rct::identity());
|
||||
const rct::key b = rct::scalarmultH(rct::d2h(amount));
|
||||
const rct::key manual = rct::addKeys(a, b);
|
||||
ASSERT_EQ(z, manual);
|
||||
}
|
||||
|
||||
static rct::key uncachedZeroCommit(uint64_t amount)
|
||||
static rct::key uncachedZeroCommitVartime(uint64_t amount)
|
||||
{
|
||||
const rct::key am = rct::d2h(amount);
|
||||
const rct::key bH = rct::scalarmultH(am);
|
||||
@@ -1220,14 +1220,14 @@ static rct::key uncachedZeroCommit(uint64_t amount)
|
||||
|
||||
TEST(ringct, zeroCommitCache)
|
||||
{
|
||||
ASSERT_EQ(rct::zeroCommit(0), uncachedZeroCommit(0));
|
||||
ASSERT_EQ(rct::zeroCommit(1), uncachedZeroCommit(1));
|
||||
ASSERT_EQ(rct::zeroCommit(2), uncachedZeroCommit(2));
|
||||
ASSERT_EQ(rct::zeroCommit(10), uncachedZeroCommit(10));
|
||||
ASSERT_EQ(rct::zeroCommit(200), uncachedZeroCommit(200));
|
||||
ASSERT_EQ(rct::zeroCommit(1000000000), uncachedZeroCommit(1000000000));
|
||||
ASSERT_EQ(rct::zeroCommit(3000000000000), uncachedZeroCommit(3000000000000));
|
||||
ASSERT_EQ(rct::zeroCommit(900000000000000), uncachedZeroCommit(900000000000000));
|
||||
ASSERT_EQ(rct::zeroCommitVartime(0), uncachedZeroCommitVartime(0));
|
||||
ASSERT_EQ(rct::zeroCommitVartime(1), uncachedZeroCommitVartime(1));
|
||||
ASSERT_EQ(rct::zeroCommitVartime(2), uncachedZeroCommitVartime(2));
|
||||
ASSERT_EQ(rct::zeroCommitVartime(10), uncachedZeroCommitVartime(10));
|
||||
ASSERT_EQ(rct::zeroCommitVartime(200), uncachedZeroCommitVartime(200));
|
||||
ASSERT_EQ(rct::zeroCommitVartime(1000000000), uncachedZeroCommitVartime(1000000000));
|
||||
ASSERT_EQ(rct::zeroCommitVartime(3000000000000), uncachedZeroCommitVartime(3000000000000));
|
||||
ASSERT_EQ(rct::zeroCommitVartime(900000000000000), uncachedZeroCommitVartime(900000000000000));
|
||||
}
|
||||
|
||||
TEST(ringct, H)
|
||||
|
||||
Reference in New Issue
Block a user