diff --git a/src/currency_core/pos_mining.cpp b/src/currency_core/pos_mining.cpp new file mode 100644 index 00000000..8ecc7af4 --- /dev/null +++ b/src/currency_core/pos_mining.cpp @@ -0,0 +1,103 @@ +// Copyright (c) 2022 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// +#include "currency_basic.h" +#include "difficulty.h" +#include "pos_mining.h" +#include "wallet/wallet2.h" +#include "crypto/zarcanum.h" +#include "crypto_config.h" + +namespace currency +{ + void pos_mining_context::init(const wide_difficulty_type& pos_diff, const stake_modifier_type& sm, bool is_zarcanum) + { + this->basic_diff = pos_diff; + this->sk.stake_modifier = sm; + this->zarcanum = is_zarcanum; + + if (is_zarcanum) + { + this->last_pow_block_id_hashed = crypto::hash_helper_t::hs(CRYPTO_HDS_ZARCANUM_LAST_POW_HASH, this->sk.stake_modifier.last_pow_id); + this->z_l_div_z_D = crypto::zarcanum_precalculate_z_l_div_z_D(this->basic_diff); + } + } + + 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) + { + this->stake_amount = stake_amount; + this->sk.kimage = stake_out_ki; + + if (this->zarcanum) + { + crypto::scalar_t v = view_secret; + crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); + bool r = crypto::generate_key_derivation(stake_source_tx_pub_key, view_secret, derivation); // 8 * v * R + CHECK_AND_ASSERT_MES_NO_RET(r, "generate_key_derivation failed"); + crypto::scalar_t h = AUTO_VAL_INIT(h); + crypto::derivation_to_scalar(derivation, stake_out_in_tx_index, h.as_secret_key()); // h = Hs(8 * v * R, i) + + // 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; + } + } + + bool pos_mining_context::do_iteration(uint64_t ts) + { + // update stake kernel and calculate it's hash + this->sk.block_timestamp = ts; + { + PROFILE_FUNC("calc_hash"); + this->kernel_hash = crypto::cn_fast_hash(&this->sk, sizeof(this->sk)); + } + + bool found = false; + + if (this->zarcanum /* && td.is_zc() */) + { + crypto::mp::uint256_t lhs; + 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); + } + if (found) + { + found = true; + LOG_PRINT_GREEN("Found Zarcanum kernel: amount: " << currency::print_money_brief(this->stake_amount) << /* ", gindex: " << td.m_global_output_index << */ ENDL + << "difficulty: " << this->basic_diff << ENDL + << "kernel info: " << ENDL + << print_stake_kernel_info(this->sk) + << "kernel_hash: " << this->kernel_hash << ENDL + << "lhs: " << lhs << ENDL + << "rhs: " << rhs + , LOG_LEVEL_0); + + } + } + else + { + // old PoS with non-hidden amounts + currency::wide_difficulty_type final_diff = this->basic_diff / this->stake_amount; + { + PROFILE_FUNC("check_hash"); + found = currency::check_hash(this->kernel_hash, final_diff); + } + if (found) + { + LOG_PRINT_GREEN("Found kernel: amount: " << currency::print_money_brief(this->stake_amount)<< /* ", gindex: " << td.m_global_output_index << */ ENDL + << "difficulty: " << this->basic_diff << ", final_diff: " << final_diff << ENDL + << "kernel info: " << ENDL + << print_stake_kernel_info(this->sk) + << "kernel_hash(proof): " << this->kernel_hash, + LOG_LEVEL_0); + } + } + + return found; + } + +}; // namespace currency diff --git a/src/currency_core/pos_mining.h b/src/currency_core/pos_mining.h new file mode 100644 index 00000000..15c43314 --- /dev/null +++ b/src/currency_core/pos_mining.h @@ -0,0 +1,33 @@ +// Copyright (c) 2022 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 + +namespace currency +{ + + struct pos_mining_context + { + wide_difficulty_type basic_diff; + stake_kernel sk; + + crypto::scalar_t last_pow_block_id_hashed; // Zarcanum notation: f' + crypto::scalar_t secret_q; // Zarcanum notation: q + boost::multiprecision::uint256_t z_l_div_z_D; // Zarcanum notation: z * floor( l / (z * D) ) (max possible value (assuming z=2^64) : z * 2^252 / (z * 1) ~= 2^252) + crypto::hash kernel_hash; // Zarcanum notation: h + crypto::scalar_t stake_out_blinding_mask; // Zarcanum notation: f + + uint64_t stake_amount; + + bool zarcanum; + + void init(const wide_difficulty_type& pos_diff, const stake_modifier_type& sm, bool is_zarcanum); + + void 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); + + bool do_iteration(uint64_t ts); + }; + +}; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d45fecc7..69038ce1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3567,8 +3567,11 @@ bool wallet2::get_transfer_address(const std::string& adr_str, currency::account return m_core_proxy->get_transfer_address(adr_str, addr, payment_id); } //---------------------------------------------------------------------------------------------------- -bool wallet2::is_transfer_okay_for_pos(const transfer_details& tr, uint64_t& stake_unlock_time) const +bool wallet2::is_transfer_okay_for_pos(const transfer_details& tr, bool is_zarcanum_hf, uint64_t& stake_unlock_time) const { + if (is_zarcanum_hf && !tr.is_zc()) + return false; + if (!tr.is_spendable()) return false; @@ -3603,6 +3606,7 @@ void wallet2::get_mining_history(wallet_public::mining_history& hist, uint64_t t //---------------------------------------------------------------------------------------------------- size_t wallet2::get_pos_entries_count() { + bool is_zarcanum_hf = is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM); size_t counter = 0; for (size_t i = 0, size = m_transfers.size(); i < size; i++) @@ -3610,7 +3614,7 @@ size_t wallet2::get_pos_entries_count() auto& tr = m_transfers[i]; uint64_t stake_unlock_time = 0; - if (!is_transfer_okay_for_pos(tr, stake_unlock_time)) + if (!is_transfer_okay_for_pos(tr, is_zarcanum_hf, stake_unlock_time)) continue; ++counter; @@ -3621,12 +3625,13 @@ size_t wallet2::get_pos_entries_count() //---------------------------------------------------------------------------------------------------- bool wallet2::get_pos_entries(std::vector& entries) { + bool is_zarcanum_hf = is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM); for (size_t i = 0; i != m_transfers.size(); i++) { auto& tr = m_transfers[i]; uint64_t stake_unlock_time = 0; - if (!is_transfer_okay_for_pos(tr, stake_unlock_time)) + if (!is_transfer_okay_for_pos(tr, is_zarcanum_hf, stake_unlock_time)) continue; pos_entry pe = AUTO_VAL_INIT(pe); @@ -3819,26 +3824,17 @@ bool wallet2::fill_mining_context(mining_context& ctx) { currency::COMMAND_RPC_GET_POS_MINING_DETAILS::request pos_details_req = AUTO_VAL_INIT(pos_details_req); currency::COMMAND_RPC_GET_POS_MINING_DETAILS::response pos_details_resp = AUTO_VAL_INIT(pos_details_resp); - ctx.status = API_RETURN_CODE_NOT_FOUND; m_core_proxy->call_COMMAND_RPC_GET_POS_MINING_DETAILS(pos_details_req, pos_details_resp); if (pos_details_resp.status != API_RETURN_CODE_OK) return false; - ctx.basic_diff.assign(pos_details_resp.pos_basic_difficulty); - ctx.sk = AUTO_VAL_INIT(ctx.sk); - ctx.sk.stake_modifier = pos_details_resp.sm; - if (is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM)) - { - // Zarcanum (PoS with hidden amounts) - ctx.zarcanum = true; - ctx.last_pow_block_id_hashed = crypto::hash_helper_t::hs(CRYPTO_HDS_ZARCANUM_LAST_POW_HASH, ctx.sk.stake_modifier.last_pow_id); - ctx.z_l_div_z_D = crypto::zarcanum_precalculate_z_l_div_z_D(ctx.basic_diff); - } + ctx = mining_context{}; + ctx.init(wide_difficulty_type(pos_details_resp.pos_basic_difficulty), pos_details_resp.sm, is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM)); ctx.last_block_hash = pos_details_resp.last_block_hash; ctx.is_pos_allowed = pos_details_resp.pos_mining_allowed; ctx.starter_timestamp = pos_details_resp.starter_timestamp; - ctx.status = API_RETURN_CODE_OK; + ctx.status = API_RETURN_CODE_NOT_FOUND; return true; } //------------------------------------------------------------------ @@ -3885,82 +3881,17 @@ void wallet2::do_pos_mining_prepare_entry(mining_context& context, size_t transf CHECK_AND_ASSERT_MES_NO_RET(transfer_index < m_transfers.size(), "transfer_index is out of bounds: " << transfer_index); const transfer_details& td = m_transfers[transfer_index]; - // pre build kernel - context.sk.kimage = td.m_key_image; + crypto::scalar_t blinding_mask{}; + if (td.m_opt_blinding_mask) + blinding_mask = *td.m_opt_blinding_mask; - if (context.zarcanum) - { - crypto::scalar_t v = m_account.get_keys().view_secret_key; - crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); - bool r = crypto::generate_key_derivation(get_tx_pub_key_from_extra(td.m_ptx_wallet_info->m_tx), m_account.get_keys().view_secret_key, derivation); // 8 * v * R - CHECK_AND_ASSERT_MES_NO_RET(r, "generate_key_derivation failed"); - crypto::scalar_t h = AUTO_VAL_INIT(h); - crypto::derivation_to_scalar(derivation, td.m_internal_output_index, h.as_secret_key()); // h = Hs(8 * v * R, i) - - // q = Hs(domain_sep, Hs(8 * v * R, i) ) * 8 * v - context.secret_q = v * 8 * crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h); - } + context.prepare_entry(td.amount(), td.m_key_image, get_tx_pub_key_from_extra(td.m_ptx_wallet_info->m_tx), td.m_internal_output_index, + blinding_mask, m_account.get_keys().view_secret_key); } //------------------------------------------------------------------ bool wallet2::do_pos_mining_iteration(mining_context& context, size_t transfer_index, uint64_t ts) { - CHECK_AND_NO_ASSERT_MES(transfer_index < m_transfers.size(), false, "transfer_index is out of bounds: " << transfer_index); - const transfer_details& td = m_transfers[transfer_index]; - - // update stake kernel and calculate it's hash - context.sk.block_timestamp = ts; - { - PROFILE_FUNC("calc_hash"); - context.kernel_hash = crypto::cn_fast_hash(&context.sk, sizeof(context.sk)); - } - - const uint64_t stake_amount = td.amount(); - bool found = false; - - if (context.zarcanum && td.is_zc()) - { - crypto::mp::uint256_t lhs; - crypto::mp::uint512_t rhs; - { - PROFILE_FUNC("check_zarcanum"); - found = crypto::zarcanum_check_main_pos_inequality(context.kernel_hash, *td.m_opt_blinding_mask, context.secret_q, context.last_pow_block_id_hashed, context.z_l_div_z_D, stake_amount, lhs, rhs); - ++context.iterations_processed; - } - if (found) - { - found = true; - LOG_PRINT_GREEN("Found Zarcanum kernel: amount: " << currency::print_money_brief(stake_amount) << ", gindex: " << td.m_global_output_index << ENDL - << "difficulty: " << context.basic_diff << ENDL - << "kernel info: " << ENDL - << print_stake_kernel_info(context.sk) - << "kernel_hash: " << context.kernel_hash << ENDL - << "lhs: " << lhs << ENDL - << "rhs: " << rhs - , LOG_LEVEL_0); - - } - } - else - { - // old PoS with non-hidden amounts - currency::wide_difficulty_type final_diff = context.basic_diff / stake_amount; - { - PROFILE_FUNC("check_hash"); - found = currency::check_hash(context.kernel_hash, final_diff); - ++context.iterations_processed; - } - if (found) - { - LOG_PRINT_GREEN("Found kernel: amount: " << currency::print_money_brief(stake_amount)<< ", gindex: " << td.m_global_output_index << ENDL - << "difficulty: " << context.basic_diff << ", final_diff: " << final_diff << ENDL - << "kernel info: " << ENDL - << print_stake_kernel_info(context.sk) - << "kernel_hash(proof): " << context.kernel_hash, - LOG_LEVEL_0); - } - } - - return found; + return context.do_iteration(ts); } //------------------------------- bool wallet2::reset_history() diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index d12d1689..6d1d470a 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -43,6 +43,7 @@ #include "common/pod_array_file_container.h" #include "wallet_chain_shortener.h" #include "tor-connect/torlib/tor_lib_iface.h" +#include "currency_core/pos_mining.h" #define WALLET_DEFAULT_TX_SPENDABLE_AGE 10 @@ -412,28 +413,18 @@ namespace tools uint64_t m_unlock_time = 0; }; - struct mining_context + struct mining_context : public currency::pos_mining_context { std::string status; - bool is_pos_allowed = false;; - bool zarcanum = false; + bool is_pos_allowed = false; uint64_t index = 0; // index in m_transfers uint64_t stake_unlock_time = 0; - //uint64_t block_timestamp; uint64_t height = 0; uint64_t starter_timestamp = 0; crypto::hash last_block_hash = currency::null_hash; - crypto::scalar_t last_pow_block_id_hashed; // Zarcanum notation: f' - crypto::scalar_t secret_q; // Zarcanum notation: q - boost::multiprecision::uint256_t z_l_div_z_D; // Zarcanum notation: z * floor( l / (z * D) ) (max possible value (assuming z=2^64) : z * 2^252 / (z * 1) ~= 2^252) - crypto::hash kernel_hash; // Zarcanum notation: h - - currency::wide_difficulty_type basic_diff; - currency::stake_kernel sk; - uint64_t iterations_processed = 0; uint64_t total_items_checked = 0; uint64_t total_amount_checked = 0; @@ -968,7 +959,7 @@ private: std::string get_alias_for_address(const std::string& addr); std::vector get_aliases_for_address(const std::string& addr); bool is_connected_to_net(); - bool is_transfer_okay_for_pos(const transfer_details& tr, uint64_t& stake_unlock_time) const; + bool is_transfer_okay_for_pos(const transfer_details& tr, bool is_zarcanum_hf, uint64_t& stake_unlock_time) const; bool scan_unconfirmed_outdate_tx(); const currency::transaction& get_transaction_by_id(const crypto::hash& tx_hash); void rise_on_transfer2(const wallet_public::wallet_transfer_info& wti); @@ -1342,7 +1333,7 @@ namespace tools auto& tr = m_transfers[transfer_index]; uint64_t stake_unlock_time = 0; - if (!is_transfer_okay_for_pos(tr, stake_unlock_time)) + if (!is_transfer_okay_for_pos(tr, cxt.zarcanum, stake_unlock_time)) continue;