From 9b2c951d71bdd254c3845d0203be304b30f6da66 Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 6 Jul 2022 15:14:25 +0200 Subject: [PATCH 1/4] variant tag for zarcanum_outs_range_proof --- src/currency_core/currency_basic.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index 9b6f878f..edd5807e 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -1026,6 +1026,7 @@ SET_VARIANT_TAGS(crypto::bppe_signature_serialized, 41, "bppe_signature_serializ SET_VARIANT_TAGS(currency::NLSAG_sig, 42, "NLSAG_sig"); SET_VARIANT_TAGS(currency::zarcanum_sig, 43, "zarcanum_sig"); SET_VARIANT_TAGS(currency::void_sig, 44, "void_sig"); +SET_VARIANT_TAGS(currency::zarcanum_outs_range_proof, 45, "zarcanum_outs_range_proof"); From e18f300fae5949a7e0aeee785337edebfff98f0f Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 6 Jul 2022 15:54:51 +0200 Subject: [PATCH 2/4] inline for is_out_burned --- src/currency_core/currency_format_utils.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 0724d822..47366931 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -406,20 +406,20 @@ namespace currency } template - bool is_out_burned(const out_t& out) { CHECK_AND_ASSERT_THROW_MES(false, "incorrect out type: " << typeid(out).name()); } - bool is_out_burned(const tx_out_bare& o) { return is_out_burned(o.target); } - bool is_out_burned(const txout_to_key& o) { return o.key == null_pkey; } - bool is_out_burned(const tx_out_zarcanum& o) { return o.stealth_address == null_pkey; } + inline bool is_out_burned(const out_t& out) { CHECK_AND_ASSERT_THROW_MES(false, "incorrect out type: " << typeid(out).name()); } + inline bool is_out_burned(const tx_out_bare& o) { return is_out_burned(o.target); } + inline bool is_out_burned(const txout_to_key& o) { return o.key == null_pkey; } + inline bool is_out_burned(const tx_out_zarcanum& o) { return o.stealth_address == null_pkey; } struct zz_is_out_burned_helper_visitor : boost::static_visitor { template bool operator()(const T& v) const { return is_out_burned(v); } }; - bool is_out_burned(const tx_out_v& v) + inline bool is_out_burned(const tx_out_v& v) { return boost::apply_visitor(zz_is_out_burned_helper_visitor(), v); } - bool is_out_burned(const txout_target_v& v) + inline bool is_out_burned(const txout_target_v& v) { return boost::apply_visitor(zz_is_out_burned_helper_visitor(), v); } From 11ab985bc6ffea14ac2eb12ee020639958a66f3b Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 6 Jul 2022 18:52:24 +0200 Subject: [PATCH 3/4] crypto: bpp minor refactoring (mostly prototypes), minor improvements, compilation fixes --- src/crypto/range_proof_bpp.h | 35 ++++++++++----------- src/crypto/range_proof_bppe.h | 9 ++++-- src/crypto/range_proofs.h | 9 ++++++ src/currency_core/currency_format_utils.cpp | 6 ++++ 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/crypto/range_proof_bpp.h b/src/crypto/range_proof_bpp.h index cd61ca85..08e2dc57 100644 --- a/src/crypto/range_proof_bpp.h +++ b/src/crypto/range_proof_bpp.h @@ -23,16 +23,18 @@ namespace crypto scalar_t delta; }; -#define DBG_VAL_PRINT(x) std::cout << #x ": " << x << ENDL -#define DBG_PRINT(x) std::cout << x << ENDL +#define DBG_VAL_PRINT(x) (void(0)) // std::cout << #x ": " << x << ENDL +#define DBG_PRINT(x) (void(0)) // std::cout << x << ENDL + +#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \ + if (!(cond)) { LOG_PRINT_RED("bpp_gen: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \ + if (p_err) { *p_err = err_code; } return false; } + template - bool bpp_gen(const scalar_vec_t& values, const scalar_vec_t& masks, bpp_signature& sig, std::vector& commitments, uint8_t* p_err = nullptr) + bool bpp_gen(const scalar_vec_t& values, const scalar_vec_t& masks, bpp_signature& sig, const std::vector& commitments_1div8, uint8_t* p_err = nullptr) { -#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \ - if (!(cond)) { LOG_PRINT_RED("bpp_gen: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \ - if (p_err) { *p_err = err_code; } return false; } - + // Note: commitments_1div8 are supposed to be already calculated static_assert(CT::c_bpp_n <= 255, "too big N"); CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(values.size() > 0 && values.size() <= CT::c_bpp_values_max && values.size() == masks.size(), 1); CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(masks.is_reduced(), 3); @@ -42,15 +44,6 @@ namespace crypto const size_t c_bpp_mn = c_bpp_m * CT::c_bpp_n; const size_t c_bpp_log2_mn = c_bpp_log2_m + CT::c_bpp_log2_n; - // pre-multiply all output points by c_scalar_1div8 - // in order to enforce these points to be in the prime-order subgroup (after mul by 8 in bpp_verify()) - - // calc commitments vector as commitments[i] = 1/8 * values[i] * G + 1/8 * masks[i] * H - commitments.resize(values.size()); - for (size_t i = 0; i < values.size(); ++i) - CT::calc_pedersen_commitment(values[i] * c_scalar_1div8, masks[i] * c_scalar_1div8, commitments[i]); - - // s.a. BP+ paper, page 15, eq. 11 // decompose v into aL and aR: // v = aL o (1, 2, 2^2, ..., 2^n-1), o - component-wise product aka Hadamard product @@ -85,7 +78,7 @@ namespace crypto DBG_PRINT("initial transcript: " << e); hash_helper_t::hs_t hsc; - CT::update_transcript(hsc, e, commitments); + CT::update_transcript(hsc, e, commitments_1div8); // BP+ paper, page 15: The prover begins with sending A = g^aL h^aR h^alpha (group element) // so we calculate A0 = alpha * H + SUM(aL_i * G_i) + SUM(aR_i * H_i) @@ -96,7 +89,8 @@ namespace crypto for (size_t i = 0; i < c_bpp_mn; ++i) A0 += aLs[i] * CT::get_generator(false, i) + aRs[i] * CT::get_generator(true, i); - // part of 1/8 defense scheme + // pre-multiply all output points by c_scalar_1div8 + // in order to enforce these points to be in the prime-order subgroup (after mul by 8 in bpp_verify()) A0 *= c_scalar_1div8; A0.to_public_key(sig.A0); @@ -147,7 +141,7 @@ namespace crypto // aL_hat = aL - 1*z scalar_vec_t aLs_hat = aLs - z; - // aL_hat = aR + d o y^leftarr + 1*z where y^leftarr = (y^n, y^(n-1), ..., y) (BP+ paper, page 18, Fig. 3) + // aR_hat = aR + d o y^leftarr + 1*z where y^leftarr = (y^n, y^(n-1), ..., y) (BP+ paper, page 18, Fig. 3) scalar_vec_t aRs_hat = aRs + z; for (size_t i = 0; i < c_bpp_mn; ++i) aRs_hat[i] += d[i] * y_powers[c_bpp_mn - i]; @@ -696,4 +690,7 @@ namespace crypto #undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE } +#undef DBG_VAL_PRINT +#undef DBG_PRINT + } // namespace crypto diff --git a/src/crypto/range_proof_bppe.h b/src/crypto/range_proof_bppe.h index 7aaed80c..4b074dd9 100644 --- a/src/crypto/range_proof_bppe.h +++ b/src/crypto/range_proof_bppe.h @@ -24,8 +24,8 @@ namespace crypto scalar_t delta_2; }; -#define DBG_VAL_PRINT(x) std::cout << #x ": " << x << ENDL -#define DBG_PRINT(x) std::cout << x << ENDL +#define DBG_VAL_PRINT(x) (void(0)) // std::cout << #x ": " << x << ENDL +#define DBG_PRINT(x) (void(0)) // std::cout << x << ENDL template bool bppe_gen(const scalar_vec_t& values, const scalar_vec_t& masks, const scalar_vec_t& masks2, bppe_signature& sig, std::vector& commitments, uint8_t* p_err = nullptr) @@ -149,7 +149,7 @@ namespace crypto // aL_hat = aL - 1*z scalar_vec_t aLs_hat = aLs - z; - // aL_hat = aR + d o y^leftarr + 1*z where y^leftarr = (y^n, y^(n-1), ..., y) (BP+ paper, page 18, Fig. 3) + // aR_hat = aR + d o y^leftarr + 1*z where y^leftarr = (y^n, y^(n-1), ..., y) (BP+ paper, page 18, Fig. 3) scalar_vec_t aRs_hat = aRs + z; for (size_t i = 0; i < c_bpp_mn; ++i) aRs_hat[i] += d[i] * y_powers[c_bpp_mn - i]; @@ -716,4 +716,7 @@ namespace crypto #undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE } +#undef DBG_VAL_PRINT +#undef DBG_PRINT + } // namespace crypto diff --git a/src/crypto/range_proofs.h b/src/crypto/range_proofs.h index bcd03393..c933ee2c 100644 --- a/src/crypto/range_proofs.h +++ b/src/crypto/range_proofs.h @@ -88,6 +88,15 @@ namespace crypto e = hsc.calc_hash(); } + // assumes hsc is cleared + static void update_transcript(hash_helper_t::hs_t& hsc, scalar_t& e, const std::vector& pub_keys) + { + hsc.add_scalar(e); + for(auto p : pub_keys) + hsc.add_pub_key(*p); + e = hsc.calc_hash(); + } + // TODO: refactor with proper OOB handling static const point_t& get_generator(bool select_H, size_t index) { diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index b36a4417..cea28afc 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -2868,6 +2868,12 @@ namespace currency tv.details_view = tv.short_view; return true; } + bool operator()(const zarcanum_outs_range_proof& rp) + { + tv.type = "zarcanum_outs_range_proof"; + tv.short_view = "outputs_count = " + std::to_string(rp.outputs_count); + return true; + } }; //------------------------------------------------------------------ template From ea24d40f84753670a2ea3ea71acc5cd6d39f2a9f Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 6 Jul 2022 22:07:54 +0200 Subject: [PATCH 4/4] range proofs for zarcanum outputs --- src/crypto/range_proof_bpp.h | 6 +- src/crypto/range_proofs.h | 2 +- src/currency_core/currency_format_utils.cpp | 76 ++++++++++++++++++--- src/currency_core/currency_format_utils.h | 2 +- 4 files changed, 71 insertions(+), 15 deletions(-) diff --git a/src/crypto/range_proof_bpp.h b/src/crypto/range_proof_bpp.h index 08e2dc57..93e039b9 100644 --- a/src/crypto/range_proof_bpp.h +++ b/src/crypto/range_proof_bpp.h @@ -31,8 +31,8 @@ namespace crypto if (p_err) { *p_err = err_code; } return false; } - template - bool bpp_gen(const scalar_vec_t& values, const scalar_vec_t& masks, bpp_signature& sig, const std::vector& commitments_1div8, uint8_t* p_err = nullptr) + template> + bool bpp_gen(const scalar_vec_t& values, const scalar_vec_t& masks, const std::vector& commitments_1div8, bpp_signature& sig, uint8_t* p_err = nullptr) { // Note: commitments_1div8 are supposed to be already calculated static_assert(CT::c_bpp_n <= 255, "too big N"); @@ -333,7 +333,7 @@ namespace crypto }; - template + template> bool bpp_verify(const std::vector& sigs, uint8_t* p_err = nullptr) { #define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \ diff --git a/src/crypto/range_proofs.h b/src/crypto/range_proofs.h index c933ee2c..c584c3ff 100644 --- a/src/crypto/range_proofs.h +++ b/src/crypto/range_proofs.h @@ -89,7 +89,7 @@ namespace crypto } // assumes hsc is cleared - static void update_transcript(hash_helper_t::hs_t& hsc, scalar_t& e, const std::vector& pub_keys) + static void update_transcript(hash_helper_t::hs_t& hsc, scalar_t& e, const std::vector& pub_keys) { hsc.add_scalar(e); for(auto p : pub_keys) diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index cea28afc..f44b7ae7 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -609,10 +609,12 @@ namespace currency bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, const account_keys& self, uint8_t tx_outs_attr) { finalized_tx result = AUTO_VAL_INIT(result); - return construct_tx_out(de, tx_sec_key, output_index, tx, deriv_cache, self, result, tx_outs_attr); + crypto::scalar_t out_blinding_mask = AUTO_VAL_INIT(out_blinding_mask); + return construct_tx_out(de, tx_sec_key, output_index, tx, deriv_cache, self, out_blinding_mask, result, tx_outs_attr); } //--------------------------------------------------------------- - bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, const account_keys& self, finalized_tx& result, uint8_t tx_outs_attr) + bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, + const account_keys& self, crypto::scalar_t& out_blinding_mask, finalized_tx& result, uint8_t tx_outs_attr) { if (tx.version > TRANSACTION_VERSION_PRE_HF4) { @@ -634,8 +636,8 @@ namespace currency crypto::scalar_t amount_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_MASK, h); out.encrypted_amount = de.amount ^ amount_mask.m_u64[0]; - crypto::scalar_t blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_BLINDING_MASK, h); // f = Hs(domain_sep, d, i) - out.amount_commitment = (de.amount * crypto::c_point_H + blinding_mask * crypto::c_point_G).to_public_key(); + out_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_BLINDING_MASK, h); // f = Hs(domain_sep, d, i) + out.amount_commitment = (crypto::c_scalar_1div8 * de.amount * crypto::c_point_H + crypto::c_scalar_1div8 * out_blinding_mask * crypto::c_point_G).to_public_key(); out.mix_attr = tx_outs_attr; // TODO @#@# @CZ check this } @@ -648,11 +650,11 @@ namespace currency out.stealth_address = (h * crypto::c_point_G + crypto::point_t(apa.spend_public_key)).to_public_key(); out.concealing_point = (crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h) * crypto::point_t(apa.view_public_key)).to_public_key(); // Q = Hs(domain_sep, h) * V - crypto::scalar_t amount_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_MASK, h); + crypto::scalar_t amount_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_MASK, h); out.encrypted_amount = de.amount ^ amount_mask.m_u64[0]; - crypto::scalar_t blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_BLINDING_MASK, h); // f = Hs(domain_sep, d, i) - out.amount_commitment = (de.amount * crypto::c_point_H + blinding_mask * crypto::c_point_G).to_public_key(); + out_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_BLINDING_MASK, h); // f = Hs(domain_sep, d, i) + out.amount_commitment = (crypto::c_scalar_1div8 * de.amount * crypto::c_point_H + crypto::c_scalar_1div8 * out_blinding_mask * crypto::c_point_G).to_public_key(); if (de.addr.front().is_auditable()) out.mix_attr = CURRENCY_TO_KEY_OUT_FORCED_NO_MIX; // override mix_attr to 1 for auditable target addresses @@ -1524,11 +1526,14 @@ namespace currency //fill outputs size_t output_index = tx.vout.size(); // in case of append mode we need to start output indexing from the last one + 1 std::set deriv_cache; + crypto::scalar_vec_t blinding_masks(destinations.size()); // vector of secret blinging masks for each output. For range proof generation + crypto::scalar_vec_t amounts(destinations.size()); // vector of amounts, converted to scalars. For rnage proof generation for(const tx_destination_entry& dst_entr : shuffled_dsts) { - CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount); - bool r = construct_tx_out(dst_entr, txkey.sec, output_index, tx, deriv_cache, sender_account_keys, result, tx_outs_attr); - CHECK_AND_ASSERT_MES(r, false, "Failed to construc tx out"); + CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount); // <<-- TODO @#@# consider removing this check + bool r = construct_tx_out(dst_entr, txkey.sec, output_index, tx, deriv_cache, sender_account_keys, blinding_masks[output_index], result, tx_outs_attr); + CHECK_AND_ASSERT_MES(r, false, "Failed to construct tx out"); + amounts[output_index] = dst_entr.amount; output_index++; summary_outs_money += dst_entr.amount; } @@ -3452,6 +3457,57 @@ namespace currency return false; } //-------------------------------------------------------------------------------- + bool generate_zarcanum_outs_range_proof(size_t out_index_start, size_t outs_count, const crypto::scalar_vec_t& amounts, const crypto::scalar_vec_t& blinding_masks, + const std::vector& vouts, zarcanum_outs_range_proof& result) + { + CHECK_AND_ASSERT_MES(amounts.size() == outs_count, false, ""); + CHECK_AND_ASSERT_MES(blinding_masks.size() == outs_count, false, ""); + CHECK_AND_ASSERT_MES(out_index_start + outs_count == vouts.size(), false, ""); + + std::vector commitments_1div8; + for(size_t out_index = out_index_start, i = 0; i < outs_count; ++out_index, ++i) + { + const tx_out_zarcanum& toz = boost::get(vouts[out_index]); // may throw an exception, only zarcanum outputs are exprected + const crypto::public_key* p = &toz.amount_commitment; + commitments_1div8.push_back(p); + } + + uint8_t err = 0; + bool r = crypto::bpp_gen<>(amounts, blinding_masks, commitments_1div8, result.bpp, &err); + CHECK_AND_ASSERT_MES(r, false, "bpp_gen failed with error " << err); + + return true; + } + //-------------------------------------------------------------------------------- + struct zarcanum_outs_range_proof_commit_ref_t + { + zarcanum_outs_range_proof_commit_ref_t(const zarcanum_outs_range_proof& range_proof, const std::vector& amount_commitments) + : range_proof(range_proof) + , amount_commitments(amount_commitments) + {} + const zarcanum_outs_range_proof& range_proof; + const std::vector& amount_commitments; + }; + + bool verify_multiple_zarcanum_outs_range_proofs(const std::vector& range_proofs) + { + std::vector sigs; + for(auto el : range_proofs) + sigs.emplace_back(el.range_proof.bpp, el.amount_commitments); + + uint8_t err = 0; + bool r = crypto::bpp_verify<>(sigs, &err); + CHECK_AND_ASSERT_MES(r, false, "bpp_vefiry failed with error " << err); + + return true; + } + //-------------------------------------------------------------------------------- + bool generate_zarcanum_signature(const crypto::hash& prefix_hash, const std::vector& sources, const txin_zarcanum_inputs& zins, zarcanum_sig& result) + { + return true; + } + //-------------------------------------------------------------------------------- + boost::multiprecision::uint1024_t get_a_to_b_relative_cumulative_difficulty(const wide_difficulty_type& difficulty_pos_at_split_point, const wide_difficulty_type& difficulty_pow_at_split_point, diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 47366931..756ee938 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -235,7 +235,7 @@ namespace currency //--------------------------------------------------------------- uint64_t get_string_uint64_hash(const std::string& str); - bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, const account_keys& self, finalized_tx& result, uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED); + bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, const account_keys& self, crypto::scalar_t& out_blinding_mask, finalized_tx& result, uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED); bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, const account_keys& self, uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED); bool validate_alias_name(const std::string& al); bool validate_password(const std::string& password);