From f1b1de851ec249768a6b7f2dc011e7179dfae662 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Wed, 17 Jun 2020 12:39:09 +0200 Subject: [PATCH 1/4] Remove checks for tx version where it is not needed --- src/cryptonote_core/blockchain.cpp | 34 ++----------------------- src/cryptonote_core/blockchain.h | 2 +- src/cryptonote_core/cryptonote_core.cpp | 13 ++-------- src/cryptonote_core/tx_pool.cpp | 2 +- 4 files changed, 6 insertions(+), 45 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 7b46a0c1e..01373ee4a 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -4003,22 +4003,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, return false; } } - - // min/max tx version based on HF, and we accept v1 txes if having a non mixable - const size_t max_tx_version = get_maximum_tx_version_supported(); - if (tx.version > max_tx_version) - { - MERROR_VER("transaction version " << (unsigned)tx.version << " is higher than max accepted version " << max_tx_version); - tvc.m_verifivation_failed = true; - return false; - } - const size_t min_tx_version = MIN_SUPPORTED_TX_VERSION; - if (tx.version < min_tx_version) - { - MERROR_VER("transaction version " << (unsigned)tx.version << " is lower than min accepted version " << min_tx_version); - tvc.m_verifivation_failed = true; - return false; - } } //sorted ins @@ -4152,8 +4136,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, return false; } - if (tx.version >= MIN_SUPPORTED_TX_VERSION && tx.version <= get_maximum_tx_version_supported()) - { if (threads > 1) { // ND: Speedup @@ -4261,18 +4243,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } it->second[k_image] = true; } - } sig_index++; } - if ((tx.version >= MIN_SUPPORTED_TX_VERSION && tx.version <= get_maximum_tx_version_supported()) && threads > 1) - waiter.wait(); - - if (tx.version >= MIN_SUPPORTED_TX_VERSION && tx.version <= get_maximum_tx_version_supported()) - { if (threads > 1) { + waiter.wait(); // save results to table, passed or otherwise bool failed = false; for (size_t i = 0; i < tx.vin.size(); i++) @@ -4291,12 +4268,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, return false; } } - } - else - { - MERROR_VER("Transaction of version " << tx.version<<" not yet supported"); - return false; - } if(!check_safex_tx(tx,tvc)){ tvc.m_verifivation_failed = true; @@ -6660,9 +6631,8 @@ bool Blockchain::are_safex_tokens_unlocked(const std::vector &tx_vin) { return true; } -uint8_t Blockchain::get_maximum_tx_version_supported() const +uint8_t Blockchain::get_maximum_tx_version_supported(uint8_t hf_version) const { - auto hf_version = get_current_hard_fork_version(); switch (m_nettype) { case cryptonote::network_type::FAKECHAIN: diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 66dab4be8..012904f31 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -640,7 +640,7 @@ namespace cryptonote * * @return return max tx_version that is supported for current hardfork */ - uint8_t get_maximum_tx_version_supported() const; + uint8_t get_maximum_tx_version_supported(uint8_t hf_version) const; /** * @brief get dynamic per kB fee for a given block size diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 4fa5b84c8..a5d3b2156 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -675,14 +675,6 @@ namespace cryptonote } bad_semantics_txes_lock.unlock(); - if (tx.version == 0 || tx.version > m_blockchain_storage.get_maximum_tx_version_supported()) - { - // MAX_SUPPORTED_TX_VERSION is the latest transaction version - // we know in the current protocol version - tvc.m_verifivation_failed = true; - return false; - } - return true; } //----------------------------------------------------------------------------------------------- @@ -848,11 +840,10 @@ namespace cryptonote return false; } - + const uint8_t version = m_blockchain_storage.get_current_hard_fork_version(); if(!check_money_overflow(tx)) { - const uint8_t version = m_blockchain_storage.get_current_hard_fork_version(); bool known_problem = false; if(version < HF_VERSION_STOP_COUNTERFEIT_TOKENS) { @@ -875,7 +866,7 @@ namespace cryptonote } } - if (tx.version >= MIN_SUPPORTED_TX_VERSION && tx.version <= m_blockchain_storage.get_maximum_tx_version_supported()) + if (tx.version >= MIN_SUPPORTED_TX_VERSION && tx.version <= m_blockchain_storage.get_maximum_tx_version_supported(version)) { uint64_t amount_in = 0; get_inputs_cash_amount(tx, amount_in); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 4b1fea4c5..b81b273de 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -149,7 +149,7 @@ namespace cryptonote // fee per kilobyte, size rounded up. uint64_t fee; - if (tx.version >= MIN_SUPPORTED_TX_VERSION && tx.version <= m_blockchain.get_maximum_tx_version_supported()) + if (tx.version >= MIN_SUPPORTED_TX_VERSION && tx.version <= m_blockchain.get_maximum_tx_version_supported(version)) { uint64_t inputs_amount = 0; if(!get_inputs_cash_amount(tx, inputs_amount)) From a936cebbf971adeef6290803b62b43fd8fe71982 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Wed, 17 Jun 2020 18:13:47 +0200 Subject: [PATCH 2/4] Change logic of multiple purchase in tx pool. Now we store for each purchase in the tx_pool quantity and add up to the total sum of items to buy. If purchase that is coming to the pool wants to get more items than it is in the db + items from other purchases, it is rejected right away. Now, we store in meta of tx if tx is safex tx. After 1 hour, safex tx will be checked if it's logic still stays correct. If not, it is removed from the pool. --- src/blockchain_db/blockchain_db.h | 2 +- src/cryptonote_config.h | 1 + src/cryptonote_core/tx_pool.cpp | 124 ++++++++++++++++++------------ src/cryptonote_core/tx_pool.h | 12 ++- 4 files changed, 87 insertions(+), 52 deletions(-) diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index ec0306387..9cfe801bc 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -172,7 +172,7 @@ namespace cryptonote uint8_t relayed; uint8_t do_not_relay; uint8_t double_spend_seen: 1; - uint8_t safex_failed{0}; + uint8_t safex_tx{0}; uint8_t padding[75]; // till 192 bytes }; diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index ab4bf9840..786845b00 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -112,6 +112,7 @@ #define CRYPTONOTE_MEMPOOL_TX_LIVETIME (86400*3) //seconds, three days #define CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME 604800 //seconds, one week +#define CRYPTONOTE_MEMPOOL_SAFEX_TX_LIVETIME 3600 //seconds, 1 hour #define COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT 1000 diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index b81b273de..93de94a39 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -285,6 +285,7 @@ namespace cryptonote meta.relayed = relayed; meta.do_not_relay = do_not_relay; meta.double_spend_seen = have_tx_keyimges_as_spent(tx); + meta.safex_tx = tx.version >=2; memset(meta.padding, 0, sizeof(meta.padding)); try { @@ -328,6 +329,7 @@ namespace cryptonote meta.relayed = relayed; meta.do_not_relay = do_not_relay; meta.double_spend_seen = false; + meta.safex_tx = tx.version >=2; memset(meta.padding, 0, sizeof(meta.padding)); try @@ -519,6 +521,18 @@ namespace cryptonote const cryptonote::blobdata pricepegblob(std::begin(out.data), std::end(out.data)); cryptonote::parse_and_validate_from_blob(pricepegblob, price_peg); m_safex_price_peg_update_in_progress.push_back(price_peg.price_peg_id); + } else if(vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_purchase) + { + const txout_to_script &out = boost::get(vout.target); + safex::create_purchase_data purchase; + const cryptonote::blobdata purchaseblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(purchaseblob, purchase); + auto it = std::find_if(m_safex_offers_to_purchase.begin(),m_safex_offers_to_purchase.end(), [&purchase](const std::pair& item){ return purchase.offer_id == item.first;}); + if( it != m_safex_offers_to_purchase.end()){ + it->second += purchase.quantity; + } + else + m_safex_offers_to_purchase.push_back(std::make_pair(purchase.offer_id, purchase.quantity)); } } return true; @@ -633,6 +647,20 @@ namespace cryptonote auto it = std::find(m_safex_price_peg_update_in_progress.begin(), m_safex_price_peg_update_in_progress.end(), price_peg.price_peg_id); CHECK_AND_ASSERT_MES(it != m_safex_price_peg_update_in_progress.end(), false, "failed to find safex restriction for type out_safex_price_peg_update" << ENDL << "transaction id = " << get_transaction_hash(tx)); m_safex_price_peg_update_in_progress.erase(it); + } else if (vout.target.type() == typeid(txout_to_script) && get_tx_out_type(vout.target) == cryptonote::tx_out_type::out_safex_purchase) + { + const txout_to_script &out = boost::get(vout.target); + safex::create_purchase_data purchase; + const cryptonote::blobdata purchaseblob(std::begin(out.data), std::end(out.data)); + cryptonote::parse_and_validate_from_blob(purchaseblob, purchase); + auto it = std::find_if(m_safex_offers_to_purchase.begin(),m_safex_offers_to_purchase.end(), [&purchase](const std::pair& item){ return purchase.offer_id == item.first;}); + CHECK_AND_ASSERT_MES(it != m_safex_offers_to_purchase.end(), false, "failed to find safex restriction for type out_safex_purchase" << ENDL << "transaction id = " << get_transaction_hash(tx)); + if(it->second < purchase.quantity){ + LOG_ERROR("This should not happen! As tx_pool internal calculation is wrong"); + it->second = 0; + } else + it->second -= purchase.quantity; + } } return true; @@ -709,8 +737,7 @@ namespace cryptonote uint64_t tx_age = time(nullptr) - meta.receive_time; if((tx_age > CRYPTONOTE_MEMPOOL_TX_LIVETIME && !meta.kept_by_block) || - (tx_age > CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME && meta.kept_by_block) || - meta.safex_failed) + (tx_age > CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME && meta.kept_by_block)) { LOG_PRINT_L1("Tx " << txid << " removed from tx pool due to outdated, age: " << tx_age ); auto sorted_it = find_tx_in_sorted_container(txid); @@ -724,6 +751,31 @@ namespace cryptonote } m_timed_out_transactions.insert(txid); remove.insert(txid); + } else if (tx_age > CRYPTONOTE_MEMPOOL_SAFEX_TX_LIVETIME && meta.safex_tx) + { + cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid); + cryptonote::transaction tx; + if (!parse_and_validate_tx_from_blob(txblob, tx)) + { + MERROR("Failed to parse tx from txpool"); + return true; + } + tx_verification_context tvc; + if(!m_blockchain.check_safex_tx(tx, tvc)) + { + LOG_PRINT_L1("Tx " << txid << " removed from tx pool due to outdated, age: " << tx_age ); + auto sorted_it = find_tx_in_sorted_container(txid); + if (sorted_it == m_txs_by_fee_and_receive_time.end()) + { + LOG_PRINT_L1("Removing tx " << txid << " from tx pool, but it was not found in the sorted txs container!"); + } + else + { + m_txs_by_fee_and_receive_time.erase(sorted_it); + } + m_timed_out_transactions.insert(txid); + remove.insert(txid); + } } return true; }, false); @@ -1091,53 +1143,6 @@ namespace cryptonote //--------------------------------------------------------------------------------- bool tx_memory_pool::on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id) { - - CRITICAL_REGION_LOCAL(m_transactions_lock); - CRITICAL_REGION_LOCAL1(m_blockchain); - - for(auto& sorted_it : m_txs_by_fee_and_receive_time) - { - txpool_tx_meta_t meta; - - if (!m_blockchain.get_txpool_tx_meta(sorted_it.second, meta)) - { - MERROR(" failed to find tx meta"); - continue; - } - - cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(sorted_it.second); - cryptonote::transaction tx; - if (!parse_and_validate_tx_from_blob(txblob, tx)) - { - MERROR("Failed to parse tx from txpool"); - continue; - } - - // Skip transactions that are not ready to be - // included into the blockchain or that are - // missing key images - const cryptonote::txpool_tx_meta_t original_meta = meta; - tx_verification_context tvc; - if(!m_blockchain.check_tx_inputs(tx, meta.max_used_block_height, meta.max_used_block_id, tvc)) - { - meta.safex_failed = tvc.m_safex_invalid_input || tvc.m_safex_invalid_command || tvc.m_safex_verification_failed || - tvc.m_safex_invalid_command_params || tvc.m_safex_command_execution_failed; - } - if (memcmp(&original_meta, &meta, sizeof(meta))) - { - LockedTXN lock(m_blockchain); - try - { - m_blockchain.update_txpool_tx(sorted_it.second, meta); - } - catch (const std::exception &e) - { - MERROR("Failed to update tx meta: " << e.what()); - // continue, not fatal - } - lock.commit(); - } - } return true; } //--------------------------------------------------------------------------------- @@ -1240,8 +1245,19 @@ namespace cryptonote safex::create_purchase_data purchase; const cryptonote::blobdata purchaseblob(std::begin(out.data), std::end(out.data)); cryptonote::parse_and_validate_from_blob(purchaseblob, purchase); + uint64_t purchased_quantity = have_tx_safex_purchase_in_use(purchase.offer_id); if(have_tx_safex_offer_in_use(purchase.offer_id)) return true; + if(purchased_quantity != 0){ + uint64_t quantity; + bool res = m_blockchain.get_safex_offer_quantity(purchase.offer_id, quantity); + if(!res){ + LOG_ERROR("Error getin offer quantity from blockchain"); + return true; + } + if(quantity < purchased_quantity + purchase.quantity) + return true; + } } } return false; @@ -1271,6 +1287,13 @@ namespace cryptonote return m_safex_price_peg_update_in_progress.end() != std::find(m_safex_price_peg_update_in_progress.begin(), m_safex_price_peg_update_in_progress.end(), price_peg_id); } //--------------------------------------------------------------------------------- + uint64_t tx_memory_pool::have_tx_safex_purchase_in_use(const crypto::hash &offer_id) const + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + auto it = std::find_if(m_safex_offers_to_purchase.begin(),m_safex_offers_to_purchase.end(), [&offer_id](const std::pair& item){ return offer_id == item.first;}); + return it != m_safex_offers_to_purchase.end() ? it->second : 0; + } + //--------------------------------------------------------------------------------- void tx_memory_pool::lock() const { m_transactions_lock.lock(); @@ -1539,7 +1562,7 @@ namespace cryptonote uint64_t best_coinbase = 0, coinbase = 0; total_size = 0; fee = 0; - + //baseline empty block get_block_reward(median_size, total_size, already_generated_coins, best_coinbase, version, height); @@ -1742,6 +1765,7 @@ namespace cryptonote m_spent_key_images.clear(); m_safex_accounts_in_use.clear(); m_safex_offers_in_use.clear(); + m_safex_offers_to_purchase.clear(); m_txpool_size = 0; std::vector remove; diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 81d78905a..32772f245 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -431,7 +431,7 @@ namespace cryptonote bool insert_key_images(const transaction &tx, bool kept_by_block); /** - * @brief insert safex data into m_safex_accounts_in_use, m_safex_offers_in_use, and m_safex_price_peg_update_in_progress + * @brief insert safex data into m_safex_accounts_in_use, m_safex_offers_in_use, m_safex_price_peg_update_in_progress and m_safex_offers_to_purchase * * @return true on success, false on error */ @@ -484,6 +484,15 @@ namespace cryptonote */ bool have_tx_safex_price_peg_in_use(const crypto::hash& price_peg_id) const; + /** + * @brief check if a transaction in the pool has a safex purchase for given offer + * + * @param username Offer ID of the offer that is being purchased + * + * @return quantity of units being purchased for givven offer + */ + uint64_t have_tx_safex_purchase_in_use(const crypto::hash& offer_id) const; + /** * @brief check if any spent key image in a transaction is in the pool * @@ -614,6 +623,7 @@ namespace cryptonote std::vector m_safex_accounts_in_use; std::vector m_safex_offers_in_use; std::vector m_safex_price_peg_update_in_progress; + std::vector> m_safex_offers_to_purchase; //TODO: this time should be a named constant somewhere, not hard-coded //! interval on which to check for stale/"stuck" transactions From 832b7857051cc0de52a0cbaac3f8aa29330ca15c Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Wed, 17 Jun 2020 19:29:37 +0200 Subject: [PATCH 3/4] Add additional block_cleanups --- src/cryptonote_core/cryptonote_core.cpp | 13 +++++++++++-- .../cryptonote_protocol_handler.inl | 5 +++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index a5d3b2156..6ab904732 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1339,7 +1339,12 @@ namespace cryptonote m_miner.resume(); return false; } - prepare_handle_incoming_blocks(blocks); + if (!prepare_handle_incoming_blocks(blocks)) + { + MERROR("Block found, but failed to prepare to add"); + m_miner.resume(); + return false; + } m_blockchain_storage.add_new_block(b, bvc); cleanup_handle_incoming_blocks(true); //anyway - update miner template @@ -1393,7 +1398,11 @@ namespace cryptonote bool core::prepare_handle_incoming_blocks(const std::list &blocks) { m_incoming_tx_lock.lock(); - m_blockchain_storage.prepare_handle_incoming_blocks(blocks); + if (!m_blockchain_storage.prepare_handle_incoming_blocks(blocks)) + { + cleanup_handle_incoming_blocks(false); + return false; + } return true; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 18c8698e6..38154f0bf 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1095,6 +1095,11 @@ skip: if (tvc.size() != block_entry.txs.size()) { LOG_ERROR_CCONTEXT("Internal error: tvc.size() != block_entry.txs.size()"); + if (!m_core.cleanup_handle_incoming_blocks()) + { + LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks"); + return 1; + } return 1; } std::list::const_iterator it = block_entry.txs.begin(); From 2d49c02417cff92bcbb1d6045b127b20f889f743 Mon Sep 17 00:00:00 2001 From: Igor Grkavac Date: Fri, 19 Jun 2020 13:14:58 +0200 Subject: [PATCH 4/4] Add stagenet advanced txs hardfork height and timestamp --- src/cryptonote_core/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 01373ee4a..36ef16ec6 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -141,7 +141,7 @@ static const struct { { 4, config::stagenet::HARDFORK_V4_START_HEIGHT, 0, 1565962165}, { 5, config::stagenet::HARDFORK_V4_START_HEIGHT + 1, 0, 1565962166}, { 6, config::stagenet::HARDFORK_V4_START_HEIGHT + 2, 0, 1592478292}, - { 7, 89400, 0, 1565972167} + { 7, 90150, 0, 1592578800} }; //------------------------------------------------------------------