From 5fba04627bf68ea1f844cf8a389facd7bbf18970 Mon Sep 17 00:00:00 2001 From: sowle Date: Fri, 18 Nov 2022 04:18:41 +0100 Subject: [PATCH] the first working implementation of Zarcanum PoS verification + few bugs fixed in proof gen; zarcanum_pos_block_math test should now succeed --- src/crypto/zarcanum.cpp | 55 ++++++++++++++++++++++-- src/crypto/zarcanum.h | 1 + src/currency_core/blockchain_storage.cpp | 21 ++++++--- src/currency_core/blockchain_storage.h | 4 +- 4 files changed, 69 insertions(+), 12 deletions(-) diff --git a/src/crypto/zarcanum.cpp b/src/crypto/zarcanum.cpp index f9619b52..285ac505 100644 --- a/src/crypto/zarcanum.cpp +++ b/src/crypto/zarcanum.cpp @@ -14,6 +14,10 @@ namespace crypto const scalar_t c_zarcanum_z_coeff_s = { 0, 1, 0, 0 }; // c_scalar_2p64 const mp::uint256_t c_zarcanum_z_coeff_mp = c_zarcanum_z_coeff_s.as_boost_mp_type(); + #define DBG_VAL_PRINT(x) (void(0)) // std::cout << #x ": " << x << std::endl + #define DBG_PRINT(x) (void(0)) // std::cout << x << std::endl + + mp::uint256_t zarcanum_precalculate_l_div_z_D(const mp::uint128_t& pos_difficulty) { //LOG_PRINT_GREEN_L0(ENDL << "floor( l / (z * D) ) = " << c_scalar_L.as_boost_mp_type() / (c_zarcanum_z_coeff_mp * pos_difficulty)); @@ -51,6 +55,7 @@ namespace crypto 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 */) { + DBG_PRINT("zarcanum_generate_proof"); const scalar_t a = stake_amount; const scalar_t h = scalar_t(kernel_hash); const scalar_t f_plus_q = stake_blinding_mask + secret_q; @@ -84,6 +89,9 @@ namespace crypto point_t F = h * C_prime - dz * C + E + last_pow_block_id_hashed * h * c_point_H; + DBG_VAL_PRINT(h); DBG_VAL_PRINT(last_pow_block_id_hashed); DBG_VAL_PRINT(dz); + DBG_VAL_PRINT(C); DBG_VAL_PRINT(C_prime); DBG_VAL_PRINT(E); DBG_VAL_PRINT(F); + scalar_t r0 = scalar_t::random(); scalar_t r1 = scalar_t::random(); scalar_t r2 = scalar_t::random(); @@ -92,9 +100,9 @@ namespace crypto point_t R_01 = r0 * c_point_X + r1 * c_point_H_plus_G; point_t R_23 = r2 * c_point_X + r3 * c_point_H_minus_G; - point_t R_4 = r4 * c_point_G; + point_t R_4 = r4 * c_point_X; - hash_helper_t::hs_t hash_calc(3); + hash_helper_t::hs_t hash_calc(7); hash_calc.add_32_chars(CRYPTO_HDS_ZARCANUM_PROOF_HASH); hash_calc.add_point(R_01); hash_calc.add_point(R_23); @@ -165,21 +173,60 @@ namespace crypto 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 */) { + DBG_PRINT("zarcanum_verify_proof"); bool r = false; - // TODO @#@# + // make sure 0 < d <= l / floor(z * D) + const mp::uint256_t l_div_z_D_mp = crypto::zarcanum_precalculate_l_div_z_D(pos_difficulty); + const scalar_t l_div_z_D(l_div_z_D_mp); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(!sig.d.is_zero() && sig.d < l_div_z_D, 2); + const scalar_t dz = sig.d * c_zarcanum_z_coeff_s; - std::vector E_for_range_proof = { point_t(sig.E) }; + // calculate h + const scalar_t h = scalar_t(kernel_hash); + + // calculate F + point_t C_prime = point_t(sig.C_prime); + C_prime.modify_mul8(); + point_t C = point_t(sig.C); + C.modify_mul8(); + point_t E = point_t(sig.E); + E.modify_mul8(); + point_t F = h * C_prime - dz * C + E + last_pow_block_id_hashed * h * c_point_H; + + DBG_VAL_PRINT(h); DBG_VAL_PRINT(last_pow_block_id_hashed); DBG_VAL_PRINT(dz); + DBG_VAL_PRINT(C); DBG_VAL_PRINT(C_prime); DBG_VAL_PRINT(E); DBG_VAL_PRINT(F); + + // check three proofs with a shared Fiat-Shamir challenge c + point_t C_plus_C_prime = C + C_prime; + point_t C_minus_C_prime = C - C_prime; + hash_helper_t::hs_t hash_calc(7); + hash_calc.add_32_chars(CRYPTO_HDS_ZARCANUM_PROOF_HASH); + hash_calc.add_point(sig.y0 * c_point_X + sig.y1 * c_point_H_plus_G - sig.c * C_plus_C_prime); // y_0 * X + y1 (H + G) - c (C + C') + hash_calc.add_point(sig.y2 * c_point_X + sig.y3 * c_point_H_minus_G - sig.c * C_minus_C_prime); // y_2 * X + y3 (H - G) - c (C - C') + hash_calc.add_point(sig.y4 * c_point_X - sig.c * F); // y_4 * X - c * F + hash_calc.add_point(C_plus_C_prime); + hash_calc.add_point(C_minus_C_prime); + hash_calc.add_point(F); + scalar_t c_prime = hash_calc.calc_hash(); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sig.c == c_prime, 3); + + // check extended range proof for E + std::vector E_for_range_proof = { point_t(sig.E) }; // consider changing to 8*sig.E to avoid additional conversion std::vector range_proofs = { bppe_sig_commit_ref_t(sig.E_range_proof, E_for_range_proof) }; CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(bppe_verify>(range_proofs), 10); + // 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); return true; } + #undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE + } // namespace crypto diff --git a/src/crypto/zarcanum.h b/src/crypto/zarcanum.h index 86a85fce..0562efd0 100644 --- a/src/crypto/zarcanum.h +++ b/src/crypto/zarcanum.h @@ -52,6 +52,7 @@ namespace crypto 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); } // namespace crypto diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 69ccb968..c39cb778 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -5428,7 +5428,8 @@ bool blockchain_storage::validate_pos_block(const block& b, // build kernel and calculate hash stake_kernel sk = AUTO_VAL_INIT(sk); stake_modifier_type sm = AUTO_VAL_INIT(sm); - bool r = build_stake_modifier(sm, alt_chain, split_height); + uint64_t last_pow_block_height = 0; + bool r = build_stake_modifier(sm, alt_chain, split_height, nullptr, &last_pow_block_height); CHECK_AND_ASSERT_MES(r, false, "failed to build_stake_modifier"); r = build_kernel(stake_key_image, sk, sm, b.timestamp); CHECK_AND_ASSERT_MES(r, false, "failed to build kernel_stake"); @@ -5451,6 +5452,9 @@ bool blockchain_storage::validate_pos_block(const block& b, r = get_output_keys_for_input_with_checks(b.miner_tx, stake_input, dummy_output_keys, max_related_block_height, dummy_source_max_unlock_time_for_pos_coinbase_dummy, scan_contex); CHECK_AND_ASSERT_MES(r, false, "get_output_keys_for_input_with_checks failed for stake input"); CHECK_AND_ASSERT_MES(scan_contex.zc_outs.size() == stake_input.key_offsets.size(), false, "incorrect number of referenced outputs found: " << scan_contex.zc_outs.size() << ", while " << stake_input.key_offsets.size() << " is expected."); + // 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); + // build a ring of references vector ring; ring.reserve(scan_contex.zc_outs.size()); @@ -5460,10 +5464,8 @@ 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; - r = crypto::zarcanum_verify_proof(id, kernel_hash, ring, last_pow_block_id_hashed, stake_input.k_image, sig, &err); + r = crypto::zarcanum_verify_proof(id, kernel_hash, ring, last_pow_block_id_hashed, stake_input.k_image, basic_diff, sig, &err); CHECK_AND_ASSERT_MES(r, false, "zarcanum_verify_proof failed with code " << (int)err); - - final_diff = basic_diff; // just for logs return true; } else @@ -6097,10 +6099,12 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt int64_t ts_diff = actual_ts - m_core_runtime_config.get_core_time(); powpos_str_entry << "PoS:\t" << proof_hash << ", stake amount: "; if (pos_coinstake_amount != UINT64_MAX) + { powpos_str_entry << print_money_brief(pos_coinstake_amount); + powpos_str_entry << ", final_difficulty: " << this_coin_diff; + } else powpos_str_entry << "hidden"; - powpos_str_entry << ", final_difficulty: " << this_coin_diff; 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 << ")"; } else @@ -6451,7 +6455,9 @@ bool blockchain_storage::build_kernel(const block& bl, stake_kernel& kernel, uin return build_kernel(txin.k_image, kernel, stake_modifier, bl.timestamp); } //------------------------------------------------------------------ -bool blockchain_storage::build_stake_modifier(stake_modifier_type& sm, const alt_chain_type& alt_chain, uint64_t split_height, crypto::hash *p_last_block_hash /* = nullptr */) const +bool blockchain_storage::build_stake_modifier(stake_modifier_type& sm, const alt_chain_type& alt_chain, uint64_t split_height, + crypto::hash* p_last_block_hash /* = nullptr */, + uint64_t* p_last_pow_block_height /* = nullptr */ ) const { CRITICAL_REGION_LOCAL(m_read_lock); sm = stake_modifier_type(); @@ -6473,6 +6479,9 @@ bool blockchain_storage::build_stake_modifier(stake_modifier_type& sm, const alt if (p_last_block_hash != nullptr) *p_last_block_hash = get_block_hash(m_db_blocks.back()->bl); + if (p_last_pow_block_height != nullptr) + *p_last_pow_block_height = pbei_last_pow->height; + return true; } //------------------------------------------------------------------ diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index 3ae1bfea..7537919d 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -346,7 +346,7 @@ namespace currency stake_kernel& kernel, const stake_modifier_type& stake_modifier, uint64_t timestamp) const; - bool build_stake_modifier(stake_modifier_type& sm, const alt_chain_type& alt_chain = alt_chain_type(), uint64_t split_height = 0, crypto::hash *p_last_block_hash = nullptr) const; + bool build_stake_modifier(stake_modifier_type& sm, const alt_chain_type& alt_chain = alt_chain_type(), uint64_t split_height = 0, crypto::hash* p_last_block_hash = nullptr, uint64_t* p_last_pow_block_height = nullptr) const; bool validate_pos_coinbase_outs_unlock_time(const transaction& miner_tx, uint64_t staked_amount, uint64_t source_max_unlock_time)const; bool validate_pos_block(const block& b, const crypto::hash& id, bool for_altchain)const; @@ -660,7 +660,7 @@ namespace currency //bool resync_spent_tx_flags(); bool prune_ring_signatures_and_attachments_if_need(); bool prune_ring_signatures_and_attachments(uint64_t height, uint64_t& transactions_pruned, uint64_t& signatures_pruned, uint64_t& attachments_pruned); - // bool build_stake_modifier_for_alt(const alt_chain_type& alt_chain, stake_modifier_type& sm); + template bool enum_blockchain(visitor_t& v, const alt_chain_type& alt_chain = alt_chain_type(), uint64_t split_height = 0) const; bool update_spent_tx_flags_for_input(uint64_t amount, const txout_ref_v& o, bool spent);