From ca2069facc8c4bc2dc863ea313fd7f8a4b3bf9fb Mon Sep 17 00:00:00 2001 From: akildemir Date: Fri, 27 Dec 2024 11:51:33 +0300 Subject: [PATCH 1/2] rewrite protocol tx construct/validate --- external/mx25519 | 1 + src/cryptonote_core/blockchain.cpp | 239 +++++--------------- src/cryptonote_core/blockchain.h | 3 +- src/cryptonote_core/cryptonote_tx_utils.cpp | 174 +------------- src/cryptonote_core/cryptonote_tx_utils.h | 2 +- src/cryptonote_core/tx_pool.cpp | 10 +- src/cryptonote_core/tx_pool.h | 5 +- 7 files changed, 65 insertions(+), 369 deletions(-) create mode 160000 external/mx25519 diff --git a/external/mx25519 b/external/mx25519 new file mode 160000 index 0000000..84ca129 --- /dev/null +++ b/external/mx25519 @@ -0,0 +1 @@ +Subproject commit 84ca1290fa344351c95692f20f41a174b70e0c7b diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index a9d3be1..cfb48f1 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1450,11 +1450,6 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height, bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version) { LOG_PRINT_L3("Blockchain::" << __func__); - if (!b.protocol_tx.vin.size()) { - // Nothing is created by this TX - check no money is included - CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == 0, false, "void protocol transaction in the block has outputs"); - return true; - } CHECK_AND_ASSERT_MES(b.protocol_tx.vin.size() == 1, false, "coinbase protocol transaction in the block has no inputs"); CHECK_AND_ASSERT_MES(b.protocol_tx.vin[0].type() == typeid(txin_gen), false, "coinbase protocol transaction in the block has the wrong type"); CHECK_AND_ASSERT_MES(b.protocol_tx.version > 1, false, "Invalid coinbase protocol transaction version"); @@ -1531,176 +1526,74 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl } //------------------------------------------------------------------ // SRCG -bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, std::vector>& txs, uint8_t hf_version) +bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version) { LOG_PRINT_L3("Blockchain::" << __func__); - CHECK_AND_ASSERT_MES(b.tx_hashes.size() == txs.size(), false, "Invalid number of TXs / hashes supplied"); - if (!b.protocol_tx.vin.size()) { - // Nothing is created by this TX - check no money is included - CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == 0, false, "void protocol transaction in the block has outputs"); - return true; + // if nothing is created by this TX - check no money is included + size_t vout_size = b.protocol_tx.vout.size(); + CHECK_AND_ASSERT_MES(b.protocol_tx.vin.size() == 1, false, "coinbase protocol transaction in the block has no inputs"); + CHECK_AND_ASSERT_MES(vout_size != 0, true, "coinbase protocol transaction in the block has no outputs"); + + // Can we have matured STAKE transactions yet? + uint64_t stake_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD; + if (height <= stake_lock_period) { + return false; } - if (!b.protocol_tx.vout.size()) { - // No money is minted, nothing to verify - bail out - return true; + // Get the staking data for the block that matured this time + cryptonote::yield_block_info ybi_matured; + uint64_t matured_height = height - stake_lock_period - 1; + bool ok = get_ybi_entry(matured_height, ybi_matured); + if (!ok || ybi_matured.locked_coins_this_block == 0) { + LOG_ERROR("Block at height: " << height << " - Failed to obtain yield block information - aborting"); + return false; } - // Get the circulating supply so we can verify - std::map circ_supply; - if (hf_version >= HF_VERSION_ENABLE_CONVERT) { - circ_supply = get_db().get_circulating_supply(); + // Iterate over the cached data for block yield, calculating the yield payouts due + std::vector> yield_payouts; + if (!calculate_yield_payouts(matured_height, yield_payouts)) { + LOG_ERROR("Block at height: " << height << " - Failed to obtain yield payout information - aborting"); + return false; } - - // Build a map of outputs from the protocol_tx - std::map> outputs; + + // go through each vout and validate for (auto& o : b.protocol_tx.vout) { + // gather the output data + uint64_t out_amount; + uint64_t out_unlock_time; + std::string out_asset_type; + crypto::public_key out_key; if (o.target.type() == typeid(txout_to_key)) { txout_to_key out = boost::get(o.target); - CHECK_AND_ASSERT_MES(out.unlock_time == CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, false, "Invalid unlock time on protocol_tx output"); - CHECK_AND_ASSERT_MES(outputs.count(out.key) == 0, false, "Output duplicated in protocol_tx"); - outputs[out.key] = std::make_tuple(out.asset_type, o.amount, out.unlock_time); + out_unlock_time = out.unlock_time; + out_asset_type = out.asset_type; + out_key = out.key; + out_amount = o.amount; } else if (o.target.type() == typeid(txout_to_tagged_key)) { txout_to_tagged_key out = boost::get(o.target); - CHECK_AND_ASSERT_MES(out.unlock_time == CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, false, "Invalid unlock time on protocol_tx output"); - CHECK_AND_ASSERT_MES(outputs.count(out.key) == 0, false, "Output duplicated in protocol_tx"); - outputs[out.key] = std::make_tuple(out.asset_type, o.amount, out.unlock_time); + out_unlock_time = out.unlock_time; + out_asset_type = out.asset_type; + out_key = out.key; + out_amount = o.amount; } else { MERROR("Block at height: " << height << " attempting to add protocol transaction with invalid type " << o.target.type().name()); return false; } - } - CHECK_AND_ASSERT_MES(outputs.size() == b.protocol_tx.vout.size(), false, "Mismatch between vout and outputs for protocol_tx - aborting"); - // Maintain a count of outputs that we have verified - std::vector outputs_verified; - - size_t tx_index = 0; - // Iterate over the block's transaction hashes, grabbing each - // from the tx_pool and validating them. - for (const auto& tx_tmp : txs) - { - // Get a ptr to the TX to simplify coding - const transaction* tx = &tx_tmp.first; - - // Check to see if the TX exists in the DB - this probably duplicates the effort in our caller, but better to be sure - if (m_db->tx_exists(tx->hash)) - { - MERROR("Block at height: " << height << " attempting to add transaction already in blockchain with id: " << tx->hash); + // check if there is entry in the yield payouts for this output + auto found = std::find_if(yield_payouts.begin(), yield_payouts.end(), [&](const std::pair& p) { + return p.first.return_address == out_key; + }); + if (found == yield_payouts.end()) { + MERROR("Block at height: " << height << " - Failed to locate output for protocol TX - rejecting block"); return false; } - if (hf_version >= HF_VERSION_ENABLE_CONVERT) { - - // Check to see if the TX is a conversion or not - if (tx->type != cryptonote::transaction_type::CONVERT) { - // Only conversion (and failed conversion, aka refund) TXs need to be verified - skip this TX - continue; - } - - // Verify that the TX has an output in the protocol_tx to verify - if (outputs.count(tx->return_address) != 1) { - LOG_ERROR("Block at height: " << height << " - Failed to locate output for conversion TX id " << tx->hash << " - rejecting block"); - return false; - } - - // Get the output information - std::string output_asset_type; - uint64_t output_amount; - uint64_t output_unlock_time; - std::tie(output_asset_type, output_amount, output_unlock_time) = outputs[tx->return_address]; - - // Verify the asset_type - if (tx->source_asset_type == output_asset_type) { - // Check the amount for REFUND - if (tx->amount_burnt != output_amount) { - LOG_ERROR("Block at height: " << height << " - Output amount does not match amount_burnt for refunded TX id " << tx->hash << " - rejecting block"); - return false; - } - - // Verified the refund successfully - outputs_verified.push_back(tx->return_address); - - } else if (tx->destination_asset_type == output_asset_type) { - // Check the amount for CONVERT - - // Verify the amount of the conversion - uint64_t amount_minted_check = 0, amount_slippage_check = 0; - bool ok = cryptonote::calculate_conversion(tx->source_asset_type, tx->destination_asset_type, tx->amount_burnt, tx->amount_slippage_limit, amount_minted_check, amount_slippage_check, circ_supply, b.pricing_record, hf_version); - if (!ok) { - LOG_ERROR("Block at height: " << height << " - Failed to calculate conversion for TX id " << tx->hash << " - rejecting block"); - return false; - } - if (amount_minted_check != output_amount) { - LOG_ERROR("Block at height: " << height << " - Output amount does not match amount minted for converted TX id " << tx->hash << " - rejecting block"); - return false; - } - - // Verified the conversion successfully - outputs_verified.push_back(tx->return_address); - - } else { - LOG_ERROR("Block at height: " << height << " - Output asset type incorrect: source " << tx->source_asset_type << ", dest " << tx->destination_asset_type << ", got " << output_asset_type << " - rejecting block"); - return false; - } - } - } - - // Can we have matured STAKE transactions yet? - uint64_t stake_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD; - if (height > stake_lock_period) { - - // Yes - Get the staking data for the block that matured this time - cryptonote::yield_block_info ybi_matured; - uint64_t matured_height = height - stake_lock_period - 1; - bool ok = get_ybi_entry(matured_height, ybi_matured); - if (ok && ybi_matured.locked_coins_this_block > 0) { - - // Iterate over the cached data for block yield, calculating the yield payouts due - std::vector> yield_payouts; - if (!calculate_yield_payouts(matured_height, yield_payouts)) { - LOG_ERROR("Block at height: " << height << " - Failed to obtain yield payout information - aborting"); - return false; - } - - // Iterate the yield payouts, verifying as we go - for (const auto& payout: yield_payouts) { - - // Do we have a singular matching output in tx.vout? - if (outputs.count(payout.first.return_address) != 1) { - LOG_ERROR("Block at height: " << height << " - Failed to locate output for matured TX id " << payout.first.tx_hash << " - rejecting block"); - return false; - } - - // Get the output information - std::string output_asset_type; - uint64_t output_amount; - uint64_t output_unlock_time; - std::tie(output_asset_type, output_amount, output_unlock_time) = outputs[payout.first.return_address]; - - // Verify the asset type - must be SAL - if (output_asset_type != "SAL") { - LOG_ERROR("Block at height: " << height << " - Incorrect output asset type for matured TX id " << payout.first.tx_hash << " - rejecting block"); - return false; - } - - // Verify the amount - if (output_amount != payout.second) { - LOG_ERROR("Block at height: " << height << " - Incorrect output amount for matured TX id " << payout.first.tx_hash << " - rejecting block"); - return false; - } - - // Amount and return_address match our expectation - outputs_verified.push_back(payout.first.return_address); - } - } - } - - // All candidates have been evaluated - make sure there are no other outputs that have not been catered for - if (outputs.size() != outputs_verified.size()) { - LOG_ERROR("Block at height: " << height << " - Incorrect number of outputs - expected " << outputs_verified.size() << " but received " << outputs.size() << " - rejecting block"); - return false; + // check other fields + CHECK_AND_ASSERT_MES(out_unlock_time == CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, false, "Invalid unlock time on protocol_tx output"); + CHECK_AND_ASSERT_MES(out_asset_type == "SAL", false, "Incorrect output asset type for protocol TX"); + CHECK_AND_ASSERT_MES(out_amount == found->second, false, "Incorrect output amount for protocol TX"); } // Everything checks out @@ -1955,46 +1848,16 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, size_t txs_weight; uint64_t fee; - /** - * Here is where the magic happens - determination of the payments for the protocol_tx - * - * We need to know the following: - * - address to send the funds to ("return_address") - * - asset_type being burnt - * - amount being burnt - * - asset_type being minted - * - * (All of this information should be provided by the txpool_tx_meta_t object) - * - * From that little lot, we can hopefully work out the slippage on all of the TXs, and - * therefore the amount to be minted for each TX, and who to pay it to, etc, etc. - */ - std::vector protocol_metadata; - std::vector protocol_entries; - if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, b.major_version, b.pricing_record, circ_supply, protocol_metadata)) + if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, b.major_version)) { return false; } - - // Clone the txpool_tx_meta_t data into a more useable format - for (auto& meta: protocol_metadata) { - cryptonote::protocol_data_entry entry; - entry.amount_burnt = meta.amount_burnt; - entry.amount_minted = 0; - entry.amount_slippage_limit = meta.amount_slippage_limit; - entry.source_asset = asset_type_from_id(meta.source_asset_id); - entry.destination_asset = asset_type_from_id(meta.destination_asset_id); - entry.return_address = meta.return_address; - entry.type = meta.tx_type; - entry.P_change = meta.one_time_public_key; - entry.return_pubkey = meta.return_pubkey; - protocol_entries.push_back(entry); - } // Check to see if there are any matured YIELD TXs uint64_t yield_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD; uint64_t start_height = (height > yield_lock_period) ? height - yield_lock_period - 1 : 0; + std::vector protocol_entries; cryptonote::yield_block_info ybi_matured; bool ok = get_ybi_entry(start_height, ybi_matured); if (ok && ybi_matured.locked_coins_this_block > 0) { @@ -2027,7 +1890,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, address_parse_info treasury_address_info; ok = cryptonote::get_account_address_from_str(treasury_address_info, m_nettype, get_config(m_nettype).TREASURY_ADDRESS); CHECK_AND_ASSERT_MES(ok, false, "Failed to obtain treasury address info"); - ok = construct_protocol_tx(height, protocol_fee, b.protocol_tx, protocol_entries, circ_supply, b.pricing_record, miner_address, treasury_address_info.address, b.major_version); + ok = construct_protocol_tx(height, b.protocol_tx, protocol_entries, b.major_version); CHECK_AND_ASSERT_MES(ok, false, "Failed to construct protocol tx"); pool_cookie = m_tx_pool.cookie(); @@ -5027,7 +4890,7 @@ leave: TIME_MEASURE_FINISH(vmt); TIME_MEASURE_START(vpt); - if(!validate_protocol_transaction(bl, blockchain_height, txs, m_hardfork->get_current_version())) + if(!validate_protocol_transaction(bl, blockchain_height, m_hardfork->get_current_version())) { MERROR_VER("Block with id: " << id << " has incorrect protocol transaction"); bvc.m_verifivation_failed = true; diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 9afbbfe..a86dac5 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -1529,12 +1529,11 @@ namespace cryptonote * * @param b the block containing the miner transaction to be validated * @param height the blockchain's weight - * @param txs a vector containing all the TXs and their blobs, needed to obtain tx_types, asset_types and burnt amounts * @param version hard fork version for that transaction * * @return false if anything is found wrong with the protocol transaction, otherwise true */ - bool validate_protocol_transaction(const block& b, uint64_t height, std::vector>& txs, uint8_t hf_version); + bool validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version); /** * @brief reverts the blockchain to its previous state following a failed switch diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 383dfd1..c122c2d 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -333,18 +333,12 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - bool construct_protocol_tx(const size_t height, - uint64_t& protocol_fee, - transaction& tx, - std::vector& protocol_data, - std::map circ_supply, - const oracle::pricing_record& pr, - const account_public_address &miner_address, - const account_public_address &treasury_address, - const uint8_t hf_version) { - - // A vector to contain all of the additional _tx_secret_keys_ - //std::vector& additional_tx_keys; + bool construct_protocol_tx( + const size_t height, + transaction& tx, + std::vector& protocol_data, + const uint8_t hf_version + ) { // Clear the TX contents tx.set_null(); @@ -358,174 +352,24 @@ namespace cryptonote if (!sort_tx_extra(tx.extra, tx.extra)) return false; - // Update the circulating_supply information, while keeping a count of amount to be created using txin_gen - std::map txin_gen_totals; - uint64_t txin_gen_final = 0; - for (auto const& entry: protocol_data) { - if (!circ_supply.count(entry.source_asset)) { - LOG_ERROR("Circulating supply does not have " << entry.source_asset << " balance - invalid source_asset"); - return false; - } - // Deduct the amount_burnt from the circulating_supply balance - circ_supply[entry.source_asset] -= entry.amount_burnt; - } - - // Calculate the slippage for the output amounts LOG_PRINT_L2("Creating protocol_tx..."); - uint64_t slippage_total = 0; std::vector additional_tx_public_keys; for (auto const& entry: protocol_data) { - if (entry.destination_asset == "BURN") { - // BURN TX - no slippage, no money minted - skip - continue; - } - // CONVERT TX - /* - // Create a secret TX key (= s) - crypto::secret_key s = keypair::generate(hw::get_device("default")).sec; - //additional_tx_keys.push_back(s); - - // Now add the correct TX public key (= sP_change) - // This has to be done using smK() call because of g_k_d() performing a torsion clear - crypto::public_key txkey_pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(entry.P_change), rct::sk2rct(s))); - additional_tx_public_keys.push_back(txkey_pub); - - // Calculate the actual return address, because the field we already have is actually the TX pubkey to use - // return address = Hs(syF || i)G + P_change = Hs(saP_change || i)G + P_change - // Generate the uniqueness for the input - size_t output_index = tx.vout.size(); - - // y = Hs(uniqueness) - ec_scalar y; - CHECK_AND_ASSERT_MES(cryptonote::calculate_uniqueness((cryptonote::transaction_type)(entry.type), entry.input_k_image, height, 0, y), false, "while creating protocol_tx outs: failed to calculate uniqueness"); - - rct::key key_y = (rct::key&)(y); - rct::key key_F = (rct::key&)(entry.return_address); - crypto::public_key syF = rct::rct2pk(rct::scalarmultKey(rct::scalarmultKey(key_F, key_y), rct::sk2rct(s))); - crypto::key_derivation derivation_syF = AUTO_VAL_INIT(derivation_syF); - std::memcpy(derivation_syF.data, syF.data, sizeof(crypto::key_derivation)); - - crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key); - bool r = crypto::derive_public_key(derivation_syF, output_index, entry.P_change, out_eph_public_key); - CHECK_AND_ASSERT_MES(r, false, "while creating protocol_tx outs: failed to derive_public_key(" << derivation_syF << ", " << key_y << ", "<< entry.P_change << ")"); - - // Sanity checks - crypto::public_key P_change_verify = crypto::null_pkey; - r = crypto::derive_subaddress_public_key(out_eph_public_key, derivation_syF, output_index, P_change_verify); - CHECK_AND_ASSERT_MES(r, false, "while creating protocol_tx outs: failed sanity check calling derive_subaddress_public_key(" << out_eph_public_key << ", " << derivation_syF << ", " << key_y << ", " << P_change_verify << ")"); - CHECK_AND_ASSERT_MES(entry.P_change == P_change_verify, false, "while creating protocol_tx outs: failed sanity check (keys do not match)"); - */ - /* - LOG_ERROR("***************************************************************************************"); - LOG_ERROR("output_index : " << output_index); - LOG_ERROR("P_change : " << entry.P_change); - LOG_ERROR("key_y : " << key_y); - LOG_ERROR("key_F : " << key_F); - LOG_ERROR("s : " << s); - LOG_ERROR("der. (syF) : " << derivation_syF); - LOG_ERROR("txkey_pub : " << txkey_pub); - LOG_ERROR("output_key : " << out_eph_public_key << " (derivation_syF, output_index, P_change)"); - LOG_ERROR("P_change_ver : " << P_change_verify); - LOG_ERROR("***************************************************************************************"); - */ - - if (entry.type == cryptonote::transaction_type::CONVERT) { - - // Now calculate the slippage, and decide if it is going to be converted or refunded - uint64_t amount_slippage = 0, amount_minted = 0; - bool ok = cryptonote::calculate_conversion(entry.source_asset, entry.destination_asset, entry.amount_burnt, entry.amount_slippage_limit, amount_minted, amount_slippage, circ_supply, pr, hf_version); - if (!ok) { - LOG_ERROR("failed to calculate slippage when trying to build protocol_tx"); - return false; - } - if (amount_minted == 0) { - - // REFUND - LOG_PRINT_L2("Conversion TX refunded - slippage too high"); - txin_gen_totals[entry.source_asset] += entry.amount_burnt; - - // Create the TX output for this refund - tx_out out; - //cryptonote::set_tx_out(entry.amount_burnt, entry.source_asset, 0, out_eph_public_key, false, crypto::view_tag{}, out); - cryptonote::set_tx_out(entry.amount_burnt, entry.source_asset, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, entry.return_address, false, crypto::view_tag{}, out); - additional_tx_public_keys.push_back(entry.return_pubkey); - tx.vout.push_back(out); - } else { - - // CONVERTED - LOG_PRINT_L2("Conversion TX submitted - converted " << print_money(entry.amount_burnt) << " " << entry.source_asset << " to " << print_money(amount_minted) << " " << entry.destination_asset << "(slippage " << print_money(amount_slippage) << ")"); - txin_gen_totals[entry.destination_asset] += amount_minted; - - // Add the slippage to our total for the block - if (entry.source_asset == "SAL") { - slippage_total += amount_slippage; - } else { - // Convert the slippage into a SAL amount so we can pay a proportion to the miner - uint64_t conversion_rate = 0, amount_slippage_converted = 0; - ok = get_conversion_rate(pr, entry.source_asset, entry.destination_asset, conversion_rate); - CHECK_AND_ASSERT_MES(ok, false, "Failed to get conversion rate for miner payout"); - ok = get_converted_amount(conversion_rate, amount_slippage, amount_slippage_converted); - CHECK_AND_ASSERT_MES(ok, false, "Failed to get converted slippage amount for miner payout"); - slippage_total += amount_slippage_converted; - } - - // Create the TX output for this conversion - tx_out out; - //cryptonote::set_tx_out(amount_minted, entry.destination_asset, 0, out_eph_public_key, false, crypto::view_tag{}, out); - cryptonote::set_tx_out(amount_minted, entry.destination_asset, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, entry.return_address, false, crypto::view_tag{}, out); - additional_tx_public_keys.push_back(entry.return_pubkey); - tx.vout.push_back(out); - } - } else if (entry.type == cryptonote::transaction_type::STAKE) { - + if (entry.type == cryptonote::transaction_type::STAKE) { // PAYOUT LOG_PRINT_L2("Yield TX payout submitted " << entry.amount_burnt << entry.source_asset); - txin_gen_totals[entry.source_asset] += entry.amount_burnt; - + // Create the TX output for this refund tx_out out; - //cryptonote::set_tx_out(entry.amount_burnt, entry.source_asset, 0, out_eph_public_key, false, crypto::view_tag{}, out); cryptonote::set_tx_out(entry.amount_burnt, entry.source_asset, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, entry.return_address, false, crypto::view_tag{}, out); additional_tx_public_keys.push_back(entry.return_pubkey); tx.vout.push_back(out); } } - + // Add in all of the additional TX pubkeys we need to process the payments add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys); - if (slippage_total > 0) { - - // Add a payout for the miner - uint64_t slippage_miner = slippage_total / 5; - - crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); - crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key); - bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << crypto::secret_key_explicit_print_ref{txkey.sec} << ")"); - - r = crypto::derive_public_key(derivation, tx.vout.size(), miner_address.m_spend_public_key, out_eph_public_key); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << 0 << ", "<< miner_address.m_spend_public_key << ")"); - - tx_out out_miner; - cryptonote::set_tx_out(slippage_miner, "SAL", CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, out_eph_public_key, false, crypto::view_tag{}, out_miner); - tx.vout.push_back(out_miner); - - // Add a payout for the treasury - crypto::key_derivation derivation_treasury = AUTO_VAL_INIT(derivation_treasury); - crypto::public_key out_eph_public_key_treasury = AUTO_VAL_INIT(out_eph_public_key_treasury); - r = crypto::generate_key_derivation(treasury_address.m_view_public_key, txkey.sec, derivation_treasury); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << treasury_address.m_view_public_key << ", " << crypto::secret_key_explicit_print_ref{txkey.sec} << ")"); - - r = crypto::derive_public_key(derivation_treasury, tx.vout.size(), treasury_address.m_spend_public_key, out_eph_public_key_treasury); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << 0 << ", "<< miner_address.m_spend_public_key << ")"); - - uint64_t slippage_treasury = slippage_miner >> 1; - tx_out out_treasury; - cryptonote::set_tx_out(slippage_treasury, "SAL", CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, out_eph_public_key_treasury, false, crypto::view_tag{}, out_treasury); - tx.vout.push_back(out_treasury); - } - // Create the txin_gen now txin_gen in; in.height = height; diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index f7bf7da..2b91c3d 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -67,7 +67,7 @@ namespace cryptonote }; //--------------------------------------------------------------- - bool construct_protocol_tx(const size_t height, uint64_t& protocol_fee, transaction& tx, std::vector& protocol_data, std::map circ_supply, const oracle::pricing_record& pr, const account_public_address &miner_address, const account_public_address &treasury_address, const uint8_t hf_version); + bool construct_protocol_tx(const size_t height, transaction& tx, std::vector& protocol_data, const uint8_t hf_version); //--------------------------------------------------------------- bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index aaed85a..3828388 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -1614,7 +1614,7 @@ namespace cryptonote //--------------------------------------------------------------------------------- //--------------------------------------------------------------------------------- //TODO: investigate whether boolean return is appropriate - bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version, oracle::pricing_record& pr, std::map& circ_supply, std::vector& protocol_metadata) + bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -1744,14 +1744,6 @@ namespace cryptonote continue; } - // Check what the TX type is - only CONVERT needs a cash_value - if (meta.source_asset_id == meta.destination_asset_id) { - // TRANSFER - } else { - // BURN OR CONVERT (both require inclusion in the protocol_tx calculation for circ_supply purposes) - protocol_metadata.push_back(meta); - } - bl.tx_hashes.push_back(sorted_it->second); total_weight += meta.weight; fee += meta.fee; diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 24c624c..0b279ee 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -227,13 +227,10 @@ namespace cryptonote * @param fee return-by-reference the total of fees from the included transactions * @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees * @param version hard fork version to use for consensus rules - * @param pr the current pricing record - * @param circ_cupply the circulating supply information for all asset types - * @param protocol_metadata the TX-specific data needed to create conversion outputs in the protocol TX (including converted amounts and refunds) * * @return true */ - bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version, oracle::pricing_record& pr, std::map& circ_supply, std::vector& protocol_metadata); + bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version); /** * @brief get a list of all transactions in the pool From e9a2b6fbb7f933fd345a48f9f98eb53a5d9a04e2 Mon Sep 17 00:00:00 2001 From: Some Random Crypto Guy Date: Fri, 27 Dec 2024 15:36:41 +0000 Subject: [PATCH 2/2] additional checks needed for protocol_tx validation --- src/cryptonote_core/blockchain.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index cfb48f1..54c88b4 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1450,6 +1450,13 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height, bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version) { LOG_PRINT_L3("Blockchain::" << __func__); + if (height == 0) { + // Genesis block exception + CHECK_AND_ASSERT_MES(b.protocol_tx.version == 1, false, "Invalid genesis protocol transaction version"); + CHECK_AND_ASSERT_MES(b.protocol_tx.vin.size() == 0, false, "genesis protocol transaction in the block has inputs"); + CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == 0, false, "genesis protocol transaction in the block has outputs"); + return true; + } CHECK_AND_ASSERT_MES(b.protocol_tx.vin.size() == 1, false, "coinbase protocol transaction in the block has no inputs"); CHECK_AND_ASSERT_MES(b.protocol_tx.vin[0].type() == typeid(txin_gen), false, "coinbase protocol transaction in the block has the wrong type"); CHECK_AND_ASSERT_MES(b.protocol_tx.version > 1, false, "Invalid coinbase protocol transaction version"); @@ -1530,6 +1537,14 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, { LOG_PRINT_L3("Blockchain::" << __func__); + if (height == 0) { + // Genesis block exception + CHECK_AND_ASSERT_MES(b.protocol_tx.version == 1, false, "Invalid genesis protocol transaction version"); + CHECK_AND_ASSERT_MES(b.protocol_tx.vin.size() == 0, false, "genesis protocol transaction in the block has inputs"); + CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == 0, false, "genesis protocol transaction in the block has outputs"); + return true; + } + // if nothing is created by this TX - check no money is included size_t vout_size = b.protocol_tx.vout.size(); CHECK_AND_ASSERT_MES(b.protocol_tx.vin.size() == 1, false, "coinbase protocol transaction in the block has no inputs"); @@ -1557,7 +1572,11 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, return false; } + // Check we have the correct number of entries + CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == yield_payouts.size(), false, "Invalid number of outputs in protocol_tx - aborting"); + // go through each vout and validate + std::set used_keys; for (auto& o : b.protocol_tx.vout) { // gather the output data uint64_t out_amount; @@ -1581,6 +1600,15 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, return false; } + // Check if key has already been seen + if (used_keys.count(out_key) != 0) { + LOG_ERROR("Block at height: " << height << " - Duplicated output key " << out_key << " for protocol TX - aborting"); + return false; + } + + // Add key to list of already-seen + used_keys.insert(out_key); + // check if there is entry in the yield payouts for this output auto found = std::find_if(yield_payouts.begin(), yield_payouts.end(), [&](const std::pair& p) { return p.first.return_address == out_key;