From e80e4a60ccadaa8571b2e7bba6664f3a07a9ed62 Mon Sep 17 00:00:00 2001 From: sowle Date: Fri, 9 Jun 2023 01:19:37 +0200 Subject: [PATCH] 1) refactored block template creation and construct_miner_tx to incorporate block reward, essential for Zarcanum PoS; 2) fixed a bug in Zarcanum PoS generation; zarcanum_block_with_txs test now passes successfully --- src/currency_core/blockchain_storage.cpp | 5 ++++- src/currency_core/blockchain_storage_basic.h | 2 ++ src/currency_core/currency_format_utils.cpp | 8 ++++---- src/currency_core/currency_format_utils.h | 3 ++- src/rpc/core_rpc_server.cpp | 2 ++ src/rpc/core_rpc_server_commands_defs.h | 4 ++++ src/wallet/wallet2.cpp | 8 ++++---- src/wallet/wallet2.h | 2 +- tests/core_tests/chaingen.cpp | 10 +++++++--- tests/core_tests/chaingen.h | 5 ++++- tests/core_tests/pos_block_builder.cpp | 5 +++-- tests/core_tests/transaction_tests.cpp | 16 +++++++++------- tests/core_tests/zarcanum_test.cpp | 2 +- 13 files changed, 47 insertions(+), 25 deletions(-) diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 405e9895..12f7ed06 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -1323,7 +1323,7 @@ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t if (is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM)) // TODO @#@# consider moving to validate_tx_for_hardfork_specific_terms { CHECK_AND_ASSERT_MES(b.miner_tx.attachment.empty(), false, "coinbase transaction has attachments; attachments are not allowed for coinbase transactions."); - CHECK_AND_ASSERT_MES(b.miner_tx.proofs.size() == 3, false, "coinbase transaction has incorrect number of proofs (" << b.miner_tx.proofs.size() << "), expected 2"); + CHECK_AND_ASSERT_MES(b.miner_tx.proofs.size() == 3, false, "coinbase transaction has incorrect number of proofs (" << b.miner_tx.proofs.size() << "), expected 3"); CHECK_AND_ASSERT_MES(b.miner_tx.proofs[0].type() == typeid(zc_asset_surjection_proof), false, "coinbase transaction has incorrect type of proof #0 (expected: zc_asset_surjection_proof)"); CHECK_AND_ASSERT_MES(b.miner_tx.proofs[1].type() == typeid(zc_outs_range_proof), false, "coinbase transaction has incorrect type of proof #1 (expected: zc_outs_range_proof)"); CHECK_AND_ASSERT_MES(b.miner_tx.proofs[2].type() == typeid(zc_balance_proof), false, "coinbase transaction has incorrect type of proof #2 (expected: zc_balance_proof)"); @@ -1518,6 +1518,8 @@ bool blockchain_storage::create_block_template(const create_block_template_param if (!block_filled) return false; + resp.txs_fee = fee; + /* instead of complicated two-phase template construction and adjustment of cumulative size with block reward we use CURRENCY_COINBASE_BLOB_RESERVED_SIZE as penalty-free coinbase transaction reservation. @@ -1528,6 +1530,7 @@ bool blockchain_storage::create_block_template(const create_block_template_param miner_address, stakeholder_address, b.miner_tx, + resp.block_reward_without_fee, get_tx_version(height, m_core_runtime_config.hard_forks), ex_nonce, CURRENCY_MINER_TX_MAX_OUTS, diff --git a/src/currency_core/blockchain_storage_basic.h b/src/currency_core/blockchain_storage_basic.h index 70556f0f..e997b58f 100644 --- a/src/currency_core/blockchain_storage_basic.h +++ b/src/currency_core/blockchain_storage_basic.h @@ -147,6 +147,8 @@ namespace currency wide_difficulty_type diffic; uint64_t height; tx_generation_context miner_tx_tgc; // bad design, a lot of copying, consider redesign -- sowle + uint64_t block_reward_without_fee; + uint64_t txs_fee; // sum of transactions' fee if any }; typedef std::unordered_map transactions_map; diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 56ef91cd..89167bdc 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -357,24 +357,24 @@ namespace currency const account_public_address &miner_address, const account_public_address &stakeholder_address, transaction& tx, + uint64_t& block_reward_without_fee, uint64_t tx_version, const blobdata& extra_nonce /* = blobdata() */, size_t max_outs /* = CURRENCY_MINER_TX_MAX_OUTS */, bool pos /* = false */, const pos_entry& pe /* = pos_entry() */, // only pe.stake_unlock_time and pe.stake_amount are used now, TODO: consider refactoring -- sowle - tx_generation_context* ogc_ptr /* = nullptr */, + tx_generation_context* ogc_ptr /* = nullptr */, const keypair* tx_one_time_key_to_use /* = nullptr */ ) { bool r = false; - uint64_t block_reward = 0; - if (!get_block_reward(pos, median_size, current_block_size, already_generated_coins, block_reward, height)) + if (!get_block_reward(pos, median_size, current_block_size, already_generated_coins, block_reward_without_fee, height)) { LOG_ERROR("Block is too big"); return false; } - block_reward += fee; + uint64_t block_reward = block_reward_without_fee + fee; // // prepare destinations diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 0d8ebaf5..bcd15258 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -255,7 +255,8 @@ namespace currency uint64_t fee, const account_public_address &miner_address, const account_public_address &stakeholder_address, - transaction& tx, + transaction& tx, + uint64_t& block_reward_without_fee, uint64_t tx_version, const blobdata& extra_nonce = blobdata(), size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS, diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 456c84a9..00646db6 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -912,6 +912,8 @@ namespace currency res.prev_hash = string_tools::pod_to_hex(resp.b.prev_id); res.miner_tx_tgc = resp.miner_tx_tgc; res.height = resp.height; + res.block_reward_without_fee = resp.block_reward_without_fee; + res.txs_fee = resp.txs_fee; //calculate epoch seed res.seed = currency::ethash_epoch_to_seed(currency::ethash_height_to_epoch(res.height)); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 4926e3c9..f36f6c1f 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -861,6 +861,8 @@ namespace currency blobdata blocktemplate_blob; std::string prev_hash; tx_generation_context miner_tx_tgc; + uint64_t block_reward_without_fee; + uint64_t txs_fee; std::string status; BEGIN_KV_SERIALIZE_MAP() @@ -870,6 +872,8 @@ namespace currency KV_SERIALIZE(blocktemplate_blob) KV_SERIALIZE(prev_hash) KV_SERIALIZE(miner_tx_tgc) + KV_SERIALIZE(block_reward_without_fee) + KV_SERIALIZE(txs_fee) KV_SERIALIZE(status) END_KV_SERIALIZE_MAP() }; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 07deff48..39d669ed 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3898,7 +3898,7 @@ bool wallet2::is_in_hardfork_zone(uint64_t hardfork_index) const return m_core_runtime_config.is_hardfork_active_for_height(hardfork_index, get_blockchain_current_size()); } //---------------------------------------------------------------------------------------------------- -bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::block& b, const pos_entry& pe, currency::tx_generation_context& miner_tx_tgc) const +bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, uint64_t full_block_reward, const currency::pos_entry& pe, currency::tx_generation_context& miner_tx_tgc, currency::block& b) const { bool r = false; WLT_CHECK_AND_ASSERT_MES(pe.wallet_index < m_transfers.size(), false, "invalid pe.wallet_index: " << pe.wallet_index); @@ -4082,9 +4082,8 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl b.miner_tx.proofs.emplace_back(std::move(range_proofs)); // balance proof - uint64_t block_reward = COIN; // TODO @#@# move it somewhere -- sowle currency::zc_balance_proof balance_proof{}; - r = generate_tx_balance_proof(b.miner_tx, miner_tx_id, miner_tx_tgc, block_reward, balance_proof); + r = generate_tx_balance_proof(b.miner_tx, miner_tx_id, miner_tx_tgc, full_block_reward, balance_proof); WLT_CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed"); b.miner_tx.proofs.emplace_back(std::move(balance_proof)); @@ -4243,7 +4242,8 @@ bool wallet2::build_minted_block(const mining_context& cxt, const currency::acco set_block_datetime(current_timestamp, b); WLT_LOG_MAGENTA("Applying actual timestamp: " << current_timestamp, LOG_LEVEL_0); - res = prepare_and_sign_pos_block(cxt, b, tmpl_req.pe, tmpl_rsp.miner_tx_tgc); + uint64_t full_block_reward = tmpl_rsp.block_reward_without_fee + tmpl_rsp.txs_fee; + res = prepare_and_sign_pos_block(cxt, full_block_reward, tmpl_req.pe, tmpl_rsp.miner_tx_tgc, b); WLT_CHECK_AND_ASSERT_MES(res, false, "Failed to prepare_and_sign_pos_block"); crypto::hash block_hash = get_block_hash(b); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 22a686c8..6046e736 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -883,7 +883,7 @@ namespace tools //next functions in public area only becausce of test_generator //TODO: Need refactoring - remove it back to private zone void set_genesis(const crypto::hash& genesis_hash); - bool prepare_and_sign_pos_block(const mining_context& cxt, currency::block& b, const currency::pos_entry& pe, currency::tx_generation_context& miner_tx_tgc) const; + bool prepare_and_sign_pos_block(const mining_context& cxt, uint64_t full_block_reward, const currency::pos_entry& pe, currency::tx_generation_context& miner_tx_tgc, currency::block& b) const; void process_new_blockchain_entry(const currency::block& b, const currency::block_direct_data_entry& bche, const crypto::hash& bl_id, diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 45b51919..03f61dee 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -299,6 +299,7 @@ bool test_generator::construct_block(currency::block& blk, blk.miner_tx = AUTO_VAL_INIT(blk.miner_tx); size_t target_block_size = txs_size + 0; // zero means no cost for ordinary coinbase tx_generation_context miner_tx_tgc{}; + uint64_t block_reward_without_fee = 0; while (true) { r = construct_miner_tx(height, misc_utils::median(block_sizes), @@ -308,6 +309,7 @@ bool test_generator::construct_block(currency::block& blk, miner_acc.get_keys().account_address, miner_acc.get_keys().account_address, blk.miner_tx, + block_reward_without_fee, get_tx_version(height, m_hardforks), blobdata(), test_generator::get_test_gentime_settings().miner_tx_max_outs, @@ -354,7 +356,7 @@ bool test_generator::construct_block(currency::block& blk, else { //need to build pos block - r = sign_block(wallets[won_walled_index].mining_context, pe, *wallets[won_walled_index].wallet, miner_tx_tgc, blk); + r = sign_block(wallets[won_walled_index].mining_context, pe, block_reward_without_fee + total_fee, *wallets[won_walled_index].wallet, miner_tx_tgc, blk); CHECK_AND_ASSERT_MES(r, false, "Failed to find_kernel_and_sign()"); } @@ -373,11 +375,12 @@ bool test_generator::construct_block(currency::block& blk, bool test_generator::sign_block(const tools::wallet2::mining_context& mining_context, const pos_entry& pe, + uint64_t full_block_reward, const tools::wallet2& w, tx_generation_context& miner_tx_tgc, currency::block& b) { - bool r = w.prepare_and_sign_pos_block(mining_context, b, pe, miner_tx_tgc); + bool r = w.prepare_and_sign_pos_block(mining_context, full_block_reward, pe, miner_tx_tgc, b); CHECK_AND_ASSERT_MES(r, false, "prepare_and_sign_pos_block failed"); return true; } @@ -933,9 +936,10 @@ bool test_generator::construct_block(int64_t manual_timestamp_adjustment, } else { + uint64_t base_block_reward = 0; size_t current_block_size = txs_sizes + get_object_blobsize(blk.miner_tx); // TODO: This will work, until size of constructed block is less then CURRENCY_BLOCK_GRANTED_FULL_REWARD_ZONE - if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, current_block_size, 0, miner_acc.get_public_address(), miner_acc.get_public_address(), blk.miner_tx, get_tx_version(height, m_hardforks), blobdata(), 1)) + if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, current_block_size, 0, miner_acc.get_public_address(), miner_acc.get_public_address(), blk.miner_tx, base_block_reward, get_tx_version(height, m_hardforks), blobdata(), 1)) return false; } diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 936e422a..96068a3b 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -488,6 +488,7 @@ public: bool sign_block(const tools::wallet2::mining_context& mining_context, const currency::pos_entry& pe, + uint64_t full_block_reward, const tools::wallet2& w, currency::tx_generation_context& miner_tx_tgc, currency::block& b); @@ -951,7 +952,9 @@ bool test_generator::construct_block_gentime_with_coinbase_cb(const currency::bl size_t height = get_block_height(prev_block) + 1; //size_t current_block_size = get_object_blobsize(miner_tx); - r = construct_miner_tx(height, epee::misc_utils::median(block_sizes), already_generated_coins, 0 /* current_block_size !HACK! */, 0, acc.get_public_address(), acc.get_public_address(), miner_tx, get_tx_version(height, m_hardforks), currency::blobdata(), 1); + uint64_t block_reward_without_fee = 0; + + r = construct_miner_tx(height, epee::misc_utils::median(block_sizes), already_generated_coins, 0 /* current_block_size !HACK! */, 0, acc.get_public_address(), acc.get_public_address(), miner_tx, block_reward_without_fee, get_tx_version(height, m_hardforks), currency::blobdata(), 1); CHECK_AND_ASSERT_MES(r, false, "construct_miner_tx failed"); if (!cb(miner_tx)) diff --git a/tests/core_tests/pos_block_builder.cpp b/tests/core_tests/pos_block_builder.cpp index e4a021e0..60f7a3bf 100644 --- a/tests/core_tests/pos_block_builder.cpp +++ b/tests/core_tests/pos_block_builder.cpp @@ -166,9 +166,10 @@ void pos_block_builder::step4_generate_coinbase_tx(size_t median_size, pe.amount = m_context.stake_amount; // generate miner tx using incorrect current_block_size only for size estimation + uint64_t block_reward_without_fee = 0; size_t estimated_block_size = m_txs_total_size; bool r = construct_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee, - reward_receiver_address, stakeholder_address, m_block.miner_tx, tx_version, extra_nonce, max_outs, true, pe, &m_miner_tx_tgc, tx_one_time_key_to_use); + reward_receiver_address, stakeholder_address, m_block.miner_tx, block_reward_without_fee, tx_version, extra_nonce, max_outs, true, pe, &m_miner_tx_tgc, tx_one_time_key_to_use); CHECK_AND_ASSERT_THROW_MES(r, "construct_miner_tx failed"); estimated_block_size = m_txs_total_size + get_object_blobsize(m_block.miner_tx); @@ -176,7 +177,7 @@ void pos_block_builder::step4_generate_coinbase_tx(size_t median_size, for (size_t try_count = 0; try_count != 10; ++try_count) { r = construct_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee, - reward_receiver_address, stakeholder_address, m_block.miner_tx, tx_version, extra_nonce, max_outs, true, pe, &m_miner_tx_tgc, tx_one_time_key_to_use); + reward_receiver_address, stakeholder_address, m_block.miner_tx, estimated_block_size, tx_version, extra_nonce, max_outs, true, pe, &m_miner_tx_tgc, tx_one_time_key_to_use); CHECK_AND_ASSERT_THROW_MES(r, "construct_homemade_pos_miner_tx failed"); cumulative_size = m_txs_total_size + get_object_blobsize(m_block.miner_tx); diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp index cd8643ca..60c37c98 100644 --- a/tests/core_tests/transaction_tests.cpp +++ b/tests/core_tests/transaction_tests.cpp @@ -34,23 +34,24 @@ bool test_transaction_generation_and_ring_signature() std::string add_str = miner_acc3.get_public_address_str(); + uint64_t block_reward_without_fee = 0; account_base rv_acc; rv_acc.generate(); account_base rv_acc2; rv_acc2.generate(); transaction tx_mine_1; - construct_miner_tx(0, 0, 0, 10, 0, miner_acc1.get_keys().account_address, miner_acc1.get_keys().account_address, tx_mine_1, TRANSACTION_VERSION_PRE_HF4); + construct_miner_tx(0, 0, 0, 10, 0, miner_acc1.get_keys().account_address, miner_acc1.get_keys().account_address, tx_mine_1, block_reward_without_fee, TRANSACTION_VERSION_PRE_HF4); transaction tx_mine_2; - construct_miner_tx(0, 0, 0, 0, 0, miner_acc2.get_keys().account_address, miner_acc2.get_keys().account_address, tx_mine_2, TRANSACTION_VERSION_PRE_HF4); + construct_miner_tx(0, 0, 0, 0, 0, miner_acc2.get_keys().account_address, miner_acc2.get_keys().account_address, tx_mine_2, block_reward_without_fee, TRANSACTION_VERSION_PRE_HF4); transaction tx_mine_3; - construct_miner_tx(0, 0, 0, 0, 0, miner_acc3.get_keys().account_address, miner_acc3.get_keys().account_address, tx_mine_3, TRANSACTION_VERSION_PRE_HF4); + construct_miner_tx(0, 0, 0, 0, 0, miner_acc3.get_keys().account_address, miner_acc3.get_keys().account_address, tx_mine_3, block_reward_without_fee, TRANSACTION_VERSION_PRE_HF4); transaction tx_mine_4; - construct_miner_tx(0, 0, 0, 0, 0, miner_acc4.get_keys().account_address, miner_acc4.get_keys().account_address, tx_mine_4, TRANSACTION_VERSION_PRE_HF4); + construct_miner_tx(0, 0, 0, 0, 0, miner_acc4.get_keys().account_address, miner_acc4.get_keys().account_address, tx_mine_4, block_reward_without_fee, TRANSACTION_VERSION_PRE_HF4); transaction tx_mine_5; - construct_miner_tx(0, 0, 0, 0, 0, miner_acc5.get_keys().account_address, miner_acc5.get_keys().account_address, tx_mine_5, TRANSACTION_VERSION_PRE_HF4); + construct_miner_tx(0, 0, 0, 0, 0, miner_acc5.get_keys().account_address, miner_acc5.get_keys().account_address, tx_mine_5, block_reward_without_fee, TRANSACTION_VERSION_PRE_HF4); transaction tx_mine_6; - construct_miner_tx(0, 0, 0, 0, 0, miner_acc6.get_keys().account_address, miner_acc6.get_keys().account_address, tx_mine_6, TRANSACTION_VERSION_PRE_HF4); + construct_miner_tx(0, 0, 0, 0, 0, miner_acc6.get_keys().account_address, miner_acc6.get_keys().account_address, tx_mine_6, block_reward_without_fee, TRANSACTION_VERSION_PRE_HF4); //fill inputs entry typedef tx_source_entry::output_entry tx_output_entry; @@ -134,8 +135,9 @@ bool test_block_creation() account_public_address adr; bool r = get_account_address_from_str(adr, "ZxDLGBGXbjo5w51tJkvxEPHFRr7Xft4hf33N8EkJPndoGCqocQF1mzpZqYwXByx5gMbfQuPAAB9vj79EFR6Jwkgu1o3aMQPwJ"); CHECK_AND_ASSERT_MES(r, false, "failed to import"); + uint64_t block_reward_without_fee = 0; block b; - r = construct_miner_tx(90, epee::misc_utils::median(szs), 3553616528562147, 33094, 10000000, adr, adr, b.miner_tx, TRANSACTION_VERSION_PRE_HF4); + r = construct_miner_tx(90, epee::misc_utils::median(szs), 3553616528562147, 33094, 10000000, adr, adr, b.miner_tx, block_reward_without_fee, TRANSACTION_VERSION_PRE_HF4); return r; } diff --git a/tests/core_tests/zarcanum_test.cpp b/tests/core_tests/zarcanum_test.cpp index 0c7d467d..245eb085 100644 --- a/tests/core_tests/zarcanum_test.cpp +++ b/tests/core_tests/zarcanum_test.cpp @@ -807,7 +807,7 @@ bool zarcanum_block_with_txs::generate(std::vector& events) co // make sure Alice received both block reward and the fee uint64_t mined_amount_2 = COIN + fee; - DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, m_alice_balance + mined_amount_2, 0, mined_amount + mined_amount_2, 0, 0)); + DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, m_alice_balance + mined_amount_2, UINT64_MAX, mined_amount + mined_amount_2, 0, 0)); m_alice_balance += mined_amount_2;