// Copyright (c) 2022-2023 Zano Project // Copyright (c) 2022-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. // // Note: This file originates from tests/functional_tests/crypto_tests.cpp #pragma once #include "crypto-sugar.h" #include "range_proofs.h" #include "clsag.h" #include namespace crypto { namespace mp = boost::multiprecision; extern const mp::uint256_t c_zarcanum_z_coeff_mp; extern const scalar_t c_zarcanum_z_coeff_s; mp::uint256_t zarcanum_precalculate_l_div_z_D(const mp::uint128_t& pos_difficulty); mp::uint256_t zarcanum_precalculate_z_l_div_z_D(const mp::uint128_t& pos_difficulty); bool zarcanum_check_main_pos_inequality(const hash& kernel_hash, const scalar_t& blinding_mask, const scalar_t& secret_q, const scalar_t& last_pow_block_id_hashed, const mp::uint256_t& z_l_div_z_D_, uint64_t stake_amount, mp::uint256_t& lhs, mp::uint512_t& rhs); struct zarcanum_proof { scalar_t d = 0; public_key C; // premultiplied by 1/8 public_key C_prime; // premultiplied by 1/8 public_key E; // premultiplied by 1/8 scalar_t c; // shared Fiat-Shamir challenge for the following three proofs scalar_t y0; // 1st linear composition proof scalar_t y1; // ( C + C' = lin(X, H + G) ) scalar_t y2; // 2nd linear composition proof scalar_t y3; // ( C - C' = lin(X, H - G) ) scalar_t y4; // Schnorr proof (F = lin(X)) bppe_signature E_range_proof; crypto::public_key pseudo_out_amount_commitment; // premultiplied by 1/8 CLSAG_GGXG_signature clsag_ggxg; }; bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector& ring, const scalar_t& last_pow_block_id_hashed, const key_image& stake_ki, const scalar_t& secret_x, const scalar_t& secret_q, uint64_t secret_index, const scalar_t& pseudo_out_blinding_mask, uint64_t stake_amount, const scalar_t& stake_blinding_mask, zarcanum_proof& result, uint8_t* p_err = nullptr); bool zarcanum_verify_proof(const hash& m, const hash& kernel_hash, const std::vector& ring, const scalar_t& last_pow_block_id_hashed, const key_image& stake_ki, const mp::uint128_t& pos_difficulty, const zarcanum_proof& sig, uint8_t* p_err = nullptr) noexcept; // TODO @#@#: make sure it is used, implement, then move it to an appropriate place struct linear_composition_proof { scalar_t c; scalar_t y0; scalar_t y1; }; enum generator_tag { gt_void = 0, gt_G = 1, gt_H = 2, gt_H2 = 3, gt_X = 4, gt_U = 5 }; template bool generate_linear_composition_proof(const hash& m, const public_key& A, const scalar_t& secret_a, const scalar_t& secret_b, linear_composition_proof& result, uint8_t* p_err = nullptr) { // consider embedding generators' tags into random entropy to distinguish proofs made with different generators during verification return false; } template bool verify_linear_composition_proof(const hash& m, const public_key& A, const linear_composition_proof& sig, uint8_t* p_err = nullptr) { return false; } struct generic_schnorr_sig { scalar_t c; scalar_t y; }; template inline bool generate_schnorr_sig(const hash& m, const point_t& A, const scalar_t& secret_a, generic_schnorr_sig& result); template<> inline bool generate_schnorr_sig(const hash& m, const point_t& A, const scalar_t& secret_a, generic_schnorr_sig& result) { #ifndef NDEBUG if (A != secret_a * c_point_G) return false; #endif scalar_t r = scalar_t::random(); point_t R = r * c_point_G; hash_helper_t::hs_t hsc(3); hsc.add_hash(m); hsc.add_point(A); hsc.add_point(R); result.c = hsc.calc_hash(); result.y.assign_mulsub(result.c, secret_a, r); // y = r - c * secret_a return true; } template<> inline bool generate_schnorr_sig(const hash& m, const point_t& A, const scalar_t& secret_a, generic_schnorr_sig& result) { #ifndef NDEBUG if (A != secret_a * c_point_X) return false; #endif scalar_t r = scalar_t::random(); point_t R = r * c_point_X; hash_helper_t::hs_t hsc(3); hsc.add_hash(m); hsc.add_point(A); hsc.add_point(R); result.c = hsc.calc_hash(); result.y.assign_mulsub(result.c, secret_a, r); // y = r - c * secret_a return true; } template inline bool verify_schnorr_sig(const hash& m, const public_key& A, const generic_schnorr_sig& sig) noexcept; template<> inline bool verify_schnorr_sig(const hash& m, const public_key& A, const generic_schnorr_sig& sig) noexcept { try { if (!sig.c.is_reduced() || !sig.y.is_reduced()) return false; hash_helper_t::hs_t hsc(3); hsc.add_hash(m); hsc.add_pub_key(A); hsc.add_point(point_t(A).mul_plus_G(sig.c, sig.y)); // sig.y * G + sig.c * A return sig.c == hsc.calc_hash(); } catch(...) { return false; } } template<> inline bool verify_schnorr_sig(const hash& m, const public_key& A, const generic_schnorr_sig& sig) noexcept { try { if (!sig.c.is_reduced() || !sig.y.is_reduced()) return false; hash_helper_t::hs_t hsc(3); hsc.add_hash(m); hsc.add_pub_key(A); hsc.add_point(sig.y * c_point_X + sig.c * point_t(A)); return sig.c == hsc.calc_hash(); } catch(...) { return false; } } // TODO: improve this proof using random weightning factor struct vector_UG_aggregation_proof { std::vector amount_commitments_for_rp_aggregation; // E' = e * U + y' * G, premultiplied by 1/8 scalar_vec_t y0s; scalar_vec_t y1s; scalar_t c; // common challenge }; bool generate_vector_UG_aggregation_proof(const hash& m, const scalar_vec_t& u_secrets, const scalar_vec_t& g_secrets0, const scalar_vec_t& g_secrets1, const std::vector& amount_commitments, const std::vector& amount_commitments_for_rp_aggregation, const std::vector& blinded_asset_ids, vector_UG_aggregation_proof& result, uint8_t* p_err = nullptr); bool verify_vector_UG_aggregation_proof(const hash& m, const std::vector amount_commitments_1div8, const std::vector blinded_asset_ids_1div8, const vector_UG_aggregation_proof& sig, uint8_t* p_err = nullptr) noexcept; } // namespace crypto