diff --git a/src/crypto/crypto-sugar.h b/src/crypto/crypto-sugar.h index 7e0d881f..70e5a210 100644 --- a/src/crypto/crypto-sugar.h +++ b/src/crypto/crypto-sugar.h @@ -517,6 +517,24 @@ namespace crypto m_u64[bit_index >> 6] &= ~(1ull << (bit_index & 63)); } + // the result is guaranteed to be within [ 0; 2 ^ bits_count ) + uint64_t get_bits(uint8_t bit_index_first, uint8_t bits_count) const + { + if (bits_count == 0 || bits_count > 64) + return 0; + uint8_t bits_count_m_1 = bits_count - 1; + unsigned int bit_index_last = bit_index_first + bits_count_m_1; + if (bit_index_last > 255) + bit_index_last = 255; + + uint64_t result = m_u64[bit_index_first >> 6] >> (bit_index_first & 63); + if (bits_count_m_1 > (bit_index_last & 63)) + result |= m_u64[bit_index_last >> 6] << (bits_count_m_1 - (bit_index_last & 63)); + + uint64_t result_mask = ((1ull << bits_count_m_1) - 1) << 1 | 1; // (just because 1ull << 64 in undefined behaviour, not a 0 as one would expect) + return result & result_mask; + } + // does not reduce static scalar_t power_of_2(uint8_t exponent) { @@ -746,6 +764,24 @@ namespace crypto return *this; } + point_t modify_mul_pow_2(size_t power) + { + if (power > 0) + { + ge_p1p1 p1; + ge_p2 p2; + ge_p3_to_p2(&p2, &m_p3); + for (size_t i = 1; i < power; ++i) + { + ge_p2_dbl(&p1, &p2); + ge_p1p1_to_p2(&p2, &p1); + } + ge_p2_dbl(&p1, &p2); + ge_p1p1_to_p3(&m_p3, &p1); + } + return *this; + } + // returns a * this + G point_t mul_plus_G(const scalar_t& a) const { diff --git a/src/crypto/msm.cpp b/src/crypto/msm.cpp new file mode 100644 index 00000000..628d905a --- /dev/null +++ b/src/crypto/msm.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2023-2023 Zano Project +// Copyright (c) 2023-2023 sowle (val@zano.org, crypto.sowle@gmail.com) +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// +#include "epee/include/misc_log_ex.h" +//#include "zarcanum.h" +#include "msm.h" +//#include "../currency_core/crypto_config.h" // TODO: move it to the crypto +//#include "../common/crypto_stream_operators.h" // TODO: move it to the crypto + +#if 0 +# define DBG_VAL_PRINT(x) std::cout << std::setw(30) << std::left << #x ": " << x << std::endl +# define DBG_PRINT(x) std::cout << x << std::endl +#else +# define DBG_VAL_PRINT(x) (void(0)) +# define DBG_PRINT(x) (void(0)) +#endif + +namespace crypto +{ + + + +} // namespace crypto diff --git a/src/crypto/msm.h b/src/crypto/msm.h new file mode 100644 index 00000000..218b55f6 --- /dev/null +++ b/src/crypto/msm.h @@ -0,0 +1,173 @@ +// Copyright (c) 2023-2023 Zano Project (https://zano.org/) +// Copyright (c) 2023-2023 sowle (val@zano.org, crypto.sowle@gmail.com) +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#pragma once + +// This file contains Multi-Scalar Multiplication routines + +#include "epee/include/misc_log_ex.h" +#include "crypto-sugar.h" + +namespace crypto +{ + + template + 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 + 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 buckets(C * K); + std::vector buckets_inited(C * K); + std::vector Sk(K); + std::vector Sk_inited(K); + std::vector Gk(K); + std::vector 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 + 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(g_scalars, h_scalars, summand); + return msm_and_check_zero_pippenger_v3(g_scalars, h_scalars, summand, 7); + } + + + +} // namespace crypto diff --git a/src/crypto/range_proof_bpp.h b/src/crypto/range_proof_bpp.h index 77dcb1a7..db1ffa1a 100644 --- a/src/crypto/range_proof_bpp.h +++ b/src/crypto/range_proof_bpp.h @@ -716,7 +716,7 @@ namespace crypto point_t GH_exponents = c_point_0; CT::calc_pedersen_commitment(G_scalar, H_scalar, GH_exponents); - bool result = multiexp_and_check_being_zero(g_scalars, h_scalars, summand + GH_exponents); + bool result = msm_and_check_zero(g_scalars, h_scalars, summand + GH_exponents); if (result) DBG_PRINT(ENDL << " . . . . bpp_verify() -- SUCCEEDED!!!" << ENDL); return result; diff --git a/src/crypto/range_proof_bppe.h b/src/crypto/range_proof_bppe.h index 260132b0..de786541 100644 --- a/src/crypto/range_proof_bppe.h +++ b/src/crypto/range_proof_bppe.h @@ -734,7 +734,7 @@ namespace crypto point_t GH_exponents = c_point_0; CT::calc_pedersen_commitment_2(G_scalar, H_scalar, H2_scalar, GH_exponents); - bool result = multiexp_and_check_being_zero(g_scalars, h_scalars, summand + GH_exponents); + bool result = msm_and_check_zero(g_scalars, h_scalars, summand + GH_exponents); if (result) DBG_PRINT(ENDL << " . . . . bppe_verify() -- SUCCEEDED!!!" << ENDL); return result; diff --git a/src/crypto/range_proofs.h b/src/crypto/range_proofs.h index 238bc300..646f2908 100644 --- a/src/crypto/range_proofs.h +++ b/src/crypto/range_proofs.h @@ -41,7 +41,7 @@ namespace crypto //////////////////////////////////////// struct bpp_ct_generators_HGX { - // NOTE! This notation follows the original BP+ whitepaper, see mapping to Zano's generators below + // NOTE! This notation follows the original BP+ whitepaper, see mapping to Zano's generators in range_proofs.cpp static const point_t& bpp_G; static const point_t& bpp_H; static const point_t& bpp_H2; @@ -49,7 +49,7 @@ namespace crypto struct bpp_ct_generators_UGX { - // NOTE! This notation follows the original BP+ whitepaper, see mapping to Zano's generators below + // NOTE! This notation follows the original BP+ whitepaper, see mapping to Zano's generators in range_proofs.cpp static const point_t& bpp_G; static const point_t& bpp_H; static const point_t& bpp_H2; @@ -138,32 +138,11 @@ namespace crypto typedef bpp_crypto_trait_zano bpp_crypto_trait_Zarcanum; - - // efficient multiexponentiation (naive stub implementation atm, TODO) - template - bool multiexp_and_check_being_zero(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("multiexp result is non zero: " << result); - return false; - } - return true; - } - } // namespace crypto +#include "epee/include/profile_tools.h" // <- remove this, sowle + +#include "msm.h" #include "range_proof_bpp.h" #include "range_proof_bppe.h" diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 883a65ee..a0619c73 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -1360,13 +1360,10 @@ bool blockchain_storage::validate_miner_transaction(const block& b, return false; } - if (b.miner_tx.version > TRANSACTION_VERSION_PRE_HF4) + if (!verify_asset_surjection_proof(b.miner_tx, tx_id_for_post_hf4_era)) { - if (!verify_asset_surjection_proof(b.miner_tx, tx_id_for_post_hf4_era)) - { - LOG_ERROR("asset surjection proof verification failed for miner tx"); - return false; - } + LOG_ERROR("asset surjection proof verification failed for miner tx"); + return false; } LOG_PRINT_MAGENTA("Mining tx verification ok, blocks_size_median = " << blocks_size_median, LOG_LEVEL_2); @@ -4776,7 +4773,7 @@ bool blockchain_storage::print_tx_outputs_lookup(const crypto::hash& tx_id)const return true; } //------------------------------------------------------------------ -bool check_tx_explicit_asset_id_rules(const transaction& tx, bool all_tx_ins_have_explicit_asset_ids) +bool check_tx_explicit_asset_id_rules(const transaction& tx, bool all_tx_ins_have_explicit_native_asset_ids) { if (tx.version <= TRANSACTION_VERSION_PRE_HF4) return true; @@ -4784,13 +4781,13 @@ bool check_tx_explicit_asset_id_rules(const transaction& tx, bool all_tx_ins_hav // ( assuming that post-HF4 txs can only have tx_out_zarcanum outs ) bool r = false; - // if all tx inputs have explicit asset id AND it does not emit a new asset THEN all outputs must have explicit asset id (native coin) - if (all_tx_ins_have_explicit_asset_ids && !is_asset_emitting_transaction(tx)) + // if all tx inputs have explicit native asset id AND it does not emit a new asset THEN all outputs must have explicit asset id (native coin) + if (all_tx_ins_have_explicit_native_asset_ids && !is_asset_emitting_transaction(tx)) { for(size_t j = 0, k = tx.vout.size(); j < k; ++j) { r = crypto::point_t(boost::get(tx.vout[j]).blinded_asset_id).modify_mul8().to_public_key() == native_coin_asset_id; - CHECK_AND_ASSERT_MES(r, false, "output #" << j << " has a non-explicit asset id"); + CHECK_AND_ASSERT_MES(r, false, "output #" << j << " has a non-explicit asset id in a tx where all inputs have an explicit native asset id"); } } else // otherwise all outputs must have hidden asset id (unless they burn money by sending them to null pubkey) @@ -4799,7 +4796,7 @@ bool check_tx_explicit_asset_id_rules(const transaction& tx, bool all_tx_ins_hav { const tx_out_zarcanum& zo = boost::get(tx.vout[j]); r = zo.stealth_address == null_pkey || crypto::point_t(zo.blinded_asset_id).modify_mul8().to_public_key() != native_coin_asset_id; - CHECK_AND_ASSERT_MES(r, false, "output #" << j << " has an explicit asset id"); + CHECK_AND_ASSERT_MES(r, false, "output #" << j << " has an explicit asset id in a tx where not all inputs have an explicit native asset id"); } } return true; @@ -4845,7 +4842,7 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha { size_t sig_index = 0; max_used_block_height = 0; - bool all_tx_ins_have_explicit_asset_ids = true; + bool all_tx_ins_have_explicit_native_asset_ids = true; auto local_check_key_image = [&](const crypto::key_image& ki) -> bool { @@ -4907,7 +4904,7 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha if (!local_check_key_image(in_zc.k_image)) return false; - if (!check_tx_input(tx, sig_index, in_zc, tx_prefix_hash, max_used_block_height, all_tx_ins_have_explicit_asset_ids)) + if (!check_tx_input(tx, sig_index, in_zc, tx_prefix_hash, max_used_block_height, all_tx_ins_have_explicit_native_asset_ids)) { LOG_ERROR("Failed to validate zc input #" << sig_index << " in tx: " << tx_prefix_hash); return false; @@ -4930,7 +4927,7 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha CHECK_AND_ASSERT_MES(r, false, "Failed to validate attachments in tx " << tx_prefix_hash << ": incorrect extra_attachment_info in tx.extra"); } - CHECK_AND_ASSERT_MES(check_tx_explicit_asset_id_rules(tx, all_tx_ins_have_explicit_asset_ids), false, "tx does not comply with explicit asset id rules"); + CHECK_AND_ASSERT_MES(check_tx_explicit_asset_id_rules(tx, all_tx_ins_have_explicit_native_asset_ids), false, "tx does not comply with explicit asset id rules"); } TIME_MEASURE_FINISH_PD(tx_check_inputs_attachment_check); return true; @@ -5314,7 +5311,7 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, } //------------------------------------------------------------------ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, const txin_zc_input& zc_in, const crypto::hash& tx_prefix_hash, - uint64_t& max_related_block_height, bool& all_tx_ins_have_explicit_asset_ids) const + uint64_t& max_related_block_height, bool& all_tx_ins_have_explicit_native_asset_ids) const { CRITICAL_REGION_LOCAL(m_read_lock); @@ -5347,8 +5344,8 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, for(auto& zc_out : scan_contex.zc_outs) { ring.emplace_back(zc_out.stealth_address, zc_out.amount_commitment, zc_out.blinded_asset_id); - if (all_tx_ins_have_explicit_asset_ids && crypto::point_t(zc_out.blinded_asset_id).modify_mul8().to_public_key() != native_coin_asset_id) - all_tx_ins_have_explicit_asset_ids = false; + if (all_tx_ins_have_explicit_native_asset_ids && crypto::point_t(zc_out.blinded_asset_id).modify_mul8().to_public_key() != native_coin_asset_id) + all_tx_ins_have_explicit_native_asset_ids = false; } // calculate corresponding tx prefix hash @@ -5940,10 +5937,12 @@ bool blockchain_storage::validate_pos_block(const block& b, CHECK_AND_ASSERT_MES(b.miner_tx.vin[1].type() == typeid(txin_to_key) || b.miner_tx.vin[1].type() == typeid(txin_zc_input), false, "incorrect input 1 type: " << b.miner_tx.vin[1].type().name()); const crypto::key_image& stake_key_image = get_key_image_from_txin_v(b.miner_tx.vin[1]); //check keyimage if it's main chain candidate + TIME_MEASURE_START_PD(pos_validate_ki_search); if (!for_altchain) { CHECK_AND_ASSERT_MES(!have_tx_keyimg_as_spent(stake_key_image), false, "stake key image has been already spent in blockchain: " << stake_key_image); } + TIME_MEASURE_FINISH_PD(pos_validate_ki_search); if (!is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM)) { @@ -5980,6 +5979,7 @@ bool blockchain_storage::validate_pos_block(const block& b, if (!for_altchain) { + TIME_MEASURE_START_PD(pos_validate_get_out_keys_for_inputs); // do general input check for main chain blocks only // TODO @#@#: txs in alternative PoS blocks (including miner_tx) must be validated by validate_alt_block_txs() const zarcanum_sig& sig = boost::get(b.miner_tx.signatures[0]); @@ -5993,6 +5993,8 @@ bool blockchain_storage::validate_pos_block(const block& b, // make sure that all referring inputs are either older then, or the same age as, the most resent PoW block. CHECK_AND_ASSERT_MES(max_related_block_height <= last_pow_block_height, false, "stake input refs' max related block height is " << max_related_block_height << " while last PoW block height is " << last_pow_block_height); + TIME_MEASURE_FINISH_PD(pos_validate_get_out_keys_for_inputs); + // build a ring of references vector ring; ring.reserve(scan_contex.zc_outs.size()); @@ -6002,7 +6004,9 @@ bool blockchain_storage::validate_pos_block(const block& b, crypto::scalar_t last_pow_block_id_hashed = crypto::hash_helper_t::hs(CRYPTO_HDS_ZARCANUM_LAST_POW_HASH, sm.last_pow_id); uint8_t err = 0; + TIME_MEASURE_START_PD(pos_validate_zvp); r = crypto::zarcanum_verify_proof(id, kernel_hash, ring, last_pow_block_id_hashed, stake_input.k_image, basic_diff, sig, &err); + TIME_MEASURE_FINISH_PD(pos_validate_zvp); CHECK_AND_ASSERT_MES(r, false, "zarcanum_verify_proof failed with code " << (int)err); } @@ -6457,7 +6461,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt } TIME_MEASURE_FINISH_PD(tx_check_inputs_time); tx_total_inputs_processing_time += tx_check_inputs_time; - tx_total_inputs_count++; + tx_total_inputs_count += tx.vin.size(); burned_coins += get_burned_amount(tx); TIME_MEASURE_START_PD(tx_prapare_append); @@ -6508,6 +6512,8 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt if (!m_is_in_checkpoint_zone) { + // validate_miner_transaction will check balance proof and asset surjection proof + // and, as a side effect, it MAY recalculate base_reward, consider redisign, TODO -- sowle TIME_MEASURE_START_PD(validate_miner_transaction_time); if (!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins)) // TODO @#@# base_reward will be calculated once again, consider refactoring { @@ -6658,7 +6664,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt TIME_MEASURE_FINISH_PD_MS(block_processing_time_0_ms); //print result - stringstream powpos_str_entry, timestamp_str_entry; + stringstream powpos_str_entry, timestamp_str_entry, pos_validation_str_entry; if (is_pos_bl) { // PoS int64_t actual_ts = get_block_datetime(bei.bl); // signed int is intentionally used here @@ -6672,6 +6678,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt else powpos_str_entry << "hidden"; timestamp_str_entry << ", actual ts: " << actual_ts << " (diff: " << std::showpos << ts_diff << "s) block ts: " << std::noshowpos << bei.bl.timestamp << " (shift: " << std::showpos << static_cast(bei.bl.timestamp) - actual_ts << ")"; + pos_validation_str_entry << "(" << m_performance_data.pos_validate_ki_search.get_last_val() << "/" << m_performance_data.pos_validate_get_out_keys_for_inputs.get_last_val() << "/" << m_performance_data.pos_validate_zvp.get_last_val() << ")"; } else { // PoW @@ -6692,14 +6699,15 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt << ", timing: " << block_processing_time_0_ms << "ms" << "(micrsec:" << block_processing_time_1 << "(" << target_calculating_time_2 << "(" << m_performance_data.target_calculating_enum_blocks.get_last_val() << "/" << m_performance_data.target_calculating_calc.get_last_val() << ")" - << "/" << longhash_calculating_time_3 + << "/" << longhash_calculating_time_3 << pos_validation_str_entry.str() << "/" << insert_time_4 << "/" << all_txs_insert_time_5 << "/" << etc_stuff_6 << "/" << tx_total_inputs_processing_time << " of " << tx_total_inputs_count << "/(" << m_performance_data.validate_miner_transaction_time.get_last_val() << "|" << m_performance_data.collect_rangeproofs_data_from_tx_time.get_last_val() << "|" - << m_performance_data.verify_multiple_zc_outs_range_proofs_time.get_last_val() + << m_performance_data.verify_multiple_zc_outs_range_proofs_time.get_last_val() << "~" + << range_proofs_agregated.size() << ")" << "))"); diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index 3f040f19..b3cf29c6 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -77,6 +77,11 @@ namespace currency epee::math_helper::average target_calculating_enum_blocks; epee::math_helper::average target_calculating_calc; + //longhash_calculating_time_3 + epee::math_helper::average pos_validate_ki_search; + epee::math_helper::average pos_validate_get_out_keys_for_inputs; + epee::math_helper::average pos_validate_zvp; + //tx processing zone epee::math_helper::average tx_check_inputs_time; epee::math_helper::average tx_add_one_tx_time; @@ -299,7 +304,7 @@ namespace currency bool check_tx_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height, uint64_t& source_max_unlock_time_for_pos_coinbase)const; bool check_tx_input(const transaction& tx, size_t in_index, const txin_multisig& txin, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height)const; bool check_tx_input(const transaction& tx, size_t in_index, const txin_htlc& txin, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height)const; - bool check_tx_input(const transaction& tx, size_t in_index, const txin_zc_input& zc_in, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height, bool& tx_has_explicit_asset_ids_in_all_ins) const; + bool check_tx_input(const transaction& tx, size_t in_index, const txin_zc_input& zc_in, const crypto::hash& tx_prefix_hash, uint64_t& max_related_block_height, bool& all_tx_ins_have_explicit_native_asset_ids) const; bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t& max_used_block_height)const; bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash) const; bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t& max_used_block_height, crypto::hash& max_used_block_id)const; diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index 26b17a9f..0fbab8a8 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -315,51 +315,6 @@ namespace currency ///////////////////////////////////////////////////////////////////////////// // Zarcanum structures // - //#pragma pack(push, 1) - /* - struct zarcanum_input : public referring_input - { - zarcanum_input() {} - // Boost's Assignable concept - zarcanum_input(const zarcanum_input&) = default; - zarcanum_input& operator=(const zarcanum_input&)= default; - - crypto::key_image k_image; - - BEGIN_SERIALIZE_OBJECT() - FIELD(k_image) - FIELD(key_offsets) // referring_input - END_SERIALIZE() - - BEGIN_BOOST_SERIALIZATION() - BOOST_SERIALIZE(k_image) - BOOST_SERIALIZE(key_offsets) // referring_input - END_BOOST_SERIALIZATION() - }; - - // txin_zarcanum_inputs contains several zarcanum_input instances and corresponds to one ZC_sig - struct txin_zarcanum_inputs - { - txin_zarcanum_inputs() {} - - // Boost's Assignable concept - txin_zarcanum_inputs(const txin_zarcanum_inputs&) = default; - txin_zarcanum_inputs& operator=(const txin_zarcanum_inputs&) = default; - - std::vector elements; - std::vector etc_details; - - BEGIN_SERIALIZE_OBJECT() - FIELD(elements) - FIELD(etc_details) - END_SERIALIZE() - - BEGIN_BOOST_SERIALIZATION() - BOOST_SERIALIZE(elements) - BOOST_SERIALIZE(etc_details) - END_BOOST_SERIALIZATION() - }; - */ struct txin_zc_input : public referring_input { diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index a68cf842..ce50f866 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -116,7 +116,7 @@ namespace currency secret_index = ring.size() - 1; } - CHECK_AND_ASSERT_MES(secret_index != SIZE_MAX, false, "out #" << j << ": can't find a corresponding asset id in inputs"); + CHECK_AND_ASSERT_MES(secret_index != SIZE_MAX, false, "out #" << j << ": can't find a corresponding asset id in inputs, asset id: " << H); result.bge_proofs.emplace_back(crypto::BGE_proof_s{}); uint8_t err = 0; @@ -550,16 +550,16 @@ namespace currency if (additional_inputs_amount_and_fees_for_mining_tx == 0) { // normal tx - CHECK_AND_ASSERT_MES(bare_inputs_sum >= bare_outputs_sum, false, "tx balance error: sum of inputs (" << print_money_brief(bare_inputs_sum) - << ") is less than or equal to sum of outputs(" << print_money_brief(bare_outputs_sum) << ")"); + CHECK_AND_ASSERT_MES(bare_inputs_sum >= bare_outputs_sum, false, "tx balance error: the sum of inputs (" << print_money_brief(bare_inputs_sum) + << ") is less than or equal to the sum of outputs (" << print_money_brief(bare_outputs_sum) << ")"); } else { // miner tx CHECK_AND_ASSERT_MES(bare_inputs_sum + additional_inputs_amount_and_fees_for_mining_tx == bare_outputs_sum, false, - "tx balance error: sum of inputs (" << print_money_brief(bare_inputs_sum) << + "tx balance error: the sum of inputs (" << print_money_brief(bare_inputs_sum) << ") + additional inputs and fees (" << print_money_brief(additional_inputs_amount_and_fees_for_mining_tx) << - ") is less than or equal to sum of outputs(" << print_money_brief(bare_outputs_sum) << ")"); + ") is less than or equal to the sum of outputs (" << print_money_brief(bare_outputs_sum) << ")"); } return true; } @@ -4060,11 +4060,7 @@ namespace currency //----------------------------------------------------------------------- bool is_pos_coinbase(const transaction& tx) { - bool pos = false; - if (!is_coinbase(tx, pos) || !pos) - return false; - - return true; + return is_pos_miner_tx(tx); } //----------------------------------------------------------------------- bool is_coinbase(const transaction& tx, bool& pos_coinbase) @@ -4072,7 +4068,7 @@ namespace currency if (!is_coinbase(tx)) return false; - pos_coinbase = (tx.vin.size() == 2 && tx.vin[1].type() == typeid(txin_to_key)); + pos_coinbase = is_pos_coinbase(tx); return true; } //----------------------------------------------------------------------- diff --git a/src/version.h.in b/src/version.h.in index 4c2f9ef0..87ceb356 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -8,6 +8,6 @@ #define PROJECT_REVISION "0" #define PROJECT_VERSION PROJECT_MAJOR_VERSION "." PROJECT_MINOR_VERSION "." PROJECT_REVISION -#define PROJECT_VERSION_BUILD_NO 249 +#define PROJECT_VERSION_BUILD_NO 250 #define PROJECT_VERSION_BUILD_NO_STR STRINGIFY_EXPAND(PROJECT_VERSION_BUILD_NO) #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO_STR "[" BUILD_COMMIT_ID "]" diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index f85cbdc3..71f53761 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1241,6 +1241,9 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(hard_fork_2_incorrect_alias_update); GENERATE_AND_PLAY(hard_fork_2_incorrect_alias_update); + // HF4 + // GENERATE_AND_PLAY_HF(hard_fork_4_consolidated_txs, "4"); TODO, doesn't work atm -- sowle + // atomics GENERATE_AND_PLAY(atomic_simple_test); GENERATE_AND_PLAY(atomic_test_wrong_redeem_wrong_refund); diff --git a/tests/core_tests/chaingen_tests_list.h b/tests/core_tests/chaingen_tests_list.h index 2dccb4e3..c6a952f6 100644 --- a/tests/core_tests/chaingen_tests_list.h +++ b/tests/core_tests/chaingen_tests_list.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018 Zano Project +// Copyright (c) 2014-2024 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying @@ -38,9 +38,10 @@ #include "hard_fork_1_bad_pos_source.h" #include "hard_fork_1.h" #include "hard_fork_2.h" +#include "hard_fork_4.h" #include "atomic_tests.h" #include "isolate_auditable_and_proof.h" #include "zarcanum_test.h" #include "multiassets_test.h" #include "ionic_swap_tests.h" -#include "attachment_isolation_encryption_test.h" \ No newline at end of file +#include "attachment_isolation_encryption_test.h" diff --git a/tests/core_tests/hard_fork_4.cpp b/tests/core_tests/hard_fork_4.cpp new file mode 100644 index 00000000..2c4a7fd3 --- /dev/null +++ b/tests/core_tests/hard_fork_4.cpp @@ -0,0 +1,223 @@ +// Copyright (c) 2023-2024 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "chaingen.h" +#include "hard_fork_4.h" +#include "random_helper.h" + +using namespace currency; + +namespace currency +{ + bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, + const std::vector& destinations, + const std::vector& extra, + const std::vector& attachments, + transaction& tx, + uint64_t tx_version, + crypto::secret_key& one_time_secret_key, + uint64_t unlock_time, + uint64_t expiration_time, + uint8_t tx_outs_attr, + bool shuffle, + uint64_t flags, + uint64_t explicit_consolidated_tx_fee, + tx_generation_context& gen_context) + { + //extra copy operation, but creating transaction is not sensitive to this + finalize_tx_param ftp{}; + ftp.tx_version = tx_version; + ftp.sources = sources; + ftp.prepared_destinations = destinations; + ftp.extra = extra; + ftp.attachments = attachments; + ftp.unlock_time = unlock_time; + // ftp.crypt_address = crypt_destination_addr; + ftp.expiration_time = expiration_time; + ftp.tx_outs_attr = tx_outs_attr; + ftp.shuffle = shuffle; + ftp.flags = flags; + ftp.mode_separate_fee = explicit_consolidated_tx_fee; + + finalized_tx ft = AUTO_VAL_INIT(ft); + ft.tx = tx; + ft.one_time_key = one_time_secret_key; + ftp.gen_context = gen_context; // ftp, not ft here, this is UGLY -- sowle + bool r = construct_tx(sender_account_keys, ftp, ft); + tx = ft.tx; + one_time_secret_key = ft.one_time_key; + gen_context = ft.ftp.gen_context; + return r; + } +} // namespace currency + +void add_flags_to_all_destination_entries(const uint64_t flags, std::vector& destinations) +{ + for(auto& de : destinations) + de.flags |= flags; +} + +//------------------------------- + + +hard_fork_4_consolidated_txs::hard_fork_4_consolidated_txs() +{ + REGISTER_CALLBACK_METHOD(hard_fork_4_consolidated_txs, c1); +} + +bool hard_fork_4_consolidated_txs::generate(std::vector& events) const +{ + bool r = false; + + uint64_t ts = test_core_time::get_time(); + m_accounts.resize(TOTAL_ACCS_COUNT); + account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts); + account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts); + account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(); bob_acc.set_createtime(ts); + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts); + DO_CALLBACK(events, "configure_core"); // necessary for the test to be run by GENERATE_AND_PLAY_HF + + REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + m_post_hf4_zarcanum = get_hardforks().get_the_most_recent_hardfork_id_for_height(CURRENCY_MINED_MONEY_UNLOCK_WINDOW) >= ZANO_HARDFORK_04_ZARCANUM; + + uint64_t alice_amount = MK_TEST_COINS(50); + MAKE_TX(events, tx_0a, miner_acc, alice_acc, alice_amount, blk_0r); + // tx_0b is only needed for decoy outputs with amount = alice_amount (important only for pre-HF4) + transaction tx_0b{}; + construct_tx_with_many_outputs(m_hardforks, events, blk_0r, miner_acc.get_keys(), miner_acc.get_public_address(), alice_amount * 10, 10, TESTS_DEFAULT_FEE, tx_0b); + ADD_CUSTOM_EVENT(events, tx_0b); + MAKE_NEXT_BLOCK_TX_LIST(events, blk_1, blk_0r, miner_acc, std::list({tx_0a, tx_0b})); + + REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + // check Alice's balance + std::shared_ptr alice_wlt; + r = generator.init_test_wallet(alice_acc, get_block_hash(blk_0), alice_wlt); + CHECK_AND_ASSERT_MES(r, false, "init_test_wallet failed"); + r = generator.refresh_test_wallet(events, alice_wlt.get(), get_block_hash(blk_1r), 2 * CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1); + CHECK_AND_ASSERT_MES(r, false, "refresh_test_wallet failed"); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "alice", alice_amount, 0, alice_amount, 0, 0), false, ""); + + uint64_t miner_amount = MK_TEST_COINS(60); + uint64_t bob_amount = miner_amount + alice_amount - TX_DEFAULT_FEE; + + // Consolidated tx (TX_FLAG_SIGNATURE_MODE_SEPARATE). + + // this data will be transferred between stage 1 and 2 + transaction tx_1{}; + crypto::secret_key one_time_secret_key{}; + tx_generation_context gen_context{}; + + // Part 1/2, miner's inputs + { + std::vector sources; + r = fill_tx_sources(sources, events, blk_1r, miner_acc.get_keys(), miner_amount, 10); + CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed"); + uint64_t miner_change = get_sources_total_amount(sources) - miner_amount; + + std::vector destinations; + if (miner_change != 0) + destinations.push_back(tx_destination_entry(miner_change, miner_acc.get_public_address())); + destinations.push_back(tx_destination_entry(bob_amount, bob_acc.get_public_address())); + + add_flags_to_all_destination_entries(tx_destination_entry_flags::tdef_explicit_native_asset_id, destinations); + r = construct_tx(miner_acc.get_keys(), sources, destinations, empty_extra, empty_attachment, tx_1, get_tx_version_from_events(events), one_time_secret_key, + 0, 0, 0, true, TX_FLAG_SIGNATURE_MODE_SEPARATE, TX_DEFAULT_FEE, gen_context); + CHECK_AND_ASSERT_MES(r, false, "construct_tx failed"); + + // partially completed tx_1 shouldn't be accepted + //DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx_1); + MAKE_NEXT_BLOCK_TX1(events, blk_2a, blk_1r, miner_acc, tx_1); + } + + + // Part 2/2, Alice's inputs + { + std::vector sources; + r = fill_tx_sources(sources, events, blk_1r, alice_acc.get_keys(), alice_amount, 10); + CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed"); + CHECK_AND_ASSERT_MES(get_sources_total_amount(sources) == alice_amount, false, "no change for Alice is expected"); + sources.back().separately_signed_tx_complete = true; + + std::vector destinations; + + r = construct_tx(alice_acc.get_keys(), sources, destinations, empty_extra, empty_attachment, tx_1, get_tx_version_from_events(events), one_time_secret_key, + 0, 0, 0, true, TX_FLAG_SIGNATURE_MODE_SEPARATE, 0 /* note zero fee here */, gen_context); + CHECK_AND_ASSERT_MES(r, false, "construct_tx failed"); + + ADD_CUSTOM_EVENT(events, tx_1); + } + MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner_acc, tx_1); + + //std::shared_ptr bob_wlt; + //r = generator.init_test_wallet(bob_acc, get_block_hash(blk_0), bob_wlt); + //CHECK_AND_ASSERT_MES(r, false, "init_test_wallet failed"); + //r = generator.refresh_test_wallet(events, bob_wlt.get(), get_block_hash(blk_2), 2 * CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2); + //CHECK_AND_ASSERT_MES(r, false, "refresh_test_wallet failed"); + //CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt.get(), "Bob", bob_amount, 0, 0, 0, 0), false, ""); + + + return true; +} + +bool hard_fork_4_consolidated_txs::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + return true; +} + + + +/* +hardfork_4_explicit_native_ids_in_outs::hardfork_4_explicit_native_ids_in_outs() +{ + REGISTER_CALLBACK_METHOD(hardfork_4_explicit_native_ids_in_outs, c1); + + m_hardforks.clear(); + m_hardforks.set_hardfork_height(ZANO_HARDFORK_04_ZARCANUM, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1); +} + +bool hardfork_4_explicit_native_ids_in_outs::generate(std::vector& events) const +{ + bool r = false; + + uint64_t ts = test_core_time::get_time(); + m_accounts.resize(TOTAL_ACCS_COUNT); + account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts); + account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts); + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts); + DO_CALLBACK(events, "configure_core"); // necessary to set m_hardforks + + REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + DO_CALLBACK_PARAMS(events, "check_hardfork_inactive", static_cast(ZANO_HARDFORK_04_ZARCANUM)); + + // tx_0: miner -> Alice + // make tx_0 before HF4, so Alice will have only bare outs + m_alice_initial_balance = MK_TEST_COINS(1000); + MAKE_TX(events, tx_0, miner_acc, alice_acc, m_alice_initial_balance, blk_0r); + MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0); + + // make sure HF4 has been activated + DO_CALLBACK_PARAMS(events, "check_hardfork_active", static_cast(ZANO_HARDFORK_04_ZARCANUM)); + + // rewind blocks + REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + // check Alice's balance and make sure she cannot deploy an asset + DO_CALLBACK(events, "c1_alice_cannot_deploy_asset"); + + // tx_1: Alice -> Alice (all coins) : this will convert all Alice outputs to ZC outs + MAKE_TX(events, tx_1, alice_acc, alice_acc, m_alice_initial_balance - TESTS_DEFAULT_FEE, blk_1r); + + + + return true; +} + +bool hardfork_4_explicit_native_ids_in_outs::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + return true; +} +*/ \ No newline at end of file diff --git a/tests/core_tests/hard_fork_4.h b/tests/core_tests/hard_fork_4.h new file mode 100644 index 00000000..bf12419f --- /dev/null +++ b/tests/core_tests/hard_fork_4.h @@ -0,0 +1,26 @@ +// Copyright (c) 2023-2024 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#pragma once +#include "chaingen.h" +#include "wallet_tests_basic.h" + + +struct hard_fork_4_consolidated_txs : public wallet_test +{ + hard_fork_4_consolidated_txs(); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); + + mutable bool m_post_hf4_zarcanum = false; +}; + + +struct hardfork_4_explicit_native_ids_in_outs : public wallet_test +{ + hardfork_4_explicit_native_ids_in_outs(); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); + + mutable uint64_t m_alice_initial_balance = 0; +}; diff --git a/tests/functional_tests/crypto_tests.cpp b/tests/functional_tests/crypto_tests.cpp index efa4aab0..4afdbe5e 100644 --- a/tests/functional_tests/crypto_tests.cpp +++ b/tests/functional_tests/crypto_tests.cpp @@ -1619,6 +1619,7 @@ TEST(crypto, schnorr_sig) return true; } + TEST(crypto, point_negation) { ASSERT_EQ(c_point_0, -c_point_0); @@ -1651,6 +1652,116 @@ TEST(crypto, point_negation) } +TEST(crypto, scalar_get_bits) +{ + scalar_t x = scalar_t::random(); + for(size_t i = 0; i < 256; ++i) + ASSERT_EQ(x.get_bits(i, 0), 0); + for(size_t i = 0; i < 256; ++i) + ASSERT_EQ(x.get_bits(i, std::min(255ull, i + 65)), 0); + + ASSERT_EQ(x.get_bits(0, 64), x.m_u64[0]); + ASSERT_EQ(x.get_bits(64, 64), x.m_u64[1]); + ASSERT_EQ(x.get_bits(128, 64), x.m_u64[2]); + ASSERT_EQ(x.get_bits(192, 64), x.m_u64[3]); + + uint64_t high_32_bits = x.m_u64[3] >> 32; + ASSERT_EQ(x.get_bits(192+32, 32), high_32_bits); + + for(size_t i = 33; i <= 64; ++i) + ASSERT_EQ(x.get_bits(192+32, i), high_32_bits); + + for(size_t i = 0; i < 10000; ++i) + { + scalar_t b = scalar_t::random(); + scalar_t x = scalar_t::random(); + size_t bit_index_from = b.m_s[5]; + size_t bits_count = b.m_s[6] % 65; // [0; 64] are allowed + + uint64_t extracted_bits = 0; + for(size_t j = 0; j < bits_count; ++j) + { + if (bit_index_from + j <= 255 && x.get_bit(bit_index_from + j)) + extracted_bits |= 1ull << j; + } + + if (extracted_bits != x.get_bits(bit_index_from, bits_count)) + { + std::cout << "i: " << i << ", bit_index_from: " << bit_index_from << ", bits_count: " << bits_count << ENDL + << "extracted_bits: " << extracted_bits << ", get_bits(): " << x.get_bits(bit_index_from, bits_count); + ASSERT_TRUE(false); + } + } + return true; +} + +template +bool crypto_msm_runner(size_t N, size_t low_bits_to_clear, size_t high_bits_to_clear) +{ + scalar_vec_t g_scalars, h_scalars; + g_scalars.resize_and_make_random(N); + h_scalars.resize_and_make_random(N); + if (N > 4) + { + g_scalars[0] = c_scalar_Lm1; // always include the max and the min + h_scalars[0] = c_scalar_Lm1; + g_scalars[1] = 0; + h_scalars[1] = 0; + } + + point_t sum = c_point_0; + for(size_t i = 0; i < N; ++i) + { + for(size_t bit_index = 0; bit_index < low_bits_to_clear; ++bit_index) + { + g_scalars[i].clear_bit(bit_index); + h_scalars[i].clear_bit(bit_index); + } + for(size_t bit_index = 256 - high_bits_to_clear; bit_index < 256; ++bit_index) + { + g_scalars[i].clear_bit(bit_index); + h_scalars[i].clear_bit(bit_index); + } + sum += g_scalars[i] * CT::get_generator(false, i) + h_scalars[i] * CT::get_generator(true, i); + } + + //TIME_MEASURE_START(t); + bool r = msm_and_check_zero(g_scalars, h_scalars, -sum); + //TIME_MEASURE_FINISH(t); + return r; +} + +TEST(crypto, msm) +{ + // test the default msm_and_check_zero correctness + bool r = false; + + for(size_t N = 1; N <= 128; ++N) + { + std::cout << "N = " << N << ENDL; + r = crypto_msm_runner(N, 0, 0); + ASSERT_TRUE(r); + r = crypto_msm_runner(N, 0, 0); + ASSERT_TRUE(r); + } + + for(size_t i = 0; i <= 128; ++i) + { + std::cout << "i = " << i << ENDL; + r = crypto_msm_runner(128, i, 0); + ASSERT_TRUE(r); + r = crypto_msm_runner(128, 0, i); + ASSERT_TRUE(r); + r = crypto_msm_runner(256, i, 0); + ASSERT_TRUE(r); + r = crypto_msm_runner(256, 0, i); + ASSERT_TRUE(r); + } + + return true; +} + + // // test's runner // diff --git a/tests/functional_tests/crypto_tests_performance.h b/tests/functional_tests/crypto_tests_performance.h index b686bbad..68a8a7e7 100644 --- a/tests/functional_tests/crypto_tests_performance.h +++ b/tests/functional_tests/crypto_tests_performance.h @@ -3,7 +3,45 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #pragma once -TEST(crypto, primitives) + +uint64_t get_bits_v1(const scalar_t& s, uint8_t bit_index_first, uint8_t bits_count) +{ + if (bits_count == 0 || bits_count > 64) + return 0; + unsigned int bit_index_last = bit_index_first + bits_count - 1; + if (bit_index_last > 255) + bit_index_last = 255; + uint64_t result_mask = ((1ull << (bits_count - 1)) - 1) << 1 | 1; // (just because 1ull << 64 in undefined behaviour, not a 0 as one would expect) + + uint64_t result = s.m_u64[bit_index_first >> 6] >> (bit_index_first & 63); + if (bits_count > (bit_index_last & 63) + 1) + result |= s.m_u64[bit_index_last >> 6] << (bits_count - (bit_index_last & 63) - 1); + return result & result_mask; +} + + +inline std::ostream &operator <<(std::ostream &o, const crypto::ge_precomp v) +{ + o << "{{"; + + for(size_t i = 0; i < 9; ++i) + o << v.yplusx[i] << ", "; + + o << v.yplusx[9] << "},\n {"; + + for(size_t i = 0; i < 9; ++i) + o << v.yminusx[i] << ", "; + + o << v.yminusx[9] << "},\n {"; + + for(size_t i = 0; i < 9; ++i) + o << v.xy2d[i] << ", "; + + o << v.xy2d[9] << "}}\n"; + return o; +} + +TEST(perf, primitives) { struct helper { @@ -46,6 +84,44 @@ TEST(crypto, primitives) #define HASH_64_VEC(vec_var_name) hash_64(vec_var_name.data(), vec_var_name.size() * sizeof(vec_var_name[0])) + LOG_PRINT_L0(ENDL << "hash functions:"); + + struct run_cn_fash_hash + { + static uint64_t run(timer_t& t, size_t rounds, size_t data_size) + { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + struct bytes64 + { + unsigned char b[64]; + }; + + std::vector scalars_64(rounds); + for (size_t i = 0; i < scalars_64.size(); ++i) + crypto::generate_random_bytes(sizeof(bytes64), scalars_64[i].b); + + std::vector results(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + results[i] = cn_fast_hash(scalars_64[rnd_indecies[i]].b, 64); + } + t.stop(); + + return HASH_64_VEC(results); + }; + }; + + run("cn_fast_hash(64 bytes)", 1000, [](timer_t& t, size_t rounds) { + return run_cn_fash_hash::run(t, rounds, 64ull); + }); + + run("cn_fast_hash(2048 bytes)", 1000, [](timer_t& t, size_t rounds) { + return run_cn_fash_hash::run(t, rounds, 2048ull); + }); + LOG_PRINT_L0(ENDL << "native crypto primitives:"); run("sc_reduce", 30000, [](timer_t& t, size_t rounds) {