diff --git a/src/crypto/zarcanum.cpp b/src/crypto/zarcanum.cpp index ff500aae..043f4113 100644 --- a/src/crypto/zarcanum.cpp +++ b/src/crypto/zarcanum.cpp @@ -63,9 +63,9 @@ namespace crypto if (!(cond)) { LOG_PRINT_RED("zarcanum_generate_proof: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \ if (p_err) { *p_err = err_code; } return false; } - bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector& ring, + 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, + const scalar_t& secret_x, const scalar_t& secret_q, uint64_t secret_index, const crypto::scalar_t& stake_out_asset_id_blinding_mask, 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 */) { DBG_PRINT("zarcanum_generate_proof"); @@ -139,7 +139,7 @@ namespace crypto CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(bppe_gen(values, masks, masks2, E_1div8_vec_ptr, result.E_range_proof), 10); - // = four-layers ring signature data outline = + // = five-layers ring signature data outline = // (j in [0, ring_size-1]) // layer 0 ring // A[j] ( = ring[j].stealth_address) @@ -153,25 +153,35 @@ namespace crypto // layer 1 secret (with respect to G) // stake_blinding_mask - pseudo_out_blinding_mask // - // additional layers for Zarcanum: + // additional layer for confidential assets: // // layer 2 ring - // C - A[j] - Q[j] + // ring[j].blinded_asset_id - pseudo_out_blinded_asset_id // layer 2 secret (with respect to X) - // x0 + // -pseudo_out_asset_id_blinding_mask ( = -r'_i ) + // + // additional layers for Zarcanum: // // layer 3 ring + // C - A[j] - Q[j] + // layer 3 secret (with respect to X) + // x0 + // + // layer 4 ring // Q[j] - // layer 3 secret (with respect to G) + // layer 4 secret (with respect to G) // secret_q - point_t pseudo_out_amount_commitment = a * crypto::c_point_H + pseudo_out_blinding_mask * crypto::c_point_G; + // such pseudo_out_asset_id_blinding_mask effectively makes pseudo_out_blinded_asset_id == currency::native_coin_asset_id_pt == crypto::point_H + scalar_t pseudo_out_asset_id_blinding_mask = -stake_out_asset_id_blinding_mask; // T^p_i = T_i + (-r_i) * X = H_i + + point_t pseudo_out_amount_commitment = a * crypto::c_point_H + pseudo_out_blinding_mask * crypto::c_point_G; // A^p_i = a_i * H_i + f'_i * G result.pseudo_out_amount_commitment = (crypto::c_scalar_1div8 * pseudo_out_amount_commitment).to_public_key(); TRY_ENTRY() - CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(generate_CLSAG_GGXG(m, ring, pseudo_out_amount_commitment, C, stake_ki, - secret_x, stake_blinding_mask - pseudo_out_blinding_mask, x0, secret_q, secret_index, - result.clsag_ggxg), 20); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(generate_CLSAG_GGXXG(m, ring, pseudo_out_amount_commitment, crypto::c_point_H, C, stake_ki, + secret_x, stake_blinding_mask - pseudo_out_blinding_mask, -pseudo_out_asset_id_blinding_mask, x0, secret_q, secret_index, + result.clsag_ggxxg), 20); CATCH_ENTRY2(false); return true; @@ -179,12 +189,12 @@ namespace crypto #undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE - + #define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \ if (!(cond)) { LOG_PRINT_RED("zarcanum_verify_proof: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \ if (p_err) { *p_err = err_code; } return false; } - bool zarcanum_verify_proof(const hash& m, const hash& kernel_hash, const std::vector& ring, + 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 @@ -235,8 +245,10 @@ namespace crypto CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(bppe_verify(range_proofs), 10); + static public_key native_coin_asset_id = (crypto::c_scalar_1div8 * crypto::c_point_H).to_public_key(); // consider making it less ugly -- sowle + // check extended CLSAG-GGXG ring signature - CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(verify_CLSAG_GGXG(m, ring, sig.pseudo_out_amount_commitment, sig.C, stake_ki, sig.clsag_ggxg), 1); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(verify_CLSAG_GGXXG(m, ring, sig.pseudo_out_amount_commitment, native_coin_asset_id, sig.C, stake_ki, sig.clsag_ggxxg), 1); } CATCH_ENTRY_CUSTOM2({if (p_err) *p_err = 100;}, false) diff --git a/src/crypto/zarcanum.h b/src/crypto/zarcanum.h index 34a00d6e..9f881c28 100644 --- a/src/crypto/zarcanum.h +++ b/src/crypto/zarcanum.h @@ -41,16 +41,16 @@ namespace crypto bppe_signature E_range_proof; crypto::public_key pseudo_out_amount_commitment; // premultiplied by 1/8 - CLSAG_GGXG_signature clsag_ggxg; + CLSAG_GGXXG_signature clsag_ggxxg; }; - bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector& ring, + 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, + const scalar_t& secret_x, const scalar_t& secret_q, uint64_t secret_index, const crypto::scalar_t& stake_out_asset_id_blinding_mask, 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, + 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; diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 6fbbaee0..992e8cce 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -376,6 +376,28 @@ namespace currency if (ogc_ptr) *ogc_ptr = outs_gen_context; // TODO @#@# consider refactoring (a lot of copying) -- sowle + + if (tx.version > TRANSACTION_VERSION_PRE_HF4 && !pos) + { + // This is for PoW blocks only, because PoS blocks proofs are handled in wallet2::prepare_and_sign_pos_block() due to the necessity of making Zarcanum proofs first + // + // tx hash should be sealed by now + crypto::hash tx_id = get_transaction_hash(tx); + + //add range proofs + currency::zc_outs_range_proof range_proofs = AUTO_VAL_INIT(range_proofs); + bool r = generate_zc_outs_range_proof(tx_id, 0, outs_gen_context, tx.vout, range_proofs); + CHECK_AND_ASSERT_MES(r, false, "Failed to generate zc_outs_range_proof()"); + tx.proofs.emplace_back(std::move(range_proofs)); + + currency::zc_balance_proof balance_proof{}; + r = generate_tx_balance_proof(tx, tx_id, outs_gen_context, block_reward, balance_proof); + CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed"); + tx.proofs.emplace_back(std::move(balance_proof)); + } + + + return true; } //------------------------------------------------------------------ diff --git a/src/currency_core/pos_mining.cpp b/src/currency_core/pos_mining.cpp index a2b6237b..5bc1d973 100644 --- a/src/currency_core/pos_mining.cpp +++ b/src/currency_core/pos_mining.cpp @@ -25,7 +25,7 @@ namespace currency } void pos_mining_context::prepare_entry(uint64_t stake_amount, const crypto::key_image& stake_out_ki, const crypto::public_key& stake_source_tx_pub_key, uint64_t stake_out_in_tx_index, - const crypto::scalar_t& stake_out_blinding_mask, const crypto::secret_key& view_secret) + const crypto::scalar_t& stake_out_amount_blinding_mask, const crypto::secret_key& view_secret) { this->stake_amount = stake_amount; this->sk.kimage = stake_out_ki; @@ -41,7 +41,7 @@ namespace currency // q = Hs(domain_sep, Hs(8 * v * R, i) ) * 8 * v this->secret_q = v * 8 * crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h); - this->stake_out_blinding_mask = stake_out_blinding_mask; + this->stake_out_amount_blinding_mask = stake_out_amount_blinding_mask; } } @@ -62,7 +62,7 @@ namespace currency crypto::mp::uint512_t rhs; { PROFILE_FUNC("check_zarcanum"); - found = crypto::zarcanum_check_main_pos_inequality(this->kernel_hash, this->stake_out_blinding_mask, this->secret_q, this->last_pow_block_id_hashed, this->z_l_div_z_D, this->stake_amount, lhs, rhs); + found = crypto::zarcanum_check_main_pos_inequality(this->kernel_hash, this->stake_out_amount_blinding_mask, this->secret_q, this->last_pow_block_id_hashed, this->z_l_div_z_D, this->stake_amount, lhs, rhs); } if (found) { diff --git a/src/currency_core/pos_mining.h b/src/currency_core/pos_mining.h index 988eaf57..ae6baf03 100644 --- a/src/currency_core/pos_mining.h +++ b/src/currency_core/pos_mining.h @@ -9,15 +9,15 @@ namespace currency struct pos_mining_context { - // Zarcanum notation: - wide_difficulty_type basic_diff; // D + // Zarcanum notation: + wide_difficulty_type basic_diff; // D stake_kernel sk; - crypto::scalar_t last_pow_block_id_hashed; // f' - crypto::scalar_t secret_q; // q - boost::multiprecision::uint256_t z_l_div_z_D; // z * floor( l / (z * D) ) (max possible value (assuming z=2^64) : z * 2^252 / (z * 1) ~= 2^252) - crypto::hash kernel_hash; // h - crypto::scalar_t stake_out_blinding_mask; // f - uint64_t stake_amount; // a + crypto::scalar_t last_pow_block_id_hashed; // f' + crypto::scalar_t secret_q; // q + boost::multiprecision::uint256_t z_l_div_z_D; // z * floor( l / (z * D) ) (max possible value (assuming z=2^64) : z * 2^252 / (z * 1) ~= 2^252) + crypto::hash kernel_hash; // h + crypto::scalar_t stake_out_amount_blinding_mask; // f + uint64_t stake_amount; // a bool zarcanum; // false for pre-HF4 classic PoS with explicit amounts diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7efaf452..19d8758e 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3899,7 +3899,7 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl const tx_out_zarcanum& stake_out = boost::get(stake_out_v); COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response decoys_resp = AUTO_VAL_INIT(decoys_resp); - std::vector ring; + std::vector ring; uint64_t secret_index = 0; // index of the real stake output // get decoys outputs and construct miner tx @@ -3952,7 +3952,7 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl if (gindex == td.m_global_output_index) secret_index = i; ++i; - ring.emplace_back(el.stealth_address, el.amount_commitment, el.concealing_point); + ring.emplace_back(el.stealth_address, el.amount_commitment, el.blinded_asset_id, el.concealing_point); stake_input.key_offsets.push_back(el.global_amount_index); } r = absolute_sorted_output_offsets_to_relative_in_place(stake_input.key_offsets); @@ -3961,7 +3961,7 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl else { // no decoys, the ring consist of one element -- the real stake output - ring.emplace_back(stake_out.stealth_address, stake_out.amount_commitment, stake_out.concealing_point); + ring.emplace_back(stake_out.stealth_address, stake_out.amount_commitment, stake_out.blinded_asset_id, stake_out.concealing_point); stake_input.key_offsets.push_back(td.m_global_output_index); } stake_input.k_image = pe.keyimage; @@ -3969,16 +3969,21 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl #ifndef NDEBUG { crypto::point_t source_amount_commitment = crypto::c_scalar_1div8 * td.m_amount * crypto::c_point_H + crypto::c_scalar_1div8 * td.m_zc_info_ptr->amount_blinding_mask * crypto::c_point_G; - CHECK_AND_ASSERT_MES(stake_out.amount_commitment == source_amount_commitment.to_public_key(), false, "real output amount commitment check failed"); - CHECK_AND_ASSERT_MES(ring[secret_index].amount_commitment == stake_out.amount_commitment, false, "ring secret member doesn't match with the stake output"); + WLT_CHECK_AND_ASSERT_MES(stake_out.amount_commitment == source_amount_commitment.to_public_key(), false, "real output amount commitment check failed"); + WLT_CHECK_AND_ASSERT_MES(ring[secret_index].amount_commitment == stake_out.amount_commitment, false, "ring secret member doesn't match with the stake output"); + WLT_CHECK_AND_ASSERT_MES(cxt.stake_amount == td.m_amount, false, "stake_amount missmatch"); + //WLT_CHECK_AND_ASSERT_MES(source_amount_commitment == cxt.stake_amount * cxt.stake_out_blinded_asset_id + cxt.stake_out_amount_blinding_mask * crypto::c_point_G, false, "source_amount_commitment missmatch"); } #endif crypto::hash hash_for_zarcanum_sig = get_block_hash(b); + WLT_CHECK_AND_ASSERT_MES(miner_tx_ogc.pseudo_out_amount_blinding_masks_sum.is_zero(), false, "pseudo_out_amount_blinding_masks_sum is nonzero"); // it should be zero because there's only one input (stake), and thus one pseudo out + crypto::scalar_t pseudo_out_amount_blinding_mask = miner_tx_ogc.amount_blinding_masks_sum; // sum of outputs' amount blinding masks + uint8_t err = 0; r = crypto::zarcanum_generate_proof(hash_for_zarcanum_sig, cxt.kernel_hash, ring, cxt.last_pow_block_id_hashed, cxt.sk.kimage, - secret_x, cxt.secret_q, secret_index, -miner_tx_ogc.amount_blinding_masks_sum, cxt.stake_amount, cxt.stake_out_blinding_mask, + secret_x, cxt.secret_q, secret_index, td.m_zc_info_ptr->asset_id_blinding_mask, pseudo_out_amount_blinding_mask, cxt.stake_amount, cxt.stake_out_amount_blinding_mask, static_cast(sig), &err); WLT_CHECK_AND_ASSERT_MES(r, false, "zarcanum_generate_proof failed, err: " << (int)err); @@ -3991,14 +3996,14 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl // proofs for miner_tx currency::zc_outs_range_proof range_proofs = AUTO_VAL_INIT(range_proofs); r = generate_zc_outs_range_proof(miner_tx_id, 0, miner_tx_ogc, b.miner_tx.vout, range_proofs); - CHECK_AND_ASSERT_MES(r, false, "Failed to generate zc_outs_range_proof()"); + WLT_CHECK_AND_ASSERT_MES(r, false, "Failed to generate zc_outs_range_proof()"); b.miner_tx.proofs.emplace_back(std::move(range_proofs)); uint64_t block_reward = COIN; currency::zc_balance_proof balance_proof{}; r = generate_tx_balance_proof(b.miner_tx, miner_tx_id, miner_tx_ogc, block_reward, balance_proof); - CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed"); + WLT_CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed"); b.miner_tx.proofs.emplace_back(std::move(balance_proof)); return true; diff --git a/tests/core_tests/pos_block_builder.cpp b/tests/core_tests/pos_block_builder.cpp index 900e0d84..6cce63b5 100644 --- a/tests/core_tests/pos_block_builder.cpp +++ b/tests/core_tests/pos_block_builder.cpp @@ -221,18 +221,18 @@ void pos_block_builder::step5_sign(const currency::tx_source_entry& se, const cu size_t prepared_real_out_index = 0; std::vector prepared_outputs = prepare_outputs_entries_for_key_offsets(se.outputs, se.real_output, prepared_real_out_index); - std::vector ring; + std::vector ring; for(const auto& el : prepared_outputs) { stake_input.key_offsets.push_back(el.out_reference); - ring.emplace_back(el.stealth_address, el.amount_commitment, el.concealing_point); + ring.emplace_back(el.stealth_address, el.amount_commitment, el.blinded_asset_id, el.concealing_point); } crypto::hash tx_hash_for_sig = get_block_hash(m_block); uint8_t err = 0; r = crypto::zarcanum_generate_proof(tx_hash_for_sig, m_context.kernel_hash, ring, m_context.last_pow_block_id_hashed, m_context.sk.kimage, - secret_x, m_context.secret_q, prepared_real_out_index, -m_miner_tx_ogc.amount_blinding_masks_sum, m_context.stake_amount, m_context.stake_out_blinding_mask, + secret_x, m_context.secret_q, prepared_real_out_index, se.real_out_asset_id_blinding_mask, -m_miner_tx_ogc.amount_blinding_masks_sum, m_context.stake_amount, m_context.stake_out_amount_blinding_mask, static_cast(sig), &err); CHECK_AND_ASSERT_THROW_MES(r, "zarcanum_generate_proof failed, err: " << (int)err); } diff --git a/tests/core_tests/zarcanum_test.cpp b/tests/core_tests/zarcanum_test.cpp index f1d88db8..44a8a964 100644 --- a/tests/core_tests/zarcanum_test.cpp +++ b/tests/core_tests/zarcanum_test.cpp @@ -19,7 +19,7 @@ using namespace currency; //------------------------------------------------------------------------------ // helpers -void invalidate_CLSAG_GGXG_sig(crypto::CLSAG_GGXG_signature& sig) +void invalidate_CLSAG_GGXXG_sig(crypto::CLSAG_GGXXG_signature& sig) { sig.c = 7; } @@ -41,7 +41,7 @@ bool invalidate_zarcanum_sig(size_t n, zarcanum_sig& sig) case 0: break; case 1: invalidate_pub_key(sig.C); break; case 2: sig.c.make_random(); break; - case 3: invalidate_CLSAG_GGXG_sig(sig.clsag_ggxg); break; + case 3: invalidate_CLSAG_GGXXG_sig(sig.clsag_ggxxg); break; case 4: invalidate_pub_key(sig.C_prime); break; case 5: sig.d.make_random(); break; case 6: invalidate_pub_key(sig.E); break;