From ea62c8833b6d7fccbd01d81ad9bf538ab18fc50c Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 16 Nov 2022 00:28:18 +0100 Subject: [PATCH 01/16] chaingen: pos_block_builder final touches --- tests/core_tests/pos_block_builder.cpp | 60 ++++---------------------- tests/core_tests/pos_block_builder.h | 2 - 2 files changed, 9 insertions(+), 53 deletions(-) diff --git a/tests/core_tests/pos_block_builder.cpp b/tests/core_tests/pos_block_builder.cpp index 9231b200..dd3ea7d4 100644 --- a/tests/core_tests/pos_block_builder.cpp +++ b/tests/core_tests/pos_block_builder.cpp @@ -139,8 +139,6 @@ void pos_block_builder::step3b( - - void pos_block_builder::step4_generate_coinbase_tx(size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, const account_public_address &reward_and_stake_receiver_address, @@ -199,6 +197,7 @@ void pos_block_builder::step4_generate_coinbase_tx(size_t median_size, } // supports Zarcanum and mixins +// (se.outputs can be unsorted) void pos_block_builder::step5_sign(const currency::tx_source_entry& se, const currency::account_keys& stakeholder_keys) { bool r = false; @@ -219,25 +218,28 @@ void pos_block_builder::step5_sign(const currency::tx_source_entry& se, const cu stake_input.k_image = m_context.sk.kimage; + 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; - for(const auto& el : se.outputs) + 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); } - r = absolute_sorted_output_offsets_to_relative_in_place(stake_input.key_offsets); - CHECK_AND_ASSERT_THROW_MES(r, "absolute_sorted_output_offsets_to_relative_in_place failed"); crypto::hash tx_hash_for_sig = get_transaction_hash(m_block.miner_tx); // TODO @#@# change to block hash after the corresponding test is made 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, se.real_output, m_blinding_masks_sum, m_context.stake_amount, m_context.stake_out_blinding_mask, + secret_x, m_context.secret_q, prepared_real_out_index, m_blinding_masks_sum, m_context.stake_amount, m_context.stake_out_blinding_mask, static_cast(sig), &err); CHECK_AND_ASSERT_THROW_MES(r, "zarcanum_generate_proof failed, err: " << (int)err); } else { + CHECK_AND_ASSERT_THROW_MES(se.outputs.size() == 1, "PoS blocks with NLSAG and mixing are not supported atm"); + // old PoS with non-hidden amounts NLSAG_sig& sig = boost::get(m_block.miner_tx.signatures[0]); txin_to_key& stake_input = boost::get(m_block.miner_tx.vin[1]); @@ -255,7 +257,7 @@ void pos_block_builder::step5_sign(const currency::tx_source_entry& se, const cu m_step = 5; } - +// pre-Zarcanum sign function void pos_block_builder::step5_sign(const crypto::public_key& stake_tx_pub_key, size_t stake_tx_out_index, const crypto::public_key& stake_tx_out_pub_key, const currency::account_base& stakeholder_account) { @@ -269,47 +271,3 @@ void pos_block_builder::step5_sign(const crypto::public_key& stake_tx_pub_key, s step5_sign(se, stakeholder_account.get_keys()); } - - -bool mine_next_pos_block_in_playtime_sign_cb(currency::core& c, const currency::block& prev_block, const currency::block& coinstake_scr_block, const currency::account_base& acc, - std::function before_sign_cb, currency::block& output) -{ - blockchain_storage& bcs = c.get_blockchain_storage(); - - // these values (median and diff) are correct only for the next main chain block, it's incorrect for altblocks, especially for old altblocks - // but for now we assume they will work fine - uint64_t block_size_median = bcs.get_current_comulative_blocksize_limit() / 2; - currency::wide_difficulty_type difficulty = bcs.get_next_diff_conditional(true); - - crypto::hash prev_id = get_block_hash(prev_block); - size_t height = get_block_height(prev_block) + 1; - - block_extended_info bei = AUTO_VAL_INIT(bei); - bool r = bcs.get_block_extended_info_by_hash(prev_id, bei); - CHECK_AND_ASSERT_MES(r, false, "get_block_extended_info_by_hash failed for hash = " << prev_id); - - - const transaction& stake = coinstake_scr_block.miner_tx; - crypto::public_key stake_tx_pub_key = get_tx_pub_key_from_extra(stake); - size_t stake_output_idx = 0; - size_t stake_output_gidx = 0; - uint64_t stake_output_amount =boost::get( stake.vout[stake_output_idx]).amount; - crypto::key_image stake_output_key_image; - keypair kp; - generate_key_image_helper(acc.get_keys(), stake_tx_pub_key, stake_output_idx, kp, stake_output_key_image); - crypto::public_key stake_output_pubkey = boost::get(boost::get(stake.vout[stake_output_idx]).target).key; - - pos_block_builder pb; - pb.step1_init_header(bcs.get_core_runtime_config().hard_forks, height, prev_id); - pb.step2_set_txs(std::vector()); - pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, difficulty, prev_id, null_hash, prev_block.timestamp); - pb.step4_generate_coinbase_tx(block_size_median, bei.already_generated_coins, acc.get_public_address()); - - if (!before_sign_cb(pb.m_block)) - return false; - - pb.step5_sign(stake_tx_pub_key, stake_output_idx, stake_output_pubkey, acc); - output = pb.m_block; - - return true; -} diff --git a/tests/core_tests/pos_block_builder.h b/tests/core_tests/pos_block_builder.h index d42c67e1..0d7297f6 100644 --- a/tests/core_tests/pos_block_builder.h +++ b/tests/core_tests/pos_block_builder.h @@ -69,8 +69,6 @@ struct pos_block_builder void step5_sign(const crypto::public_key& stake_tx_pub_key, size_t stake_tx_out_index, const crypto::public_key& stake_tx_out_pub_key, const currency::account_base& stakeholder_account); - //void step5_sign_zarcanum(const crypto::public_key& stake_tx_pub_key, size_t stake_tx_out_index, const currency::account_base& stakeholder_account); - currency::block m_block {}; size_t m_step = 0; From 5e6103b38baf59c7c1f2384c1e5cd05b212d6628 Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 16 Nov 2022 01:11:53 +0100 Subject: [PATCH 02/16] chaingen: pos_block_builder final touches 2 --- tests/core_tests/checkpoints_tests.cpp | 47 ++++++++++++++++++++++++++ tests/core_tests/pos_block_builder.h | 22 ------------ 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/tests/core_tests/checkpoints_tests.cpp b/tests/core_tests/checkpoints_tests.cpp index 87218fe0..42665344 100644 --- a/tests/core_tests/checkpoints_tests.cpp +++ b/tests/core_tests/checkpoints_tests.cpp @@ -13,6 +13,53 @@ using namespace epee; using namespace crypto; using namespace currency; +// helpers + +bool mine_next_pos_block_in_playtime_sign_cb(currency::core& c, const currency::block& prev_block, const currency::block& coinstake_scr_block, const currency::account_base& acc, + std::function before_sign_cb, currency::block& output) +{ + blockchain_storage& bcs = c.get_blockchain_storage(); + + // these values (median and diff) are correct only for the next main chain block, it's incorrect for altblocks, especially for old altblocks + // but for now we assume they will work fine + uint64_t block_size_median = bcs.get_current_comulative_blocksize_limit() / 2; + currency::wide_difficulty_type difficulty = bcs.get_next_diff_conditional(true); + + crypto::hash prev_id = get_block_hash(prev_block); + size_t height = get_block_height(prev_block) + 1; + + block_extended_info bei = AUTO_VAL_INIT(bei); + bool r = bcs.get_block_extended_info_by_hash(prev_id, bei); + CHECK_AND_ASSERT_MES(r, false, "get_block_extended_info_by_hash failed for hash = " << prev_id); + + + const transaction& stake = coinstake_scr_block.miner_tx; + crypto::public_key stake_tx_pub_key = get_tx_pub_key_from_extra(stake); + size_t stake_output_idx = 0; + size_t stake_output_gidx = 0; + uint64_t stake_output_amount =boost::get( stake.vout[stake_output_idx]).amount; + crypto::key_image stake_output_key_image; + keypair kp; + generate_key_image_helper(acc.get_keys(), stake_tx_pub_key, stake_output_idx, kp, stake_output_key_image); + crypto::public_key stake_output_pubkey = boost::get(boost::get(stake.vout[stake_output_idx]).target).key; + + pos_block_builder pb; + pb.step1_init_header(bcs.get_core_runtime_config().hard_forks, height, prev_id); + pb.step2_set_txs(std::vector()); + pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, difficulty, prev_id, null_hash, prev_block.timestamp); + pb.step4_generate_coinbase_tx(block_size_median, bei.already_generated_coins, acc.get_public_address()); + + if (!before_sign_cb(pb.m_block)) + return false; + + pb.step5_sign(stake_tx_pub_key, stake_output_idx, stake_output_pubkey, acc); + output = pb.m_block; + + return true; +} + +//------------------------------------------------------------------------------ + checkpoints_test::checkpoints_test() { REGISTER_CALLBACK_METHOD(checkpoints_test, set_checkpoint); diff --git a/tests/core_tests/pos_block_builder.h b/tests/core_tests/pos_block_builder.h index 0d7297f6..581f277f 100644 --- a/tests/core_tests/pos_block_builder.h +++ b/tests/core_tests/pos_block_builder.h @@ -82,25 +82,3 @@ struct pos_block_builder currency::pos_mining_context m_context {}; }; - -/* bool construct_homemade_pos_miner_tx(bool zarcanum, size_t height, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, - size_t current_block_size, - uint64_t fee, - uint64_t pos_stake_amount, - crypto::key_image pos_stake_keyimage, - size_t pos_stake_gindex, - const currency::account_public_address &reward_receiving_address, - const currency::account_public_address &stakeholder_address, - currency::transaction& tx, - const currency::blobdata& extra_nonce = currency::blobdata(), - size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS, - currency::keypair tx_one_time_key = currency::keypair::generate()); */ - -bool mine_next_pos_block_in_playtime_sign_cb(currency::core& c, const currency::block& prev_block, const currency::block& coinstake_scr_block, const currency::account_base& acc, - std::function before_sign_cb, currency::block& output); - -inline bool mine_next_pos_block_in_playtime(currency::core& c, const currency::block& prev_block, const currency::block& coinstake_scr_block, const currency::account_base& acc, - std::function before_sign_cb, currency::block& output) -{ - return mine_next_pos_block_in_playtime_sign_cb(c, prev_block, coinstake_scr_block, acc, [](currency::block&){ return true; }, output); -} From 94c9c0cebca8f40c7be495b1f67c3cc16df35ae3 Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 16 Nov 2022 02:31:22 +0100 Subject: [PATCH 03/16] chaingen: 1) added support of mixins for build_wallets (via proxy stub); 2) add_block_info adatped for zc inputs + minor improvements --- tests/core_tests/chaingen.cpp | 84 ++++++++++++++++++++++++++++++----- tests/core_tests/chaingen.h | 13 ++++-- 2 files changed, 82 insertions(+), 15 deletions(-) diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index d1f93321..8c9e3084 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -193,7 +193,7 @@ bool test_generator::add_block_info(const currency::block& b, const std::listb); - sk.kimage = boost::get(b.miner_tx.vin[1]).k_image; + sk.kimage = get_key_image_from_txin_v(b.miner_tx.vin[1]); sk.block_timestamp = b.timestamp; sk_hash = crypto::cn_fast_hash(&sk, sizeof(sk)); } @@ -259,10 +259,9 @@ bool test_generator::construct_block(currency::block& blk, build_outputs_indext_for_chain(blocks, oi, txs_outs); //build wallets - build_wallets(blocks, coin_stake_sources, txs_outs, wallets); + build_wallets(blocks, coin_stake_sources, txs_outs, oi, wallets); r = find_kernel(coin_stake_sources, blocks, - oi, wallets, pe, won_walled_index, @@ -361,6 +360,7 @@ bool test_generator::sign_block(const tools::wallet2::mining_context& mining_con bool test_generator::build_wallets(const blockchain_vector& blockchain, const std::list& accs, const tx_global_indexes& txs_outs, + const outputs_index& oi, wallets_vector& wallets, const core_runtime_config& cc) { @@ -369,10 +369,12 @@ bool test_generator::build_wallets(const blockchain_vector& blockchain, const tx_global_indexes& m_txs_outs; const blockchain_vector& m_blockchain; const core_runtime_config& m_core_runtime_config; + const outputs_index& m_outputs_index; - stub_core_proxy(const blockchain_vector& blockchain, const tx_global_indexes& txs_outs, const core_runtime_config& crc) + stub_core_proxy(const blockchain_vector& blockchain, const tx_global_indexes& txs_outs, const outputs_index& oi, const core_runtime_config& crc) : m_blockchain(blockchain) , m_txs_outs(txs_outs) + , m_outputs_index(oi) , m_core_runtime_config(crc) {} @@ -412,9 +414,68 @@ bool test_generator::build_wallets(const blockchain_vector& blockchain, return true; } - }; + bool call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& rqt, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& rsp) override + { + for (uint64_t amount : rqt.amounts) + { + rsp.outs.resize(rsp.outs.size() + 1); + auto& rsp_entry = rsp.outs.back(); + rsp_entry.amount = amount; + auto it = m_outputs_index.find(amount); + if (it == m_outputs_index.end()) + continue; - std::shared_ptr tmp_proxy(new stub_core_proxy(blockchain, txs_outs, cc)); + std::vector random_mapping(it->second.size()); + for(size_t i = 0; i < random_mapping.size(); ++i) + random_mapping[i] = i; + std::shuffle(random_mapping.begin(), random_mapping.end(), crypto::uniform_random_bit_generator()); + + for (size_t gindex : random_mapping) + { + const out_index_info& oii = it->second[gindex]; + if (rqt.height_upper_limit != 0 && oii.block_height > rqt.height_upper_limit) + continue; + const transaction& tx = oii.in_block_tx_index == 0 ? m_blockchain[oii.block_height]->b.miner_tx : m_blockchain[oii.block_height]->m_transactions[oii.in_block_tx_index]; + auto& out_v = tx.vout[oii.in_tx_out_index]; + uint8_t mix_attr = 0; + if (!get_mix_attr_from_tx_out_v(out_v, mix_attr)) + continue; + if (mix_attr == CURRENCY_TO_KEY_OUT_FORCED_NO_MIX) + continue; + if (rqt.use_forced_mix_outs && mix_attr == CURRENCY_TO_KEY_OUT_RELAXED) + continue; + if (mix_attr != CURRENCY_TO_KEY_OUT_RELAXED && mix_attr > rqt.decoys_count) + continue; + + if (amount == 0 && out_v.type() == typeid(tx_out_zarcanum)) + { + const tx_out_zarcanum& out_zc = boost::get(out_v); + rsp_entry.outs.emplace_back(gindex, out_zc.stealth_address, out_zc.amount_commitment, out_zc.concealing_point); + } + else if (amount != 0 && out_v.type() == typeid(tx_out_bare)) + { + txout_target_v out_tv = boost::get(out_v).target; + if (out_tv.type() != typeid(txout_to_key)) + continue; + rsp_entry.outs.emplace_back(gindex, boost::get(out_tv).key); + } + if (rsp_entry.outs.size() >= rqt.decoys_count) + break; + } + if (rsp_entry.outs.size() < rqt.decoys_count) + { + rsp.status = API_RETURN_CODE_NOT_ENOUGH_OUTPUTS_FOR_MIXING; + return true; + } + } + + rsp.status = API_RETURN_CODE_OK; + return true; + } + + }; // struct stub_core_proxy + + std::shared_ptr tmp_proxy(new stub_core_proxy(blockchain, txs_outs, oi, cc)); //build wallets wallets.clear(); @@ -471,7 +532,7 @@ bool test_generator::build_wallets(const crypto::hash& blockchain_head, const st get_block_chain(blocks, blockchain_head, std::numeric_limits::max()); build_outputs_indext_for_chain(blocks, oi, txs_outs); - return build_wallets(blocks, accounts, txs_outs, wallets, cc); + return build_wallets(blocks, accounts, txs_outs, oi, wallets, cc); } size_t test_generator::get_tx_out_gindex(const crypto::hash& blockchain_head, const crypto::hash& tx_hash, const size_t output_index) const @@ -509,7 +570,6 @@ uint64_t test_generator::get_timestamps_median(const crypto::hash& blockchain_he bool test_generator::find_kernel(const std::list& accs, const blockchain_vector& blck_chain, - const outputs_index& indexes, wallets_vector& wallets, currency::pos_entry& pe, size_t& found_wallet_index, @@ -577,7 +637,7 @@ bool test_generator::build_outputs_indext_for_chain(const blockchain_vector& blo { uint64_t amount = get_amount_from_variant(blocks[h]->b.miner_tx.vout[out_i]); coinbase_outs.push_back(index[amount].size()); - index[amount].push_back(std::tuple(h, 0, out_i)); + index[amount].push_back(out_index_info{h, 0, out_i}); } for (size_t tx_index = 0; tx_index != blocks[h]->m_transactions.size(); tx_index++) @@ -587,7 +647,7 @@ bool test_generator::build_outputs_indext_for_chain(const blockchain_vector& blo { uint64_t amount = get_amount_from_variant(blocks[h]->m_transactions[tx_index].vout[out_i]); tx_outs_indx.push_back(index[amount].size()); - index[amount].push_back(std::tuple(h, tx_index + 1, out_i)); + index[amount].push_back(out_index_info{h, tx_index + 1, out_i}); } } } @@ -932,7 +992,7 @@ bool test_generator::construct_pow_block_with_alias_info_in_coinbase(const accou struct output_index { const currency::tx_out_v out_v; - uint64_t amount; + uint64_t amount; // actual amount (decoded, cannot be zero) size_t tx_no; // index of transaction in block size_t out_no; // index of out in transaction size_t idx; // global index @@ -969,7 +1029,7 @@ struct output_index }*/ }; -typedef std::map > map_output_t; // amount -> [N -> global out index] +typedef std::map > map_output_t; // amount -> [N -> global out index] (for 'my' outputs, no specific order) typedef std::map > map_output_idx_t; // amount -> [global out index -> 'output_index'] typedef std::pair outloc_t; diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 32fa8f86..c4abf92a 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -375,9 +375,16 @@ public: crypto::hash ks_hash; }; - // amount vec_ind, tx_index, out index in tx - typedef std::map > > outputs_index; - typedef std::unordered_map > tx_global_indexes; + + struct out_index_info + { + size_t block_height; + size_t in_block_tx_index; + size_t in_tx_out_index; + }; + + typedef std::map > outputs_index; // amount -> [gindex -> out_index_info] + typedef std::unordered_map > tx_global_indexes; // tx_hash -> vector of tx's outputs global indices typedef std::vector blockchain_vector; From 8543bb187f4a764b2d333ac44ad88213f94a492a Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 16 Nov 2022 03:15:31 +0100 Subject: [PATCH 04/16] compilation fix --- tests/core_tests/chaingen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index c4abf92a..569add6e 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -428,7 +428,6 @@ public: bool find_kernel(const std::list& accs, const blockchain_vector& blck_chain, - const outputs_index& indexes, wallets_vector& wallets, currency::pos_entry& pe, size_t& found_wallet_index, @@ -438,6 +437,7 @@ public: bool build_wallets(const blockchain_vector& blocks, const std::list& accs, const tx_global_indexes& txs_outs, + const outputs_index& oi, wallets_vector& wallets, const currency::core_runtime_config& cc = currency::get_default_core_runtime_config()); From 51fbe0fc194f46196e63e59f2d7647640426f870 Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 16 Nov 2022 03:16:50 +0100 Subject: [PATCH 05/16] compilation fix --- src/rpc/core_rpc_server_commands_defs.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 4b737eba..8245256f 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -319,7 +319,7 @@ namespace currency { std::list amounts; uint64_t decoys_count; // how many decoy outputs needed (per amount) - uint64_t height_upper_limit; // all the decoy outputs must be either older than, or the same age as this height + uint64_t height_upper_limit; // if nonzero, all the decoy outputs must be either older than, or the same age as this height bool use_forced_mix_outs; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(amounts) @@ -336,6 +336,9 @@ namespace currency out_entry(uint64_t global_amount_index, const crypto::public_key& stealth_address, const crypto::public_key& amount_commitment, const crypto::public_key& concealing_point) : global_amount_index(global_amount_index), stealth_address(stealth_address), amount_commitment(amount_commitment), concealing_point(concealing_point) {} + out_entry(uint64_t global_amount_index, const crypto::public_key& stealth_address) + : global_amount_index(global_amount_index), stealth_address(stealth_address), amount_commitment{}, concealing_point{} + {} uint64_t global_amount_index; crypto::public_key stealth_address; crypto::public_key concealing_point; From a7198b33135d2c9eb6f1c70b329cb5574d2e65e0 Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 16 Nov 2022 03:34:42 +0100 Subject: [PATCH 06/16] coretests: get_params_for_next_pos_block() + zarcanum_test_pos_math finally makes custom and normal gen-time PoS Zarcanum blocks --- tests/core_tests/chaingen.cpp | 24 +++++++++++ tests/core_tests/chaingen.h | 1 + tests/core_tests/zarcanum_test.cpp | 66 +++++++++++++++--------------- 3 files changed, 59 insertions(+), 32 deletions(-) diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 8c9e3084..ba029a86 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -712,6 +712,7 @@ bool test_generator::build_stake_modifier(stake_modifier_type& sm, const test_ge return true; } + currency::wide_difficulty_type test_generator::get_difficulty_for_next_block(const crypto::hash& head_id, bool pow) const { std::vector blocks; @@ -720,6 +721,29 @@ currency::wide_difficulty_type test_generator::get_difficulty_for_next_block(con return get_difficulty_for_next_block(blocks, pow); } + +bool test_generator::get_params_for_next_pos_block(const crypto::hash& head_id, currency::wide_difficulty_type& pos_difficulty, crypto::hash& last_pow_block_hash, + crypto::hash& last_pos_block_kernel_hash) const +{ + std::vector blocks; + get_block_chain(blocks, head_id, std::numeric_limits::max()); + + pos_difficulty = get_difficulty_for_next_block(blocks, false); + + uint64_t pos_idx = get_last_block_of_type(true, blocks); + if (pos_idx != 0) + last_pos_block_kernel_hash = blocks[pos_idx]->ks_hash; + + uint64_t pow_idx = get_last_block_of_type(false, blocks); + if (pow_idx == 0) + return false; + + last_pow_block_hash = get_block_hash(blocks[pow_idx]->b); + + return true; +} + + /* static */ currency::wide_difficulty_type test_generator::get_difficulty_for_next_block(const std::vector& blocks, bool pow) { std::vector timestamps; diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 569add6e..981aad40 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -413,6 +413,7 @@ public: //----------- static currency::wide_difficulty_type get_difficulty_for_next_block(const std::vector& blocks, bool pow = true); currency::wide_difficulty_type get_difficulty_for_next_block(const crypto::hash& head_id, bool pow = true) const; + bool get_params_for_next_pos_block(const crypto::hash& head_id, currency::wide_difficulty_type& pos_difficulty, crypto::hash& last_pow_block_hash, crypto::hash& last_pos_block_kernel_hash) const; currency::wide_difficulty_type get_cumul_difficulty_for_next_block(const crypto::hash& head_id, bool pow = true) const; void get_block_chain(std::vector& blockchain, const crypto::hash& head, size_t n) const; void get_last_n_block_sizes(std::vector& block_sizes, const crypto::hash& head, size_t n) const; diff --git a/tests/core_tests/zarcanum_test.cpp b/tests/core_tests/zarcanum_test.cpp index 15bf75ba..d86718ca 100644 --- a/tests/core_tests/zarcanum_test.cpp +++ b/tests/core_tests/zarcanum_test.cpp @@ -347,63 +347,65 @@ bool zarcanum_pos_block_math::generate(std::vector& events) co MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time()); DO_CALLBACK(events, "configure_core"); // necessary to set m_hardforks MAKE_NEXT_BLOCK(events, blk_1, blk_0, miner_acc); - REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 10); - //generator.get_tx_out_gindex - - // try to make a PoS block with locked stake after the hardfork + std::vector sources; block blk_1_pos; { - const block& prev_block = blk_1r; - const transaction& stake_tx = blk_1.miner_tx; + const block& prev_block = blk_1r; const account_base& stake_acc = miner_acc; - size_t stake_out_in_tx_index = 0; - size_t stake_output_gindex = 0; + uint64_t amount_to_find = COIN; + size_t nmix = 10; - crypto::hash prev_id = get_block_hash(prev_block); size_t height = get_block_height(prev_block) + 1; - currency::wide_difficulty_type diff = generator.get_difficulty_for_next_block(prev_id, false); - crypto::public_key stake_tx_pub_key = get_tx_pub_key_from_extra(stake_tx); + crypto::hash prev_id = get_block_hash(prev_block); + r = fill_tx_sources(sources, events, prev_block, stake_acc.get_keys(), amount_to_find, nmix, true, true, false); + CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed"); + CHECK_AND_ASSERT_MES(shuffle_source_entries(sources), false, ""); + auto it = std::max_element(sources.begin(), sources.end(), [&](const tx_source_entry& lhs, const tx_source_entry& rhs){ return lhs.amount < rhs.amount; }); + const tx_source_entry& se = *it; + const tx_source_entry::output_entry& oe = se.outputs[se.real_output]; + + crypto::key_image stake_output_key_image {}; + currency::keypair ephemeral_keys {}; + r = generate_key_image_helper(stake_acc.get_keys(), se.real_out_tx_key, se.real_output_in_tx_index, ephemeral_keys, stake_output_key_image); + CHECK_AND_ASSERT_MES(r, false, "generate_key_image_helper failed"); + uint64_t stake_output_gindex = boost::get(oe.out_reference); + + currency::wide_difficulty_type pos_diff{}; + crypto::hash last_pow_block_hash{}, last_pos_block_kernel_hash{}; + r = generator.get_params_for_next_pos_block(prev_id, pos_diff, last_pow_block_hash, last_pos_block_kernel_hash); + CHECK_AND_ASSERT_MES(r, false, "get_params_for_next_pos_block failed"); pos_block_builder pb; pb.step1_init_header(generator.get_hardforks(), height, prev_id); pb.step2_set_txs(std::vector()); - - pb.step3a(diff, prev_id, null_hash); - crypto::key_derivation derivation {}; - crypto::scalar_t stake_out_blinding_mask {}; - uint64_t stake_output_amount = 0; - crypto::public_key stealth_address {}; - crypto::secret_key secret_x {}; - crypto::key_image stake_output_key_image {}; + pb.step3a(pos_diff, last_pow_block_hash, last_pos_block_kernel_hash); - r = generate_key_derivation(stake_tx_pub_key, stake_acc.get_keys().view_secret_key, derivation); - CHECK_AND_ASSERT_MES(r, false, "generate_key_derivation failed"); - r = is_out_to_acc(stake_acc.get_public_address(), boost::get(stake_tx.vout[stake_out_in_tx_index]), derivation, stake_out_in_tx_index, stake_output_amount, stake_out_blinding_mask); - CHECK_AND_ASSERT_MES(r, false, "is_out_to_acc failed"); - r = crypto::derive_public_key(derivation, stake_out_in_tx_index, stake_acc.get_public_address().spend_public_key, stealth_address); - CHECK_AND_ASSERT_MES(r, false, "derive_public_key failed"); - crypto::derive_secret_key(derivation, stake_out_in_tx_index, stake_acc.get_keys().spend_secret_key, secret_x); - crypto::generate_key_image(stealth_address, secret_x, stake_output_key_image); - - - pb.step3b(stake_output_amount, stake_output_key_image, stake_tx_pub_key, stake_out_in_tx_index, stake_out_blinding_mask, stake_acc.get_keys().view_secret_key, + pb.step3b(se.amount, stake_output_key_image, se.real_out_tx_key, se.real_output_in_tx_index, se.real_out_amount_blinding_mask, stake_acc.get_keys().view_secret_key, stake_output_gindex, prev_block.timestamp, POS_SCAN_WINDOW, POS_SCAN_STEP); pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), stake_acc.get_public_address()); - pb.step5_sign(stake_tx_pub_key, stake_out_in_tx_index, stake_tx_pub_key, stake_acc); - + pb.step5_sign(se, stake_acc.get_keys()); blk_1_pos = pb.m_block; } + generator.add_block_info(blk_1_pos, std::list{}); ADD_CUSTOM_EVENT(events, blk_1_pos); + std::list miner_stake_sources( {miner_acc} ); + MAKE_NEXT_POS_BLOCK(events, blk_2, blk_1_pos, miner_acc, miner_stake_sources); + + MAKE_NEXT_POS_BLOCK(events, blk_3, blk_2, miner_acc, miner_stake_sources); + MAKE_NEXT_POS_BLOCK(events, blk_4, blk_3, miner_acc, miner_stake_sources); + return true; } + //------------------------------------------------------------------------------ zarcanum_txs_with_big_shuffled_decoy_set_shuffled::zarcanum_txs_with_big_shuffled_decoy_set_shuffled() From 93a1aacaca5701be3d23a01a1b7923823a5c7cf3 Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 16 Nov 2022 21:56:32 +0100 Subject: [PATCH 07/16] coretests: zarcanum_pos_math added to the list, now it exposes a bug with PoS block validation --- tests/core_tests/chaingen_main.cpp | 1 + tests/core_tests/zarcanum_test.cpp | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index cb529b13..64d259db 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1082,6 +1082,7 @@ int main(int argc, char* argv[]) //GENERATE_AND_PLAY(zarcanum_test_n_inputs_validation); GENERATE_AND_PLAY(zarcanum_gen_time_balance); GENERATE_AND_PLAY(zarcanum_txs_with_big_shuffled_decoy_set_shuffled); + GENERATE_AND_PLAY(zarcanum_pos_block_math); // GENERATE_AND_PLAY(gen_block_reward); // END OF TESTS */ diff --git a/tests/core_tests/zarcanum_test.cpp b/tests/core_tests/zarcanum_test.cpp index d86718ca..7c76006f 100644 --- a/tests/core_tests/zarcanum_test.cpp +++ b/tests/core_tests/zarcanum_test.cpp @@ -393,14 +393,26 @@ bool zarcanum_pos_block_math::generate(std::vector& events) co blk_1_pos = pb.m_block; } + //CHECK_AND_ASSERT_MES(blk_1_pos.miner_tx.signatures.size() == 1, false, "unexpected signatures size"); + //zarcanum_sig& sig = boost::get(blk_1_pos.miner_tx.signatures[0]); + //sig.y0 = 0; // invalidate sig + generator.add_block_info(blk_1_pos, std::list{}); ADD_CUSTOM_EVENT(events, blk_1_pos); + // make a PoS block and than change its nonce, so its hash also changes + // this block should fail std::list miner_stake_sources( {miner_acc} ); MAKE_NEXT_POS_BLOCK(events, blk_2, blk_1_pos, miner_acc, miner_stake_sources); + generator.remove_block_info(blk_2); + events.pop_back(); + blk_2.nonce = 0xc0ffee; // this will change block's hash + generator.add_block_info(blk_2, std::list{}); + DO_CALLBACK(events, "mark_invalid_block"); + ADD_CUSTOM_EVENT(events, blk_2); - MAKE_NEXT_POS_BLOCK(events, blk_3, blk_2, miner_acc, miner_stake_sources); - MAKE_NEXT_POS_BLOCK(events, blk_4, blk_3, miner_acc, miner_stake_sources); + //MAKE_NEXT_POS_BLOCK(events, blk_3, blk_2, miner_acc, miner_stake_sources); + //MAKE_NEXT_POS_BLOCK(events, blk_4, blk_3, miner_acc, miner_stake_sources); return true; } From fa0f51a7edc74e6e94b2c683317af62fa9d55f3a Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 16 Nov 2022 22:04:18 +0100 Subject: [PATCH 08/16] chaingen: add_block/add_block_info adapted, remove_block_info implemented --- tests/core_tests/chaingen.cpp | 41 +++++++++++++++++++++++------------ tests/core_tests/chaingen.h | 3 +++ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index ba029a86..c47cb79a 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -154,22 +154,20 @@ void test_generator::add_block(const currency::block& blk, uint64_t block_reward; get_block_reward(is_pos_block(blk), misc_utils::median(block_sizes), block_size, already_generated_coins, block_reward, currency::get_block_height(blk)); - crypto::hash block_hash = get_block_hash(blk); - m_blocks_info[block_hash] = block_info(blk, already_generated_coins + block_reward, block_size, cum_diff, tx_list, ks_hash); - - - std::stringstream ss_tx_hashes; - for (auto& h : blk.tx_hashes) - { - ss_tx_hashes << " [tx]: " << h << ENDL; - } - - LOG_PRINT_MAGENTA("ADDED_BLOCK[" << block_hash << "][" << (is_pos_block(blk)? "PoS":"PoW") <<"][" << get_block_height(blk) << "][cumul_diff:" << cum_diff << "]" << ENDL << ss_tx_hashes.str(), LOG_LEVEL_0); + add_block_info(block_info(blk, already_generated_coins + block_reward, block_size, cum_diff, tx_list, ks_hash)); } void test_generator::add_block_info(const block_info& bi) { - m_blocks_info[get_block_hash(bi.b)] = bi; + crypto::hash block_hash = get_block_hash(bi.b); + m_blocks_info[block_hash] = bi; + + std::stringstream ss_tx_hashes; + for (auto& h : bi.b.tx_hashes) + { + ss_tx_hashes << " [tx]: " << h << ENDL; + } + LOG_PRINT_MAGENTA("ADDED_BLOCK[" << block_hash << "][" << (is_pos_block(bi.b)? "PoS":"PoW") <<"][" << get_block_height(bi.b) << "][cumul_diff:" << bi.cumul_difficulty << "]" << ENDL << ss_tx_hashes.str(), LOG_LEVEL_0); } bool test_generator::add_block_info(const currency::block& b, const std::list& tx_list) @@ -179,7 +177,8 @@ bool test_generator::add_block_info(const currency::block& b, const std::list chain; @@ -198,10 +197,24 @@ bool test_generator::add_block_info(const currency::block& b, const std::list& tx_list); + bool remove_block_info(const currency::block& blk); + bool remove_block_info(const crypto::hash& block_id); + bool construct_block(currency::block& blk, uint64_t height, const crypto::hash& prev_id, From 801f244d467713bd7e1d0b77c3fea3323bbd658f Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 16 Nov 2022 23:04:29 +0100 Subject: [PATCH 09/16] fix for PoS verification (block hash is used as a message for sig), zarcanum_test_pos_math shoud now pass --- src/wallet/wallet2.cpp | 9 ++------- tests/core_tests/pos_block_builder.cpp | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index f51c6d7e..6122c6be 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3697,12 +3697,7 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl // generate sring signature sig.s.resize(1); - crypto::generate_ring_signature(block_hash, - stake_input.k_image, - keys_ptrs, - secret_x, - 0, - &sig.s[0]); + crypto::generate_ring_signature(block_hash, stake_input.k_image, keys_ptrs, secret_x, 0, sig.s.data()); WLT_LOG_L4("GENERATED RING SIGNATURE for PoS block coinbase: block_id " << block_hash << "txin.k_image" << stake_input.k_image @@ -3800,7 +3795,7 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl } #endif - crypto::hash tx_hash_for_sig = get_transaction_hash(b.miner_tx); // TODO @#@# change to block hash after the corresponding test is made + crypto::hash tx_hash_for_sig = get_block_hash(b); uint8_t err = 0; r = crypto::zarcanum_generate_proof(tx_hash_for_sig, cxt.kernel_hash, ring, cxt.last_pow_block_id_hashed, cxt.sk.kimage, diff --git a/tests/core_tests/pos_block_builder.cpp b/tests/core_tests/pos_block_builder.cpp index dd3ea7d4..3b6713c1 100644 --- a/tests/core_tests/pos_block_builder.cpp +++ b/tests/core_tests/pos_block_builder.cpp @@ -228,7 +228,7 @@ void pos_block_builder::step5_sign(const currency::tx_source_entry& se, const cu ring.emplace_back(el.stealth_address, el.amount_commitment, el.concealing_point); } - crypto::hash tx_hash_for_sig = get_transaction_hash(m_block.miner_tx); // TODO @#@# change to block hash after the corresponding test is made + 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, From a0d3ca0bf327af75ec29e1e9242fa89c6f99a37f Mon Sep 17 00:00:00 2001 From: sowle Date: Wed, 16 Nov 2022 23:49:03 +0100 Subject: [PATCH 10/16] fix for PoS verification (block hash is used as a message for sig), zarcanum_test_pos_math shoud now pass (2) --- src/currency_core/blockchain_storage.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 3cadffea..69ccb968 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -2562,10 +2562,10 @@ bool blockchain_storage::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPU VARIANT_CASE_CONST(tx_out_zarcanum, toz) { COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry()); - oen.amount_commitment = toz.amount_commitment; - oen.concealing_point = toz.concealing_point; oen.global_amount_index = g_index; oen.stealth_address = toz.stealth_address; + oen.amount_commitment = toz.amount_commitment; + oen.concealing_point = toz.concealing_point; } VARIANT_SWITCH_END(); @@ -5442,7 +5442,6 @@ bool blockchain_storage::validate_pos_block(const block& b, CHECK_AND_ASSERT_MES(b.miner_tx.signatures.size() == 1, false, "incorrect number of stake input signatures: " << b.miner_tx.signatures.size()); CHECK_AND_ASSERT_MES(b.miner_tx.signatures[0].type() == typeid(zarcanum_sig), false, "incorrect sig 0 type: " << b.miner_tx.signatures[0].type().name()); const zarcanum_sig& sig = boost::get(b.miner_tx.signatures[0]); - const crypto::hash miner_tx_hash = get_transaction_hash(b.miner_tx); // TODO @#@# do general input check for main chain blocks only? uint64_t max_related_block_height = 0; @@ -5461,8 +5460,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(miner_tx_hash, kernel_hash, ring, last_pow_block_id_hashed, stake_input.k_image, sig, &err); - CHECK_AND_ASSERT_MES(r, false, "zarcanum_verify_proof failed with code " << err); + r = crypto::zarcanum_verify_proof(id, kernel_hash, ring, last_pow_block_id_hashed, stake_input.k_image, 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; From a1bae08a6780937922ab0d31a234f97d065ed695 Mon Sep 17 00:00:00 2001 From: sowle Date: Thu, 17 Nov 2022 00:38:33 +0100 Subject: [PATCH 11/16] crypto: minor improvements --- src/crypto/range_proof_bpp.h | 4 ++-- src/crypto/range_proof_bppe.h | 4 ++-- src/crypto/zarcanum.cpp | 4 ++-- src/currency_core/currency_format_utils.cpp | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/crypto/range_proof_bpp.h b/src/crypto/range_proof_bpp.h index 2bb2e4ba..fb85def3 100644 --- a/src/crypto/range_proof_bpp.h +++ b/src/crypto/range_proof_bpp.h @@ -27,7 +27,7 @@ namespace crypto #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 (!(cond)) { LOG_PRINT_RED("bpp_gen: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \ if (p_err) { *p_err = err_code; } return false; } @@ -355,7 +355,7 @@ namespace crypto bool bpp_verify(const std::vector& sigs, uint8_t* p_err = nullptr) { #define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \ - if (!(cond)) { LOG_PRINT_RED("bpp_verify: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \ + if (!(cond)) { LOG_PRINT_RED("bpp_verify: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \ if (p_err) { *p_err = err_code; } return false; } DBG_PRINT(ENDL << " . . . . bpp_verify() . . . . "); diff --git a/src/crypto/range_proof_bppe.h b/src/crypto/range_proof_bppe.h index 82d1af40..3b2158d0 100644 --- a/src/crypto/range_proof_bppe.h +++ b/src/crypto/range_proof_bppe.h @@ -28,7 +28,7 @@ namespace crypto #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("bppe_gen: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \ + if (!(cond)) { LOG_PRINT_RED("bppe_gen: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \ if (p_err) { *p_err = err_code; } return false; } @@ -366,7 +366,7 @@ namespace crypto bool bppe_verify(const std::vector& sigs, uint8_t* p_err = nullptr) { #define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \ - if (!(cond)) { LOG_PRINT_RED("bppe_verify: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \ + if (!(cond)) { LOG_PRINT_RED("bppe_verify: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \ if (p_err) { *p_err = err_code; } return false; } DBG_PRINT(ENDL << " . . . . bppe_verify() . . . . "); diff --git a/src/crypto/zarcanum.cpp b/src/crypto/zarcanum.cpp index c1f58a9a..f9619b52 100644 --- a/src/crypto/zarcanum.cpp +++ b/src/crypto/zarcanum.cpp @@ -43,7 +43,7 @@ namespace crypto } #define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \ - if (!(cond)) { LOG_PRINT_RED("zarcanum_generate_proof: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \ + 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, @@ -160,7 +160,7 @@ namespace crypto #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 = " << err_code, LOG_LEVEL_3); \ + 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, diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index ff566f7d..e59b846f 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -87,7 +87,7 @@ namespace currency result.outputs_count = outs_count; 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); + CHECK_AND_ASSERT_MES(r, false, "bpp_gen failed with error " << (int)err); return true; } From 35b3be92eb7adc3a32f19fa842b1b6e3785c732c Mon Sep 17 00:00:00 2001 From: sowle Date: Thu, 17 Nov 2022 22:19:06 +0100 Subject: [PATCH 12/16] chaingen: more verbose error logging in check_tx_verification_context() and block_tx_verification_context() --- tests/core_tests/chaingen.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index dc85d8b3..c2421453 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -266,10 +266,18 @@ public: bool check_tx_verification_context(const currency::tx_verification_context& tvc, bool tx_added, size_t event_idx, const currency::transaction& /*tx*/) { if (m_invalid_tx_index == event_idx) + { + CHECK_AND_ASSERT_MES(tvc.m_verification_failed, false, ENDL << "event #" << event_idx << ": the tx passed the verification, although it had been marked as invalid" << ENDL); return tvc.m_verification_failed; + } if (m_unverifiable_tx_index == event_idx) + { + CHECK_AND_ASSERT_MES(tvc.m_verification_failed, false, ENDL << "event #" << event_idx << ": the tx passed normally, although it had been marked as unverifiable" << ENDL); return tvc.m_verification_impossible; + } + + CHECK_AND_ASSERT_MES(tx_added, false, ENDL << "event #" << event_idx << ": the tx has not been added for some reason" << ENDL); return !tvc.m_verification_failed && tx_added; } @@ -277,10 +285,16 @@ public: bool check_block_verification_context(const currency::block_verification_context& bvc, size_t event_idx, const currency::block& /*block*/) { if (m_invalid_block_index == event_idx) + { + CHECK_AND_ASSERT_MES(bvc.m_verification_failed, false, ENDL << "event #" << event_idx << ": the block passed the verification, although it had been marked as invalid" << ENDL); return bvc.m_verification_failed; + } if (m_orphan_block_index == event_idx) + { + CHECK_AND_ASSERT_MES(bvc.m_marked_as_orphaned, false, ENDL << "event #" << event_idx << ": the block passed normally, although it had been marked as orphaned" << ENDL); return bvc.m_marked_as_orphaned; + } return !bvc.m_verification_failed; } From 57acf4013df0d13df465de024a558f64ddf6f74a Mon Sep 17 00:00:00 2001 From: sowle Date: Thu, 17 Nov 2022 22:20:33 +0100 Subject: [PATCH 13/16] coretests: zarcanum_pos_block_math greatly improved to cover various zarcanum signature issues (now exposes a known bug) --- tests/core_tests/zarcanum_test.cpp | 162 +++++++++++++++++++---------- 1 file changed, 106 insertions(+), 56 deletions(-) diff --git a/tests/core_tests/zarcanum_test.cpp b/tests/core_tests/zarcanum_test.cpp index 7c76006f..2a28a062 100644 --- a/tests/core_tests/zarcanum_test.cpp +++ b/tests/core_tests/zarcanum_test.cpp @@ -14,10 +14,51 @@ #define AMOUNT_TO_TRANSFER_ZARCANUM_BASIC (TESTS_DEFAULT_FEE*10) - using namespace currency; //------------------------------------------------------------------------------ +// helpers + +void invalidate_CLSAG_GGXG_sig(crypto::CLSAG_GGXG_signature& sig) +{ + sig.c = 7; +} + +void invalidate_bppe_sig(crypto::bppe_signature& sig) +{ + sig.delta_1.make_random(); +} + +void invalidate_pub_key(crypto::public_key& pk) +{ + pk.data[5] = 0x33; +} + +bool invalidate_zarcanum_sig(size_t n, zarcanum_sig& sig) +{ + switch(n) + { + 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 4: invalidate_pub_key(sig.C_prime); break; + case 5: sig.d.make_random(); break; + case 6: invalidate_pub_key(sig.E); break; + case 7: invalidate_bppe_sig(sig.E_range_proof); break; + case 8: invalidate_pub_key(sig.pseudo_out_amount_commitment); break; + case 9: sig.y0.make_random(); break; + case 10: sig.y1.make_random(); break; + case 11: sig.y2.make_random(); break; + case 12: sig.y3.make_random(); break; + case 13: sig.y4.make_random(); break; + default: return false; + } + return true; +} + +//------------------------------------------------------------------------------ + zarcanum_basic_test::zarcanum_basic_test() { @@ -339,6 +380,48 @@ zarcanum_pos_block_math::zarcanum_pos_block_math() m_hardforks.set_hardfork_height(ZANO_HARDFORK_04_ZARCANUM, 0); } +bool make_next_pos_block(test_generator& generator, std::vector& events, const block& prev_block, const account_base& stake_acc, + uint64_t amount_to_find, size_t nmix, block& result) +{ + bool r = false; + std::vector sources; + + size_t height = get_block_height(prev_block) + 1; + crypto::hash prev_id = get_block_hash(prev_block); + r = fill_tx_sources(sources, events, prev_block, stake_acc.get_keys(), amount_to_find, nmix, true, true, false); + CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed"); + CHECK_AND_ASSERT_MES(shuffle_source_entries(sources), false, ""); + auto it = std::max_element(sources.begin(), sources.end(), [&](const tx_source_entry& lhs, const tx_source_entry& rhs){ return lhs.amount < rhs.amount; }); + const tx_source_entry& se = *it; + const tx_source_entry::output_entry& oe = se.outputs[se.real_output]; + + crypto::key_image stake_output_key_image {}; + currency::keypair ephemeral_keys {}; + r = generate_key_image_helper(stake_acc.get_keys(), se.real_out_tx_key, se.real_output_in_tx_index, ephemeral_keys, stake_output_key_image); + CHECK_AND_ASSERT_MES(r, false, "generate_key_image_helper failed"); + uint64_t stake_output_gindex = boost::get(oe.out_reference); + + currency::wide_difficulty_type pos_diff{}; + crypto::hash last_pow_block_hash{}, last_pos_block_kernel_hash{}; + r = generator.get_params_for_next_pos_block(prev_id, pos_diff, last_pow_block_hash, last_pos_block_kernel_hash); + CHECK_AND_ASSERT_MES(r, false, "get_params_for_next_pos_block failed"); + + pos_block_builder pb; + pb.step1_init_header(generator.get_hardforks(), height, prev_id); + pb.step2_set_txs(std::vector()); + + pb.step3a(pos_diff, last_pow_block_hash, last_pos_block_kernel_hash); + + pb.step3b(se.amount, stake_output_key_image, se.real_out_tx_key, se.real_output_in_tx_index, se.real_out_amount_blinding_mask, stake_acc.get_keys().view_secret_key, + stake_output_gindex, prev_block.timestamp, POS_SCAN_WINDOW, POS_SCAN_STEP); + + pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), stake_acc.get_public_address()); + + pb.step5_sign(se, stake_acc.get_keys()); + result = pb.m_block; + return true; +} + bool zarcanum_pos_block_math::generate(std::vector& events) const { bool r = false; @@ -349,70 +432,37 @@ bool zarcanum_pos_block_math::generate(std::vector& events) co MAKE_NEXT_BLOCK(events, blk_1, blk_0, miner_acc); REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 10); - std::vector sources; - - block blk_1_pos; + // blocks with an invalid zarcanum sig + for(size_t i = 1; ; ++i) { - const block& prev_block = blk_1r; - const account_base& stake_acc = miner_acc; - uint64_t amount_to_find = COIN; - size_t nmix = 10; - - size_t height = get_block_height(prev_block) + 1; - crypto::hash prev_id = get_block_hash(prev_block); - r = fill_tx_sources(sources, events, prev_block, stake_acc.get_keys(), amount_to_find, nmix, true, true, false); - CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed"); - CHECK_AND_ASSERT_MES(shuffle_source_entries(sources), false, ""); - auto it = std::max_element(sources.begin(), sources.end(), [&](const tx_source_entry& lhs, const tx_source_entry& rhs){ return lhs.amount < rhs.amount; }); - const tx_source_entry& se = *it; - const tx_source_entry::output_entry& oe = se.outputs[se.real_output]; - - crypto::key_image stake_output_key_image {}; - currency::keypair ephemeral_keys {}; - r = generate_key_image_helper(stake_acc.get_keys(), se.real_out_tx_key, se.real_output_in_tx_index, ephemeral_keys, stake_output_key_image); - CHECK_AND_ASSERT_MES(r, false, "generate_key_image_helper failed"); - uint64_t stake_output_gindex = boost::get(oe.out_reference); - - currency::wide_difficulty_type pos_diff{}; - crypto::hash last_pow_block_hash{}, last_pos_block_kernel_hash{}; - r = generator.get_params_for_next_pos_block(prev_id, pos_diff, last_pow_block_hash, last_pos_block_kernel_hash); - CHECK_AND_ASSERT_MES(r, false, "get_params_for_next_pos_block failed"); - - pos_block_builder pb; - pb.step1_init_header(generator.get_hardforks(), height, prev_id); - pb.step2_set_txs(std::vector()); - - pb.step3a(pos_diff, last_pow_block_hash, last_pos_block_kernel_hash); - - pb.step3b(se.amount, stake_output_key_image, se.real_out_tx_key, se.real_output_in_tx_index, se.real_out_amount_blinding_mask, stake_acc.get_keys().view_secret_key, - stake_output_gindex, prev_block.timestamp, POS_SCAN_WINDOW, POS_SCAN_STEP); - - pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), stake_acc.get_public_address()); - - pb.step5_sign(se, stake_acc.get_keys()); - blk_1_pos = pb.m_block; + block blk_1_pos_bad; + CHECK_AND_ASSERT_MES(make_next_pos_block(generator, events, blk_1r, miner_acc, COIN, 10, blk_1_pos_bad), false, ""); + LOG_PRINT_CYAN("i = " << i, LOG_LEVEL_0); + if (!invalidate_zarcanum_sig(i, boost::get(blk_1_pos_bad.miner_tx.signatures[0]))) + break; + generator.add_block_info(blk_1_pos_bad, std::list{}); + DO_CALLBACK(events, "mark_invalid_block"); + ADD_CUSTOM_EVENT(events, blk_1_pos_bad); } - //CHECK_AND_ASSERT_MES(blk_1_pos.miner_tx.signatures.size() == 1, false, "unexpected signatures size"); - //zarcanum_sig& sig = boost::get(blk_1_pos.miner_tx.signatures[0]); - //sig.y0 = 0; // invalidate sig - - generator.add_block_info(blk_1_pos, std::list{}); - ADD_CUSTOM_EVENT(events, blk_1_pos); + // make a normal PoS block + std::list miner_stake_sources( {miner_acc} ); + MAKE_NEXT_POS_BLOCK(events, blk_2, blk_1r, miner_acc, miner_stake_sources); + // ... and a PoW block + MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_acc); // make a PoS block and than change its nonce, so its hash also changes // this block should fail - std::list miner_stake_sources( {miner_acc} ); - MAKE_NEXT_POS_BLOCK(events, blk_2, blk_1_pos, miner_acc, miner_stake_sources); - generator.remove_block_info(blk_2); + MAKE_NEXT_POS_BLOCK(events, blk_4_bad, blk_3, miner_acc, miner_stake_sources); + generator.remove_block_info(blk_4_bad); events.pop_back(); - blk_2.nonce = 0xc0ffee; // this will change block's hash - generator.add_block_info(blk_2, std::list{}); + blk_4_bad.nonce = 0xc0ffee; // this will change block's hash + generator.add_block_info(blk_4_bad, std::list{}); DO_CALLBACK(events, "mark_invalid_block"); - ADD_CUSTOM_EVENT(events, blk_2); + ADD_CUSTOM_EVENT(events, blk_4_bad); - //MAKE_NEXT_POS_BLOCK(events, blk_3, blk_2, miner_acc, miner_stake_sources); - //MAKE_NEXT_POS_BLOCK(events, blk_4, blk_3, miner_acc, miner_stake_sources); + // finally, make a normal block + MAKE_NEXT_POS_BLOCK(events, blk_4, blk_3, miner_acc, miner_stake_sources); return true; } From 917437b31e34f53276bb65bc41079ad90e83324c Mon Sep 17 00:00:00 2001 From: sowle Date: Thu, 17 Nov 2022 23:01:37 +0100 Subject: [PATCH 14/16] typo fixed --- tests/core_tests/chaingen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index c2421453..00b5942d 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -273,7 +273,7 @@ public: if (m_unverifiable_tx_index == event_idx) { - CHECK_AND_ASSERT_MES(tvc.m_verification_failed, false, ENDL << "event #" << event_idx << ": the tx passed normally, although it had been marked as unverifiable" << ENDL); + CHECK_AND_ASSERT_MES(tvc.m_verification_impossible, false, ENDL << "event #" << event_idx << ": the tx passed normally, although it had been marked as unverifiable" << ENDL); return tvc.m_verification_impossible; } From 5fba04627bf68ea1f844cf8a389facd7bbf18970 Mon Sep 17 00:00:00 2001 From: sowle Date: Fri, 18 Nov 2022 04:18:41 +0100 Subject: [PATCH 15/16] 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); From 197ef6861f211df64047de3e80edd56cdc123779 Mon Sep 17 00:00:00 2001 From: sowle Date: Fri, 18 Nov 2022 20:55:02 +0100 Subject: [PATCH 16/16] validate_alt_block_input adapted for post-HF4 inputs (WIP, first attempt) --- src/currency_core/blockchain_storage.cpp | 55 +++++++++++++++--------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index c39cb778..ad2e0a40 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -6696,22 +6696,24 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx, const txin_v& input_v = input_tx.vin[input_index]; - const txin_to_key& input_to_key = get_to_key_input_from_txin_v(input_v); + const crypto::key_image& input_key_image = get_key_image_from_txin_v(input_v); + const std::vector& input_key_offsets = get_key_offsets_from_txin_v(input_v); + const uint64_t input_amount = get_amount_from_variant(input_v); // check case b1: key_image spent status in main chain, should be either non-spent or has spent height >= split_height - auto p = m_db_spent_keys.get(input_to_key.k_image); - CHECK_AND_ASSERT_MES(p == nullptr || *p >= split_height, false, "key image " << input_to_key.k_image << " has been already spent in main chain at height " << *p << ", split height: " << split_height); + auto p = m_db_spent_keys.get(input_key_image); + CHECK_AND_ASSERT_MES(p == nullptr || *p >= split_height, false, "key image " << input_key_image << " has been already spent in main chain at height " << *p << ", split height: " << split_height); TIME_MEASURE_START(ki_lookup_time); //check key_image in altchain //check among this alt block already collected key images first - if (collected_keyimages.find(input_to_key.k_image) != collected_keyimages.end()) + if (collected_keyimages.find(input_key_image) != collected_keyimages.end()) { // cases b2, b3 - LOG_ERROR("key image " << input_to_key.k_image << " already spent in this alt block"); + LOG_ERROR("key image " << input_key_image << " already spent in this alt block"); return false; } - auto ki_it = m_altblocks_keyimages.find(input_to_key.k_image); + auto ki_it = m_altblocks_keyimages.find(input_key_image); if (ki_it != m_altblocks_keyimages.end()) { //have some entry for this key image. Check if this key image belongs to this alt chain @@ -6721,18 +6723,18 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx, if (alt_chain_block_ids.find(h) != alt_chain_block_ids.end()) { // cases b2, b3 - LOG_ERROR("key image " << input_to_key.k_image << " already spent in altchain"); + LOG_ERROR("key image " << input_key_image << " already spent in altchain"); return false; } } } //update altchain with key image - collected_keyimages.insert(input_to_key.k_image); + collected_keyimages.insert(input_key_image); TIME_MEASURE_FINISH(ki_lookup_time); ki_lookuptime = ki_lookup_time; - std::vector abs_key_offsets = relative_output_offsets_to_absolute(input_to_key.key_offsets); - CHECK_AND_ASSERT_MES(abs_key_offsets.size() > 0 && abs_key_offsets.size() == input_to_key.key_offsets.size(), false, "internal error: abs_key_offsets.size()==" << abs_key_offsets.size() << ", input_to_key.key_offsets.size()==" << input_to_key.key_offsets.size()); + std::vector abs_key_offsets = relative_output_offsets_to_absolute(input_key_offsets); + CHECK_AND_ASSERT_MES(abs_key_offsets.size() > 0 && abs_key_offsets.size() == input_key_offsets.size(), false, "internal error: abs_key_offsets.size()==" << abs_key_offsets.size() << ", input_key_offsets.size()==" << input_key_offsets.size()); // eventually we should found all public keys for all outputs this input refers to, for checking ring signature std::vector pub_keys(abs_key_offsets.size(), null_pkey); @@ -6747,7 +6749,7 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx, if (!alt_chain.empty()) { - auto abg_it = alt_chain.back()->second.gindex_lookup_table.find(input_to_key.amount); + auto abg_it = alt_chain.back()->second.gindex_lookup_table.find(input_amount); if (abg_it != alt_chain.back()->second.gindex_lookup_table.end()) { amount_touched_altchain = true; @@ -6757,13 +6759,13 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx, else { //quite easy, - global_outs_for_amount = m_db_outputs.get_item_size(input_to_key.amount); + global_outs_for_amount = m_db_outputs.get_item_size(input_amount); } } else { //quite easy, - global_outs_for_amount = m_db_outputs.get_item_size(input_to_key.amount); + global_outs_for_amount = m_db_outputs.get_item_size(input_amount); } CHECK_AND_ASSERT_MES(pub_keys.size() == abs_key_offsets.size(), false, "pub_keys.size()==" << pub_keys.size() << " != abs_key_offsets.size()==" << abs_key_offsets.size()); // just a little bit of paranoia @@ -6781,7 +6783,7 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx, { uint64_t offset_gindex = boost::get(off); CHECK_AND_ASSERT_MES(amount_touched_altchain || (offset_gindex < global_outs_for_amount), false, - "invalid global output index " << offset_gindex << " for amount=" << input_to_key.amount << + "invalid global output index " << offset_gindex << " for amount=" << input_amount << ", max is " << global_outs_for_amount << ", referred to by offset #" << pk_n << ", amount_touched_altchain = " << amount_touched_altchain); @@ -6790,7 +6792,7 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx, bool found_the_key = false; for (auto alt_it = alt_chain.rbegin(); alt_it != alt_chain.rend(); alt_it++) { - auto it_aag = (*alt_it)->second.gindex_lookup_table.find(input_to_key.amount); + auto it_aag = (*alt_it)->second.gindex_lookup_table.find(input_amount); if (it_aag == (*alt_it)->second.gindex_lookup_table.end()) { CHECK_AND_ASSERT_MES(alt_it != alt_chain.rbegin(), false, "internal error: was marked as amount_touched_altchain but unable to find on first entry"); @@ -6804,8 +6806,8 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx, //TODO: At the moment we ignore check of mix_attr against mixing to simplify alt chain check, but in future consider it for stronger validation uint64_t local_offset = offset_gindex - it_aag->second; auto& alt_keys = (*alt_it)->second.outputs_pub_keys; - CHECK_AND_ASSERT_MES(local_offset < alt_keys[input_to_key.amount].size(), false, "Internal error: local_offset=" << local_offset << " while alt_keys[" << input_to_key.amount << " ].size()=" << alt_keys.size()); - const output_key_or_htlc_v& out_in_alt = alt_keys[input_to_key.amount][local_offset]; + CHECK_AND_ASSERT_MES(local_offset < alt_keys[input_amount].size(), false, "Internal error: local_offset=" << local_offset << " while alt_keys[" << input_amount << " ].size()=" << alt_keys.size()); + const output_key_or_htlc_v& out_in_alt = alt_keys[input_amount][local_offset]; /* here we do validation against compatibility of input and output type @@ -6842,8 +6844,8 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx, continue; //otherwise lookup in main chain index } - auto p = m_db_outputs.get_subitem(input_to_key.amount, offset_gindex); - CHECK_AND_ASSERT_MES(p != nullptr, false, "global output was not found, amount: " << input_to_key.amount << ", gindex: " << offset_gindex << ", referred to by offset #" << pk_n); + auto p = m_db_outputs.get_subitem(input_amount, offset_gindex); + CHECK_AND_ASSERT_MES(p != nullptr, false, "global output was not found, amount: " << input_amount << ", gindex: " << offset_gindex << ", referred to by offset #" << pk_n); tx_id = p->tx_id; out_n = p->out_no; } @@ -6965,10 +6967,21 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx, VARIANT_SWITCH_END(); } + // @#@# TODO review the following checks! // do input checks (attachment_info, ring signature and extra signature, etc.) - r = check_input_signature(input_tx, input_index, input_to_key, input_tx_hash, pub_key_pointers); - CHECK_AND_ASSERT_MES(r, false, "to_key input validation failed"); + VARIANT_SWITCH_BEGIN(input_v); + VARIANT_CASE_CONST(txin_to_key, input_to_key) + r = check_input_signature(input_tx, input_index, input_to_key, input_tx_hash, pub_key_pointers); + CHECK_AND_ASSERT_MES(r, false, "to_key input validation failed"); + VARIANT_CASE_CONST(txin_zc_input, input_zc); + uint64_t max_related_block_height = 0; + r = check_tx_input(input_tx, input_index, input_zc, input_tx_hash, max_related_block_height); + CHECK_AND_ASSERT_MES(r, false, "check_tx_input failed"); + VARIANT_CASE_OTHER() + LOG_ERROR("unexpected input type: " << input_v.type().name()); + return false; + VARIANT_SWITCH_END(); // TODO: consider checking input_tx for valid extra attachment info as it's checked in check_tx_inputs()